diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b214a91729..a1cc0a114a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,10 +1,10 @@ -ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:a20b8a3538313487ac9266875bbf733e544c1aa2091df2bb99ab592a6d4f7399 +ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:fb211a0ea31a6177507498c084682aae8c9c31ca27668ea122246aa16a4723a0 FROM ${BASEIMAGE} # Flutter SDK # https://flutter.dev/docs/development/tools/sdk/releases?tab=linux ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.29.1" +ENV FLUTTER_VERSION="3.29.3" ENV FLUTTER_HOME=/flutter ENV PATH=${PATH}:${FLUTTER_HOME}/bin diff --git a/.github/.nvmrc b/.github/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/.github/DISCUSSION_TEMPLATE/feature-request.yaml b/.github/DISCUSSION_TEMPLATE/feature-request.yaml index 8a2358cc2b..2c7492fd5b 100644 --- a/.github/DISCUSSION_TEMPLATE/feature-request.yaml +++ b/.github/DISCUSSION_TEMPLATE/feature-request.yaml @@ -14,7 +14,6 @@ body: label: I have searched the existing feature requests, both open and closed, to make sure this is not a duplicate request. options: - label: 'Yes' - required: true - type: textarea id: feature diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 15274f75e8..1ac0e04332 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,6 @@ body: label: I have searched the existing issues, both open and closed, to make sure this is not a duplicate report. options: - label: 'Yes' - required: true - type: markdown attributes: diff --git a/.github/actions/image-build/action.yml b/.github/actions/image-build/action.yml new file mode 100644 index 0000000000..a4168dcd5a --- /dev/null +++ b/.github/actions/image-build/action.yml @@ -0,0 +1,118 @@ +name: 'Single arch image build' +description: 'Build single-arch image on platform appropriate runner' +inputs: + image: + description: 'Name of the image to build' + required: true + ghcr-token: + description: 'GitHub Container Registry token' + required: true + platform: + description: 'Platform to build for' + required: true + artifact-key-base: + description: 'Base key for artifact name' + required: true + context: + description: 'Path to build context' + required: true + dockerfile: + description: 'Path to Dockerfile' + required: true + build-args: + description: 'Docker build arguments' + required: false +runs: + using: 'composite' + steps: + - name: Prepare + id: prepare + shell: bash + env: + PLATFORM: ${{ inputs.platform }} + run: | + echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Login to GitHub Container Registry + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + if: ${{ !github.event.pull_request.head.repo.fork }} + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ inputs.ghcr-token }} + + - name: Generate cache key suffix + id: cache-key-suffix + shell: bash + env: + REF: ${{ github.ref_name }} + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT + else + SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g') + echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT + fi + + - name: Generate cache target + id: cache-target + shell: bash + env: + BUILD_ARGS: ${{ inputs.build-args }} + IMAGE: ${{ inputs.image }} + SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }} + PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }} + run: | + HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1) + CACHE_KEY="${PLATFORM_PAIR}-${HASH}" + echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT + if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then + # Essentially just ignore the cache output (forks can't write to registry cache) + echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT + else + echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT + fi + + - name: Generate docker image tags + id: meta + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 + env: + DOCKER_METADATA_PR_HEAD_SHA: 'true' + + - name: Build and push image + id: build + uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile }} + platforms: ${{ inputs.platform }} + labels: ${{ steps.meta.outputs.labels }} + cache-to: ${{ steps.cache-target.outputs.cache-to }} + cache-from: | + type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-${{ steps.cache-key-suffix.outputs.suffix }} + type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-main + outputs: type=image,"name=${{ inputs.image }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }} + build-args: | + BUILD_ID=${{ github.run_id }} + BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.meta.outputs.tags }} + BUILD_SOURCE_REF=${{ github.ref_name }} + BUILD_SOURCE_COMMIT=${{ github.sha }} + ${{ inputs.build-args }} + + - name: Export digest + shell: bash + run: | # zizmor: ignore[template-injection] + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ${{ inputs.artifact-key-base }}-${{ steps.cache-target.outputs.cache-key-base }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index 406e8f89e1..0fcc4f1d7c 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -7,6 +7,15 @@ on: ref: required: false type: string + secrets: + KEY_JKS: + required: true + ALIAS: + required: true + ANDROID_KEY_PASSWORD: + required: true + ANDROID_STORE_PASSWORD: + required: true pull_request: push: branches: [main] @@ -15,16 +24,23 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | mobile: @@ -38,31 +54,26 @@ jobs: build-sign-android: name: Build and sign Android needs: pre-job + permissions: + contents: read # Skip when PR from a fork if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }} runs-on: macos-14 steps: - - name: Determine ref - id: get-ref - run: | - input_ref="${{ inputs.ref }}" - github_ref="${{ github.sha }}" - ref="${input_ref:-$github_ref}" - echo "ref=$ref" >> $GITHUB_OUTPUT - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ steps.get-ref.outputs.ref }} + ref: ${{ inputs.ref || github.sha }} + persist-credentials: false - - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4 + - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'zulu' java-version: '17' cache: 'gradle' - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2 + uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml @@ -93,7 +104,7 @@ jobs: flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64 - name: Publish Android Artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-apk-signed path: mobile/build/app/outputs/flutter-apk/*.apk diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml index 0cc73c46c3..68ab8af24e 100644 --- a/.github/workflows/cache-cleanup.yml +++ b/.github/workflows/cache-cleanup.yml @@ -8,31 +8,38 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: cleanup: name: Cleanup runs-on: ubuntu-latest + permissions: + contents: read + actions: write steps: - name: Check out code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Cleanup + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REF: ${{ github.ref }} run: | gh extension install actions/gh-actions-cache REPO=${{ github.repository }} - BRANCH=${{ github.ref }} echo "Fetching list of cache keys" - cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 ) + cacheKeysForPR=$(gh actions-cache list -R $REPO -B ${REF} -L 100 | cut -f 1 ) ## Setting this to not fail the workflow while deleting cache keys. set +e echo "Deleting caches..." for cacheKey in $cacheKeysForPR do - gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + gh actions-cache delete $cacheKey -R "$REPO" -B "${REF}" --confirm done echo "Done" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 05326d063e..74f5970139 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -16,21 +16,25 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - packages: write +permissions: {} jobs: publish: name: CLI Publish runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./cli steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + # Setup .npmrc file to publish to npm - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -48,11 +52,16 @@ jobs: docker: name: Docker runs-on: ubuntu-latest + permissions: + contents: read + packages: write needs: publish steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Set up QEMU uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 @@ -61,7 +70,7 @@ jobs: uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 if: ${{ !github.event.pull_request.head.repo.fork }} with: registry: ghcr.io @@ -76,7 +85,7 @@ jobs: - name: Generate docker image tags id: metadata - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 with: flavor: | latest=false @@ -87,7 +96,7 @@ jobs: type=raw,value=latest,enable=${{ github.event_name == 'release' }} - name: Build and push image - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 with: file: cli/Dockerfile platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 99ffee2e88..fb6d686878 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,6 +24,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: analyze: name: Analyze @@ -42,11 +44,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -72,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3 + uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d1bdb5e8e7..2baf6e9ae5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -12,20 +12,23 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - packages: write +permissions: {} jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | server: @@ -37,6 +40,8 @@ jobs: - 'machine-learning/**' workflow: - '.github/workflows/docker.yml' + - '.github/workflows/multi-runner-build.yml' + - '.github/actions/image-build' - name: Check if we should force jobs to run id: should_force @@ -45,6 +50,9 @@ jobs: retag_ml: name: Re-Tag ML needs: pre-job + permissions: + contents: read + packages: write if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest strategy: @@ -52,24 +60,28 @@ jobs: suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn'] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Re-tag image + env: + REGISTRY_NAME: 'ghcr.io' + REPOSITORY: ${{ github.repository_owner }}/immich-machine-learning + TAG_OLD: main${{ matrix.suffix }} + TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} + TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }} run: | - REGISTRY_NAME="ghcr.io" - REPOSITORY=${{ github.repository_owner }}/immich-machine-learning - TAG_OLD=main${{ matrix.suffix }} - TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} - TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }} - docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD - docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD + docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}" + docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}" retag_server: name: Re-Tag Server needs: pre-job + permissions: + contents: read + packages: write if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest strategy: @@ -77,424 +89,91 @@ jobs: suffix: [''] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Re-tag image + env: + REGISTRY_NAME: 'ghcr.io' + REPOSITORY: ${{ github.repository_owner }}/immich-server + TAG_OLD: main${{ matrix.suffix }} + TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} + TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }} run: | - REGISTRY_NAME="ghcr.io" - REPOSITORY=${{ github.repository_owner }}/immich-server - TAG_OLD=main${{ matrix.suffix }} - TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }} - TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }} - docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD - docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD + docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}" + docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}" - build_and_push_ml: + machine-learning: name: Build and Push ML needs: pre-job if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }} - runs-on: ${{ matrix.runner }} - env: - image: immich-machine-learning - context: machine-learning - file: machine-learning/Dockerfile - GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning strategy: - # Prevent a failure in one image from stopping the other builds fail-fast: false - matrix: - include: - - platform: linux/amd64 - runner: ubuntu-latest - device: cpu - - - platform: linux/arm64 - runner: ubuntu-24.04-arm - device: cpu - - - platform: linux/amd64 - runner: ubuntu-latest - device: cuda - suffix: -cuda - - - platform: linux/amd64 - runner: mich - device: rocm - suffix: -rocm - - - platform: linux/amd64 - runner: ubuntu-latest - device: openvino - suffix: -openvino - - - platform: linux/arm64 - runner: ubuntu-24.04-arm - device: armnn - suffix: -armnn - - - platform: linux/arm64 - runner: ubuntu-24.04-arm - device: rknn - suffix: -rknn - - steps: - - name: Prepare - run: | - platform=${{ matrix.platform }} - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 - - - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - if: ${{ !github.event.pull_request.head.repo.fork }} - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate cache key suffix - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV - else - echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV - fi - - - name: Generate cache target - id: cache-target - run: | - if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then - # Essentially just ignore the cache output (forks can't write to registry cache) - echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT - else - echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT - fi - - - name: Generate docker image tags - id: meta - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 - env: - DOCKER_METADATA_PR_HEAD_SHA: 'true' - - - name: Build and push image - id: build - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 - with: - context: ${{ env.context }} - file: ${{ env.file }} - platforms: ${{ matrix.platforms }} - labels: ${{ steps.meta.outputs.labels }} - cache-to: ${{ steps.cache-target.outputs.cache-to }} - cache-from: | - type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }} - type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main - outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }} - build-args: | - DEVICE=${{ matrix.device }} - BUILD_ID=${{ github.run_id }} - BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }} - BUILD_SOURCE_REF=${{ github.ref_name }} - BUILD_SOURCE_COMMIT=${{ github.sha }} - - - name: Export digest - run: | - mkdir -p ${{ runner.temp }}/digests - digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }} - path: ${{ runner.temp }}/digests/* - if-no-files-found: error - retention-days: 1 - - merge_ml: - name: Merge & Push ML - runs-on: ubuntu-latest - if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }} - env: - GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning - DOCKER_REPO: altran1502/immich-machine-learning - strategy: matrix: include: - device: cpu + tag-suffix: '' - device: cuda - suffix: -cuda - - device: rocm - suffix: -rocm + tag-suffix: '-cuda' + platforms: linux/amd64 - device: openvino - suffix: -openvino + tag-suffix: '-openvino' + platforms: linux/amd64 - device: armnn - suffix: -armnn + tag-suffix: '-armnn' + platforms: linux/arm64 - device: rknn - suffix: -rknn - needs: - - build_and_push_ml - steps: - - name: Download digests - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 - with: - path: ${{ runner.temp }}/digests - pattern: ml-digests-${{ matrix.device }}-* - merge-multiple: true + tag-suffix: '-rknn' + platforms: linux/arm64 + - device: rocm + tag-suffix: '-rocm' + platforms: linux/amd64 + runner-mapping: '{"linux/amd64": "mich"}' + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0 + permissions: + contents: read + actions: read + packages: write + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + with: + image: immich-machine-learning + context: machine-learning + dockerfile: machine-learning/Dockerfile + platforms: ${{ matrix.platforms }} + runner-mapping: ${{ matrix.runner-mapping }} + tag-suffix: ${{ matrix.tag-suffix }} + dockerhub-push: ${{ github.event_name == 'release' }} + build-args: | + DEVICE=${{ matrix.device }} - - name: Login to Docker Hub - if: ${{ github.event_name == 'release' }} - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GHCR - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 - - - name: Generate docker image tags - id: meta - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 - env: - DOCKER_METADATA_PR_HEAD_SHA: 'true' - with: - flavor: | - # Disable latest tag - latest=false - suffix=${{ matrix.suffix }} - images: | - name=${{ env.GHCR_REPO }} - name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }} - tags: | - # Tag with branch name - type=ref,event=branch - # Tag with pr-number - type=ref,event=pr - # Tag with long commit sha hash - type=sha,format=long,prefix=commit- - # Tag with git tag on release - type=ref,event=tag - type=raw,value=release,enable=${{ github.event_name == 'release' }} - - - name: Create manifest list and push - working-directory: ${{ runner.temp }}/digests - run: | - # Process annotations - declare -a ANNOTATIONS=() - if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then - while IFS= read -r annotation; do - # Extract key and value by removing the manifest: prefix - if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then - key="${BASH_REMATCH[1]}" - value="${BASH_REMATCH[2]}" - # Use array to properly handle arguments with spaces - ANNOTATIONS+=(--annotation "index:$key=$value") - fi - done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON") - fi - - TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - SOURCE_ARGS=$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *) - - echo "docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS" - - docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS - - build_and_push_server: + server: name: Build and Push Server - runs-on: ${{ matrix.runner }} needs: pre-job if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} - env: + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0 + permissions: + contents: read + actions: read + packages: write + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + with: image: immich-server context: . - file: server/Dockerfile - GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server - strategy: - fail-fast: false - matrix: - include: - - platform: linux/amd64 - runner: ubuntu-latest - - platform: linux/arm64 - runner: ubuntu-24.04-arm - steps: - - name: Prepare - run: | - platform=${{ matrix.platform }} - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - if: ${{ !github.event.pull_request.head.repo.fork }} - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate cache key suffix - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV - else - echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV - fi - - - name: Generate cache target - id: cache-target - run: | - if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then - # Essentially just ignore the cache output (forks can't write to registry cache) - echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT - else - echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT - fi - - - name: Generate docker image tags - id: meta - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 - env: - DOCKER_METADATA_PR_HEAD_SHA: 'true' - - - name: Build and push image - id: build - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 - with: - context: ${{ env.context }} - file: ${{ env.file }} - platforms: ${{ matrix.platform }} - labels: ${{ steps.meta.outputs.labels }} - cache-to: ${{ steps.cache-target.outputs.cache-to }} - cache-from: | - type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }} - type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main - outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }} - build-args: | - DEVICE=cpu - BUILD_ID=${{ github.run_id }} - BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }} - BUILD_SOURCE_REF=${{ github.ref_name }} - BUILD_SOURCE_COMMIT=${{ github.sha }} - - - name: Export digest - run: | - mkdir -p ${{ runner.temp }}/digests - digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: server-digests-${{ env.PLATFORM_PAIR }} - path: ${{ runner.temp }}/digests/* - if-no-files-found: error - retention-days: 1 - - merge_server: - name: Merge & Push Server - runs-on: ubuntu-latest - if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }} - env: - GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server - DOCKER_REPO: altran1502/immich-server - needs: - - build_and_push_server - steps: - - name: Download digests - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 - with: - path: ${{ runner.temp }}/digests - pattern: server-digests-* - merge-multiple: true - - - name: Login to Docker Hub - if: ${{ github.event_name == 'release' }} - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GHCR - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 - - - name: Generate docker image tags - id: meta - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 - env: - DOCKER_METADATA_PR_HEAD_SHA: 'true' - with: - flavor: | - # Disable latest tag - latest=false - suffix=${{ matrix.suffix }} - images: | - name=${{ env.GHCR_REPO }} - name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }} - tags: | - # Tag with branch name - type=ref,event=branch - # Tag with pr-number - type=ref,event=pr - # Tag with long commit sha hash - type=sha,format=long,prefix=commit- - # Tag with git tag on release - type=ref,event=tag - type=raw,value=release,enable=${{ github.event_name == 'release' }} - - - name: Create manifest list and push - working-directory: ${{ runner.temp }}/digests - run: | - # Process annotations - declare -a ANNOTATIONS=() - if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then - while IFS= read -r annotation; do - # Extract key and value by removing the manifest: prefix - if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then - key="${BASH_REMATCH[1]}" - value="${BASH_REMATCH[2]}" - # Use array to properly handle arguments with spaces - ANNOTATIONS+=(--annotation "index:$key=$value") - fi - done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON") - fi - - TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - SOURCE_ARGS=$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *) - - echo "docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS" - - docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS + dockerfile: server/Dockerfile + dockerhub-push: ${{ github.event_name == 'release' }} + build-args: | + DEVICE=cpu success-check-server: name: Docker Build & Push Server Success - needs: [merge_server, retag_server] + needs: [server, retag_server] + permissions: {} runs-on: ubuntu-latest if: always() steps: @@ -503,11 +182,13 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" success-check-ml: name: Docker Build & Push ML Success - needs: [merge_ml, retag_ml] + needs: [machine-learning, retag_ml] + permissions: {} runs-on: ubuntu-latest if: always() steps: @@ -516,4 +197,5 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index fdd30034ee..32010728cf 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -10,16 +10,22 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | docs: @@ -33,6 +39,8 @@ jobs: build: name: Docs Build needs: pre-job + permissions: + contents: read if: ${{ needs.pre-job.outputs.should_run == 'true' }} runs-on: ubuntu-latest defaults: @@ -41,10 +49,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './docs/.nvmrc' @@ -58,8 +68,9 @@ jobs: run: npm run build - name: Upload build output - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: docs-build-output path: docs/build/ + include-hidden-files: true retention-days: 1 diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index f33c0c4c03..c04adbafc6 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -1,6 +1,6 @@ name: Docs deploy on: - workflow_run: + workflow_run: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here workflows: ['Docs build'] types: - completed @@ -9,6 +9,9 @@ jobs: checks: name: Docs Deploy Checks runs-on: ubuntu-latest + permissions: + actions: read + pull-requests: read outputs: parameters: ${{ steps.parameters.outputs.result }} artifact: ${{ steps.get-artifact.outputs.result }} @@ -17,7 +20,7 @@ jobs: run: echo 'The triggering workflow did not succeed' && exit 1 - name: Get artifact id: get-artifact - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -35,7 +38,9 @@ jobs: return { found: true, id: matchArtifact.id }; - name: Determine deploy parameters id: parameters - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} with: script: | const eventType = context.payload.workflow_run.event; @@ -57,7 +62,8 @@ jobs: } else if (eventType == "pull_request") { let pull_number = context.payload.workflow_run.pull_requests[0]?.number; if(!pull_number) { - const response = await github.rest.search.issuesAndPullRequests({q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',per_page: 1,}) + const {HEAD_SHA} = process.env; + const response = await github.rest.search.issuesAndPullRequests({q: `repo:${{ github.repository }} is:pr sha:${HEAD_SHA}`,per_page: 1,}) const items = response.data.items if (items.length < 1) { throw new Error("No pull request found for the commit") @@ -95,30 +101,36 @@ jobs: name: Docs Deploy runs-on: ubuntu-latest needs: checks + permissions: + contents: read + actions: read + pull-requests: write if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Load parameters id: parameters - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + PARAM_JSON: ${{ needs.checks.outputs.parameters }} with: script: | - const json = `${{ needs.checks.outputs.parameters }}`; - const parameters = JSON.parse(json); + const parameters = JSON.parse(process.env.PARAM_JSON); core.setOutput("event", parameters.event); core.setOutput("name", parameters.name); core.setOutput("shouldDeploy", parameters.shouldDeploy); - - run: | - echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}" - - name: Download artifact - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }} with: script: | - let artifact = ${{ needs.checks.outputs.artifact }}; + let artifact = JSON.parse(process.env.ARTIFACT_JSON); let download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, @@ -138,7 +150,7 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} - uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2 + uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8 with: tg_version: '0.58.12' tofu_version: '1.7.1' @@ -153,7 +165,7 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} - uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2 + uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8 with: tg_version: '0.58.12' tofu_version: '1.7.1' @@ -162,12 +174,15 @@ jobs: - name: Output Cleaning id: clean + env: + TG_OUTPUT: ${{ steps.docs-output.outputs.tg_action_output }} run: | - TG_OUT=$(echo '${{ steps.docs-output.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .) - echo "output=$TG_OUT" >> $GITHUB_OUTPUT + CLEANED=$(echo "$TG_OUTPUT" | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .) + echo "output=$CLEANED" >> $GITHUB_OUTPUT - name: Publish to Cloudflare Pages - uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1 + # TODO: Action is deprecated + uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} @@ -184,7 +199,7 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} - uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2 + uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8 with: tg_version: '0.58.12' tofu_version: '1.7.1' @@ -192,7 +207,7 @@ jobs: tg_command: 'apply' - name: Comment - uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3 + uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0 if: ${{ steps.parameters.outputs.event == 'pr' }} with: number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }} diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index 99499528b6..cd095b117f 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -1,15 +1,22 @@ name: Docs destroy on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here types: [closed] +permissions: {} + jobs: deploy: name: Docs Destroy runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Destroy Docs Subdomain env: @@ -18,7 +25,7 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} - uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2 + uses: gruntwork-io/terragrunt-action@aee21a7df999be8b471c2a8564c6cd853cb674e1 # v2.1.8 with: tg_version: '0.58.12' tofu_version: '1.7.1' @@ -26,7 +33,7 @@ jobs: tg_command: 'destroy -refresh=false' - name: Comment - uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3 + uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0 with: number: ${{ github.event.number }} delete: true diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 9c52691a52..7a90747c12 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -4,28 +4,32 @@ on: pull_request: types: [labeled] +permissions: {} + jobs: fix-formatting: runs-on: ubuntu-latest if: ${{ github.event.label.name == 'fix:formatting' }} permissions: + contents: write pull-requests: write steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2 + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: 'Checkout' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ steps.generate-token.outputs.token }} + persist-credentials: true - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' @@ -33,13 +37,13 @@ jobs: run: make install-all && make format-all - name: Commit and push - uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9 + uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 with: default_author: github_actions message: 'chore: fix formatting' - name: Remove label - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 if: always() with: script: | diff --git a/.github/workflows/multi-runner-build.yml b/.github/workflows/multi-runner-build.yml new file mode 100644 index 0000000000..f6d7c12355 --- /dev/null +++ b/.github/workflows/multi-runner-build.yml @@ -0,0 +1,185 @@ +name: 'Multi-runner container image build' +on: + workflow_call: + inputs: + image: + description: 'Name of the image' + type: string + required: true + context: + description: 'Path to build context' + type: string + required: true + dockerfile: + description: 'Path to Dockerfile' + type: string + required: true + tag-suffix: + description: 'Suffix to append to the image tag' + type: string + default: '' + dockerhub-push: + description: 'Push to Docker Hub' + type: boolean + default: false + build-args: + description: 'Docker build arguments' + type: string + required: false + platforms: + description: 'Platforms to build for' + type: string + runner-mapping: + description: 'Mapping from platforms to runners' + type: string + secrets: + DOCKERHUB_USERNAME: + required: false + DOCKERHUB_TOKEN: + required: false + +env: + GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }} + DOCKERHUB_IMAGE: altran1502/${{ inputs.image }} + +jobs: + matrix: + name: 'Generate matrix' + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.matrix.outputs.matrix }} + key: ${{ steps.artifact-key.outputs.base }} + steps: + - name: Generate build matrix + id: matrix + shell: bash + env: + PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }} + RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }} + run: | + matrix=$(jq -R -c \ + --argjson runner_mapping "${RUNNER_MAPPING}" \ + 'split(",") | map({platform: ., runner: $runner_mapping[.]})' \ + <<< "${PLATFORMS}") + echo "${matrix}" + echo "matrix=${matrix}" >> $GITHUB_OUTPUT + + - name: Determine artifact key + id: artifact-key + shell: bash + env: + IMAGE: ${{ inputs.image }} + SUFFIX: ${{ inputs.tag-suffix }} + run: | + if [[ -n "${SUFFIX}" ]]; then + base="${IMAGE}${SUFFIX}-digests" + else + base="${IMAGE}-digests" + fi + echo "${base}" + echo "base=${base}" >> $GITHUB_OUTPUT + + build: + needs: matrix + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.matrix.outputs.matrix) }} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - uses: ./.github/actions/image-build + with: + context: ${{ inputs.context }} + dockerfile: ${{ inputs.dockerfile }} + image: ${{ env.GHCR_IMAGE }} + ghcr-token: ${{ secrets.GITHUB_TOKEN }} + platform: ${{ matrix.platform }} + artifact-key-base: ${{ needs.matrix.outputs.key }} + build-args: ${{ inputs.build-args }} + + merge: + needs: [matrix, build] + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.head.repo.fork }} + permissions: + contents: read + actions: read + packages: write + steps: + - name: Download digests + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + path: ${{ runner.temp }}/digests + pattern: ${{ needs.matrix.outputs.key }}-* + merge-multiple: true + + - name: Login to Docker Hub + if: ${{ inputs.dockerhub-push }} + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GHCR + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 + + - name: Generate docker image tags + id: meta + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 + env: + DOCKER_METADATA_PR_HEAD_SHA: 'true' + with: + flavor: | + # Disable latest tag + latest=false + suffix=${{ inputs.tag-suffix }} + images: | + name=${{ env.GHCR_IMAGE }} + name=${{ env.DOCKERHUB_IMAGE }},enable=${{ inputs.dockerhub-push }} + tags: | + # Tag with branch name + type=ref,event=branch + # Tag with pr-number + type=ref,event=pr + # Tag with long commit sha hash + type=sha,format=long,prefix=commit- + # Tag with git tag on release + type=ref,event=tag + type=raw,value=release,enable=${{ github.event_name == 'release' }} + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + # Process annotations + declare -a ANNOTATIONS=() + if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then + while IFS= read -r annotation; do + # Extract key and value by removing the manifest: prefix + if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then + key="${BASH_REMATCH[1]}" + value="${BASH_REMATCH[2]}" + # Use array to properly handle arguments with spaces + ANNOTATIONS+=(--annotation "index:$key=$value") + fi + done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON") + fi + + TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + SOURCE_ARGS=$(printf "${GHCR_IMAGE}@sha256:%s " *) + + docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS diff --git a/.github/workflows/pr-label-validation.yml b/.github/workflows/pr-label-validation.yml index 247c625a96..19f90143e0 100644 --- a/.github/workflows/pr-label-validation.yml +++ b/.github/workflows/pr-label-validation.yml @@ -1,9 +1,11 @@ name: PR Label Validation on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here types: [opened, labeled, unlabeled, synchronize] +permissions: {} + jobs: validate-release-label: runs-on: ubuntu-latest @@ -12,7 +14,7 @@ jobs: pull-requests: write steps: - name: Require PR to have a changelog label - uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5 + uses: mheap/github-action-required-labels@fb29a14a076b0f74099f6198f77750e8fc236016 # v5.5.0 with: mode: exactly count: 1 diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 1b43c89889..ad73c78cf8 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,6 +1,8 @@ name: 'Pull Request Labeler' on: - - pull_request_target + - pull_request_target # zizmor: ignore[dangerous-triggers] no attacker inputs are used here + +permissions: {} jobs: labeler: @@ -9,4 +11,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5 + - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 diff --git a/.github/workflows/pr-require-conventional-commit.yml b/.github/workflows/pr-require-conventional-commit.yml index 20dd0492f4..78ba77495c 100644 --- a/.github/workflows/pr-require-conventional-commit.yml +++ b/.github/workflows/pr-require-conventional-commit.yml @@ -4,9 +4,13 @@ on: pull_request: types: [opened, synchronize, reopened, edited] +permissions: {} + jobs: validate-pr-title: runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - name: PR Conventional Commit Validation uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index ffb24d8952..f1995bb866 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -21,35 +21,40 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }}-root cancel-in-progress: true +permissions: {} + jobs: bump_version: runs-on: ubuntu-latest - outputs: ref: ${{ steps.push-tag.outputs.commit_long_sha }} - + permissions: {} # No job-level permissions are needed because it uses the app-token steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2 + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: token: ${{ steps.generate-token.outputs.token }} + persist-credentials: true - name: Install uv - uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - name: Bump version - run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" + env: + SERVER_BUMP: ${{ inputs.serverBump }} + MOBILE_BUMP: ${{ inputs.mobileBump }} + run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}" - name: Commit and tag id: push-tag - uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9 + uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 with: default_author: github_actions message: 'chore: version ${{ env.IMMICH_VERSION }}' @@ -59,37 +64,47 @@ jobs: build_mobile: uses: ./.github/workflows/build-mobile.yml needs: bump_version - secrets: inherit + permissions: + contents: read + secrets: + KEY_JKS: ${{ secrets.KEY_JKS }} + ALIAS: ${{ secrets.ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }} with: ref: ${{ needs.bump_version.outputs.ref }} prepare_release: runs-on: ubuntu-latest needs: build_mobile - + permissions: + actions: read # To download the app artifact + # No content permissions are needed because it uses the app-token steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2 + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: token: ${{ steps.generate-token.outputs.token }} + persist-credentials: false - name: Download APK - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: release-apk-signed - name: Create draft release - uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2 + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: draft: true tag_name: ${{ env.IMMICH_VERSION }} + token: ${{ steps.generate-token.outputs.token }} generate_release_notes: true body_path: misc/release/notes.tmpl files: | diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index 447a309a2e..edd9dfdae9 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -4,6 +4,8 @@ on: pull_request: types: [labeled, closed] +permissions: {} + jobs: comment-status: runs-on: ubuntu-latest @@ -11,7 +13,7 @@ jobs: permissions: pull-requests: write steps: - - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2 + - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 with: message-id: 'preview-status' message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/' @@ -22,7 +24,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | github.rest.issues.removeLabel({ diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index cde2075423..bb3ae8f27f 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -4,20 +4,24 @@ on: release: types: [published] -permissions: - packages: write +permissions: {} jobs: publish: name: Publish `@immich/sdk` runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./open-api/typescript-sdk steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + # Setup .npmrc file to publish to npm - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './open-api/typescript-sdk/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 615082f86a..7cd28228dc 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -9,16 +9,22 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | mobile: @@ -33,15 +39,17 @@ jobs: name: Run Dart Code Analysis needs: pre-job if: ${{ needs.pre-job.outputs.should_run == 'true' }} - runs-on: ubuntu-latest - + permissions: + contents: read steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2 + uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml @@ -59,7 +67,7 @@ jobs: working-directory: ./mobile - name: Find file changes - uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20 + uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files with: files: | @@ -69,9 +77,11 @@ jobs: - name: Verify files have not changed if: steps.verify-changed-files.outputs.files_changed == 'true' + env: + CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} run: | echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory" - echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}" + echo "Changed files: ${CHANGED_FILES}" exit 1 - name: Run dart analyze @@ -85,3 +95,30 @@ jobs: - name: Run dart custom_lint run: dart run custom_lint working-directory: ./mobile + + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + + - name: Run zizmor 🌈 + run: uvx zizmor --format=sarif . > results.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + with: + sarif_file: results.sarif + category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5273698e4e..a0e8f51b06 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,10 +9,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: + should_run_i18n: ${{ steps.found_paths.outputs.i18n == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }} should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }} @@ -24,11 +29,16 @@ jobs: should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | + i18n: + - 'i18n/**' web: - 'web/**' - 'i18n/**' @@ -58,16 +68,20 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./server steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' @@ -95,16 +109,20 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./cli steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' @@ -136,16 +154,20 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} runs-on: windows-latest + permissions: + contents: read defaults: run: working-directory: ./cli steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' @@ -165,21 +187,25 @@ jobs: run: npm run test:cov if: ${{ !cancelled() }} - web-unit-tests: - name: Test & Lint Web + web-lint: + name: Lint Web needs: pre-job if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} - runs-on: ubuntu-latest + runs-on: mich + permissions: + contents: read defaults: run: working-directory: ./web steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' @@ -191,7 +217,7 @@ jobs: run: npm ci - name: Run linter - run: npm run lint + run: npm run lint:p if: ${{ !cancelled() }} - name: Run formatter @@ -202,6 +228,35 @@ jobs: run: npm run check:svelte if: ${{ !cancelled() }} + web-unit-tests: + name: Test Web + needs: pre-job + if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: read + defaults: + run: + working-directory: ./web + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: './web/.nvmrc' + + - name: Run setup typescript-sdk + run: npm ci && npm run build + working-directory: ./open-api/typescript-sdk + + - name: Run npm install + run: npm ci + - name: Run tsc run: npm run check:typescript if: ${{ !cancelled() }} @@ -210,21 +265,65 @@ jobs: run: npm run test:cov if: ${{ !cancelled() }} + i18n-tests: + name: Test i18n + needs: pre-job + if: ${{ needs.pre-job.outputs.should_run_i18n == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: './web/.nvmrc' + + - name: Install dependencies + run: npm --prefix=web ci + + - name: Format + run: npm --prefix=web run format:i18n + + - name: Find file changes + uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 + id: verify-changed-files + with: + files: | + i18n/** + + - name: Verify files have not changed + if: steps.verify-changed-files.outputs.files_changed == 'true' + env: + CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} + run: | + echo "ERROR: i18n files not up to date!" + echo "Changed files: ${CHANGED_FILES}" + exit 1 + e2e-tests-lint: name: End-to-End Lint needs: pre-job if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./e2e steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' @@ -254,16 +353,20 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./server steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' @@ -278,19 +381,25 @@ jobs: name: End-to-End Tests (Server & CLI) needs: pre-job if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }} - runs-on: mich + runs-on: ${{ matrix.runner }} + permissions: + contents: read defaults: run: working-directory: ./e2e + strategy: + matrix: + runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + persist-credentials: false submodules: 'recursive' - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' @@ -320,19 +429,25 @@ jobs: name: End-to-End Tests (Web) needs: pre-job if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }} - runs-on: mich + runs-on: ${{ matrix.runner }} + permissions: + contents: read defaults: run: working-directory: ./e2e + strategy: + matrix: + runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + persist-credentials: false submodules: 'recursive' - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' @@ -357,18 +472,43 @@ jobs: run: npx playwright test if: ${{ !cancelled() }} + success-check-e2e: + name: End-to-End Tests Success + needs: [e2e-tests-server-cli, e2e-tests-web] + permissions: {} + runs-on: ubuntu-latest + if: always() + steps: + - name: Any jobs failed? + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + - name: All jobs passed or skipped + if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] + run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" + mobile-unit-tests: name: Unit Test Mobile needs: pre-job if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Setup Flutter SDK - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2 + uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml + + - name: Generate translation file + run: make translation + working-directory: ./mobile + - name: Run tests working-directory: ./mobile run: flutter test -j 1 @@ -378,14 +518,19 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./machine-learning steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Install uv - uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5 - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 # TODO: add caching when supported (https://github.com/actions/setup-python/pull/818) # with: # python-version: 3.11 @@ -411,16 +556,20 @@ jobs: needs: pre-job if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read defaults: run: working-directory: ./.github steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './.github/.nvmrc' @@ -434,25 +583,34 @@ jobs: shellcheck: name: ShellCheck runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Run ShellCheck uses: ludeeus/action-shellcheck@master with: ignore_paths: >- **/open-api/** - **/openapi/** + **/openapi** **/node_modules/** generated-api-up-to-date: name: OpenAPI Clients runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' @@ -466,7 +624,7 @@ jobs: run: make open-api - name: Find file changes - uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20 + uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files with: files: | @@ -476,17 +634,21 @@ jobs: - name: Verify files have not changed if: steps.verify-changed-files.outputs.files_changed == 'true' + env: + CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} run: | echo "ERROR: Generated files not up to date!" - echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}" + echo "Changed files: ${CHANGED_FILES}" exit 1 - generated-typeorm-migrations-up-to-date: - name: TypeORM Checks + sql-schema-up-to-date: + name: SQL Schema Checks runs-on: ubuntu-latest + permissions: + contents: read services: postgres: - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1 env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -504,10 +666,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' @@ -521,23 +685,25 @@ jobs: run: npm run migrations:run - name: Test npm run schema:reset command works - run: npm run typeorm:schema:reset + run: npm run schema:reset - name: Generate new migrations continue-on-error: true - run: npm run migrations:generate TestMigration + run: npm run migrations:generate src/TestMigration - name: Find file changes - uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20 + uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files with: files: | server/src - name: Verify migration files have not changed if: steps.verify-changed-files.outputs.files_changed == 'true' + env: + CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} run: | echo "ERROR: Generated migration files not up to date!" - echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}" + echo "Changed files: ${CHANGED_FILES}" cat ./src/*-TestMigration.ts exit 1 @@ -547,7 +713,7 @@ jobs: DB_URL: postgres://postgres:postgres@localhost:5432/immich - name: Find file changes - uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20 + uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-sql-files with: files: | @@ -555,9 +721,11 @@ jobs: - name: Verify SQL files have not changed if: steps.verify-changed-sql-files.outputs.files_changed == 'true' + env: + CHANGED_FILES: ${{ steps.verify-changed-sql-files.outputs.changed_files }} run: | echo "ERROR: Generated SQL files not up to date!" - echo "Changed files: ${{ steps.verify-changed-sql-files.outputs.changed_files }}" + echo "Changed files: ${CHANGED_FILES}" exit 1 # mobile-integration-tests: diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 69dce3ac41..b762ac636c 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -4,30 +4,32 @@ on: pull_request: branches: [main] +permissions: {} + jobs: pre-job: runs-on: ubuntu-latest + permissions: + contents: read outputs: should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | i18n: - 'i18n/!(en)**\.json' - - name: Debug - run: | - echo "Should run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}" - echo "Found i18n paths: ${{ steps.found_paths.outputs.i18n }}" - echo "Head ref: ${{ github.head_ref }}" enforce-lock: name: Check Weblate Lock needs: [pre-job] runs-on: ubuntu-latest + permissions: {} if: ${{ needs.pre-job.outputs.should_run == 'true' }} steps: - name: Check weblate lock @@ -36,7 +38,7 @@ jobs: exit 1 fi - name: Find Pull Request - uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1 + uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 id: find-pr with: branch: chore/translations @@ -47,6 +49,7 @@ jobs: name: Weblate Lock Check Success needs: [enforce-lock] runs-on: ubuntu-latest + permissions: {} if: always() steps: - name: Any jobs failed? @@ -54,4 +57,5 @@ jobs: run: exit 1 - name: All jobs passed or skipped if: ${{ !(contains(needs.*.result, 'failure')) }} + # zizmor: ignore[template-injection] run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" diff --git a/.gitignore b/.gitignore index e0544ad8d5..b4ebd04841 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .DS_Store .vscode/* !.vscode/launch.json +!.vscode/extensions.json .idea docker/upload diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..8be57c6ba4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "svelte.svelte-vscode", + "dbaeumer.vscode-eslint", + "dart-code.flutter", + "dart-code.dart-code", + "dcmdev.dcm-vscode-extension" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 49692809bc..396755a634 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,45 +1,63 @@ { - "editor.formatOnSave": true, - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, - "[svelte]": { - "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, "editor.tabSize": 2 }, - "svelte.enable-ts-plugin": true, - "eslint.validate": [ - "javascript", - "svelte" - ], - "typescript.preferences.importModuleSpecifier": "non-relative", "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", "editor.formatOnSave": true, "editor.selectionHighlight": false, "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggestSelection": "first", "editor.tabCompletion": "onlySnippets", - "editor.wordBasedSuggestions": "off", - "editor.defaultFormatter": "Dart-Code.dart-code" + "editor.wordBasedSuggestions": "off" }, - "cSpell.words": [ - "immich" - ], + "[javascript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[svelte]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[typescript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "cSpell.words": ["immich"], + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "svelte"], "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { - "*.ts": "${capture}.spec.ts,${capture}.mock.ts", - "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart" - } -} \ No newline at end of file + "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart", + "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + }, + "svelte.enable-ts-plugin": true, + "typescript.preferences.importModuleSpecifier": "non-relative" +} diff --git a/Makefile b/Makefile index e15faa8051..1e7760ae68 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ e2e: prod: docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans +prod-down: + docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans + prod-scale: docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans diff --git a/cli/.nvmrc b/cli/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/cli/Dockerfile b/cli/Dockerfile index 356537213b..8fc39670a1 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core +FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS core WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/cli/package-lock.json b/cli/package-lock.json index 22f8980754..5373f3cdd1 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.68", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.68", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -54,14 +54,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "typescript": "^5.3.3" } }, @@ -580,9 +580,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -647,9 +647,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -697,13 +697,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -717,32 +720,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1363,9 +1353,9 @@ } }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1380,21 +1370,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1409,17 +1399,27 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -1435,14 +1435,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1453,16 +1453,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1477,9 +1477,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "license": "MIT", "engines": { @@ -1491,20 +1491,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1544,16 +1544,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1568,13 +1568,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1586,9 +1586,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz", + "integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==", "dev": true, "license": "MIT", "dependencies": { @@ -1601,7 +1601,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -1609,8 +1609,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.4", + "vitest": "3.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1619,14 +1619,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", + "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1635,13 +1635,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", + "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1662,9 +1662,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", + "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", "dev": true, "license": "MIT", "dependencies": { @@ -1675,13 +1675,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", + "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.4", "pathe": "^2.0.3" }, "funding": { @@ -1689,13 +1689,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", + "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1704,9 +1704,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", + "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -1717,13 +1717,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2183,9 +2183,9 @@ "license": "MIT" }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -2254,20 +2254,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2315,22 +2315,25 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz", - "integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", + "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", "dev": true, "license": "MIT", "dependencies": { @@ -2753,9 +2756,9 @@ } }, "node_modules/globals": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", - "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "dev": true, "license": "MIT", "engines": { @@ -4028,6 +4031,51 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -4152,15 +4200,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4247,15 +4295,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -4319,15 +4370,15 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", + "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", + "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, @@ -4361,32 +4412,61 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", + "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.4", + "@vitest/mocker": "3.1.4", + "@vitest/pretty-format": "^3.1.4", + "@vitest/runner": "3.1.4", + "@vitest/snapshot": "3.1.4", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4402,8 +4482,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.4", + "@vitest/ui": "3.1.4", "happy-dom": "*", "jsdom": "*" }, @@ -4592,16 +4672,16 @@ } }, "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yocto-queue": { diff --git a/cli/package.json b/cli/package.json index 304c2acfbd..fa5b8bc789 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.68", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" } } diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index af7e2c52a9..35b98a35f3 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -16,7 +16,7 @@ name: immich-dev services: immich-server: container_name: immich_server - command: ['/usr/src/app/bin/immich-dev'] + command: [ '/usr/src/app/bin/immich-dev' ] image: immich-server-dev:latest # extends: # file: hwaccel.transcoding.yml @@ -48,7 +48,7 @@ services: IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/ IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs - IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/third-party + IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides ulimits: nofile: soft: 1048576 @@ -70,7 +70,7 @@ services: # user: 0:0 build: context: ../web - command: ['/usr/src/app/bin/immich-web'] + command: [ '/usr/src/app/bin/immich-web' ] env_file: - .env ports: @@ -116,13 +116,13 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884 healthcheck: test: redis-cli ping || exit 1 database: container_name: immich_postgres - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 env_file: - .env environment: @@ -134,25 +134,6 @@ services: - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data ports: - 5432:5432 - healthcheck: - test: >- - pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; - Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align - --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; - echo "checksum failure count is $$Chksum"; - [ "$$Chksum" = '0' ] || exit 1 - interval: 5m - start_interval: 30s - start_period: 5m - command: >- - postgres - -c shared_preload_libraries=vectors.so - -c 'search_path="$$user", public, vectors' - -c logging_collector=on - -c max_wal_size=2GB - -c shared_buffers=512MB - -c wal_compression=on - # set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics # immich-prometheus: # container_name: immich_prometheus diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index f4a57ecbb9..412d9cccdd 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -56,14 +56,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 env_file: - .env environment: @@ -75,14 +75,6 @@ services: - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data ports: - 5432:5432 - healthcheck: - test: >- - pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1 - interval: 5m - start_interval: 30s - start_period: 5m - command: >- - postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on restart: always # set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics @@ -90,7 +82,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199 + image: prom/prometheus@sha256:78ed1f9050eb9eaf766af6e580230b1c4965728650e332cd1ee918c0c4699775 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus @@ -102,7 +94,7 @@ services: command: [ './run.sh', '-disable-reporting' ] ports: - 3000:3000 - image: grafana/grafana:11.6.0-ubuntu@sha256:fd8fa48213c624e1a95122f1d93abbf1cf1cbe85fc73212c1e599dbd76c63ff8 + image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50 volumes: - grafana-data:/var/lib/grafana diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 499673a383..aec55fe920 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,30 +49,24 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 + image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} POSTGRES_INITDB_ARGS: '--data-checksums' + # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs + # DB_STORAGE_TYPE: 'HDD' volumes: # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file - ${DB_DATA_LOCATION}:/var/lib/postgresql/data - healthcheck: - test: >- - pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1 - interval: 5m - start_interval: 30s - start_period: 5m - command: >- - postgres -c shared_preload_libraries=vectors.so -c 'search_path="$$user", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on restart: always volumes: diff --git a/docs/.nvmrc b/docs/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index 817a7dca6d..7e55e4e88f 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -23,23 +23,32 @@ Refer to the official [postgres documentation](https://www.postgresql.org/docs/c It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored. ::: -### Automatic Database Backups +### Automatic Database Dumps -For convenience, Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`. -As mentioned above, you should make your own backup of these together with the asset folders as noted below. -You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup). -By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM. +:::warning +The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files. +There is no monitoring for these dumps and you will not be notified if they are unsuccessful. +::: -#### Trigger Backup +:::caution +The database dumps do **NOT** contain any pictures or videos, only metadata. They are only usable with a copy of the other files in `UPLOAD_LOCATION` as outlined below. +::: -You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status). -Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm". -A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder. -This backup will count towards the last X backups that will be kept based on your settings. +For disaster-recovery purposes, Immich will automatically create database dumps. The dumps are stored in `UPLOAD_LOCATION/backups`. +Please be sure to make your own, independent backup of the database together with the asset folders as noted below. +You can adjust the schedule and amount of kept database dumps in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup). +By default, Immich will keep the last 14 database dumps and create a new dump every day at 2:00 AM. + +#### Trigger Dump + +You are able to trigger a database dump in the [admin job status page](http://my.immich.app/admin/jobs-status). +Visit the page, open the "Create job" modal from the top right, select "Create Database Dump" and click "Confirm". +A job will run and trigger a dump, you can verify this worked correctly by checking the logs or the `backups/` folder. +This dumps will count towards the last `X` dumps that will be kept based on your settings. #### Restoring -We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host. +We hope to make restoring simpler in future versions, for now you can find the database dumps in the `UPLOAD_LOCATION/backups` folder on your host. Then please follow the steps in the following section for restoring the database. ### Manual Backup and Restore diff --git a/docs/docs/administration/postgres-standalone.md b/docs/docs/administration/postgres-standalone.md index 2ca23e195f..d9ad331810 100644 --- a/docs/docs/administration/postgres-standalone.md +++ b/docs/docs/administration/postgres-standalone.md @@ -10,12 +10,16 @@ Running with a pre-existing Postgres server can unlock powerful administrative f ## Prerequisites -You must install pgvecto.rs into your instance of Postgres using their [instructions][vectors-install]. After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`. +You must install `pgvector` (`>= 0.7.0, < 1.0.0`), as it is a prerequisite for `vchord`. +The easiest way to do this on Debian/Ubuntu is by adding the [PostgreSQL Apt repository][pg-apt] and then +running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`). + +You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`. :::note -Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported. Postgres 17 is nominally compatible, but pgvecto.rs does not have prebuilt images or packages for it as of writing. +Immich is known to work with Postgres versions `>= 14, < 18`. -Make sure the installed version of pgvecto.rs is compatible with your version of Immich. The current accepted range for pgvecto.rs is `>= 0.2.0, < 0.4.0`. +Make sure the installed version of VectorChord is compatible with your version of Immich. The current accepted range for VectorChord is `>= 0.3.0, < 0.5.0`. ::: ## Specifying the connection URL @@ -53,21 +57,81 @@ CREATE DATABASE ; \c BEGIN; ALTER DATABASE OWNER TO ; -CREATE EXTENSION vectors; +CREATE EXTENSION vchord CASCADE; CREATE EXTENSION earthdistance CASCADE; -ALTER DATABASE SET search_path TO "$user", public, vectors; -ALTER SCHEMA vectors OWNER TO ; COMMIT; ``` -### Updating pgvecto.rs +### Updating VectorChord -When installing a new version of pgvecto.rs, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vectors UPDATE;`. +When installing a new version of VectorChord, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vchord UPDATE;`. -### Common errors +## Migrating to VectorChord -#### Permission denied for view +VectorChord is the successor extension to pgvecto.rs, allowing for higher performance, lower memory usage and higher quality results for smart search and facial recognition. -If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO ;`. +### Migrating from pgvecto.rs -[vectors-install]: https://docs.vectorchord.ai/getting-started/installation.html +Support for pgvecto.rs will be dropped in a later release, hence we recommend all users currently using pgvecto.rs to migrate to VectorChord at their convenience. There are two primary approaches to do so. + +The easiest option is to have both extensions installed during the migration: + +1. Ensure you still have pgvecto.rs installed +2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`) +3. [Install VectorChord][vchord-install] +4. Add `shared_preload_libraries= 'vchord.so, vectors.so'` to your `postgresql.conf`, making sure to include _both_ `vchord.so` and `vectors.so`. You may include other libraries here as well if needed +5. Restart the Postgres database +6. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;` using psql or your choice of database client +7. Start Immich and wait for the logs `Reindexed face_index` and `Reindexed clip_index` to be output +8. If Immich does not have superuser permissions, run the SQL command `DROP EXTENSION vectors;` +9. Drop the old schema by running `DROP SCHEMA vectors;` +10. Remove the `vectors.so` entry from the `shared_preload_libraries` setting +11. Restart the Postgres database +12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord` + +If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps: + +1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later + +```sql +SELECT atttypmod as dimsize + FROM pg_attribute f + JOIN pg_class c ON c.oid = f.attrelid + WHERE c.relkind = 'r'::char + AND f.attnum > 0 + AND c.relname = 'smart_search'::text + AND f.attname = 'embedding'::text; +``` + +2. Remove references to pgvecto.rs using the below SQL commands + +```sql +DROP INDEX IF EXISTS clip_index; +DROP INDEX IF EXISTS face_index; +ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE real[]; +ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]; +``` + +3. [Install VectorChord][vchord-install] +4. Change the columns back to the appropriate vector types, replacing `` with the number from step 1 + +```sql +CREATE EXTENSION IF NOT EXISTS vchord CASCADE; +ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(); +ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512); +``` + +5. Start Immich and let it create new indices using VectorChord + +### Migrating from pgvector + +1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client +2. Follow the Prerequisites to install VectorChord +3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;` +4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set +5. Start Immich and let it create new indices using VectorChord + +Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps. + +[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html +[pg-apt]: https://www.postgresql.org/download/linux/#generic diff --git a/docs/docs/administration/reverse-proxy.md b/docs/docs/administration/reverse-proxy.md index 06fe3ee5bb..742f0e110f 100644 --- a/docs/docs/administration/reverse-proxy.md +++ b/docs/docs/administration/reverse-proxy.md @@ -22,7 +22,7 @@ server { client_max_body_size 50000M; # Set headers - proxy_set_header Host $http_host; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; diff --git a/docs/docs/developer/database-migrations.md b/docs/docs/developer/database-migrations.md index 2cddf5f386..c0c61340cb 100644 --- a/docs/docs/developer/database-migrations.md +++ b/docs/docs/developer/database-migrations.md @@ -1,14 +1,14 @@ # Database Migrations -After making any changes in the `server/src/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration. +After making any changes in the `server/src/schema`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration. 1. Run the command ```bash -npm run typeorm:migrations:generate +npm run migrations:generate ``` 2. Check if the migration file makes sense. -3. Move the migration file to folder `./server/src/migrations` in your code editor. +3. Move the migration file to folder `./server/src/schema/migrations` in your code editor. The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately. diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index 76106803e8..32705e3248 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -75,11 +75,12 @@ npm run dev To see local changes to `@immich/ui` in Immich, do the following: 1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui` -1. Build the `@immich/ui` project via `npm run build` -1. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) -1. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) -1. Start up the stack via `make dev` -1. After making changes in `@immich/ui`, rebuild it (`npm run build`) +2. Build the `@immich/ui` project via `npm run build` +3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) +4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) +5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';` +6. Start up the stack via `make dev` +7. After making changes in `@immich/ui`, rebuild it (`npm run build`) ### Mobile app @@ -114,32 +115,72 @@ Note: Activating the license is not required. ### VSCode -Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions. +Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions. These extensions are listed in the `extensions.json` file under `.vscode/` and should appear as workspace recommendations. -in User `settings.json` (`cmd + shift + p` and search for `Open User Settings JSON`) add the following: +Here are the settings we use, they should be active as workspace settings (`settings.json`): ```json title="settings.json" { - "editor.formatOnSave": true, - "[javascript][typescript][css]": { + "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.tabSize": 2, - "editor.formatOnSave": true - }, - "[svelte]": { - "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, "editor.tabSize": 2 }, - "svelte.enable-ts-plugin": true, - "eslint.validate": ["javascript", "svelte"], "[dart]": { + "editor.defaultFormatter": "Dart-Code.dart-code", "editor.formatOnSave": true, "editor.selectionHighlight": false, "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggestSelection": "first", "editor.tabCompletion": "onlySnippets", - "editor.wordBasedSuggestions": "off", - "editor.defaultFormatter": "Dart-Code.dart-code" - } + "editor.wordBasedSuggestions": "off" + }, + "[javascript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[svelte]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "svelte.svelte-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "[typescript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.removeUnusedImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.tabSize": 2 + }, + "cSpell.words": ["immich"], + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "svelte"], + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.patterns": { + "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart", + "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + }, + "svelte.enable-ts-plugin": true, + "typescript.preferences.importModuleSpecifier": "non-relative" } ``` diff --git a/docs/docs/features/casting.md b/docs/docs/features/casting.md new file mode 100644 index 0000000000..cc25e24da7 --- /dev/null +++ b/docs/docs/features/casting.md @@ -0,0 +1,11 @@ +# Chromecast support + +Immich supports the Google's Cast protocol so that photos and videos can be cast to devices such as a Chromecast and a Nest Hub. This feature is considered experimental and has several important limitations listed below. Currently, this feature is only supported by the web client, support on Android and iOS is planned for the future. + +## Limitations + +To use casting with Immich, there are a few prerequisites: + +1. Your instance must be accessed via an HTTPS connection in order for the casting menu to show. +2. Your instance must be publicly accessible via HTTPS and a DNS record for the server must be accessible via Google's DNS servers (`8.8.8.8` and `8.8.4.4`) +3. Videos must be in a format that is compatible with Google Cast. For more info, check out [Google's documentation](https://developers.google.com/cast/docs/media) diff --git a/docs/docs/features/hardware-transcoding.md b/docs/docs/features/hardware-transcoding.md index 18c7f6b298..d28cd97de0 100644 --- a/docs/docs/features/hardware-transcoding.md +++ b/docs/docs/features/hardware-transcoding.md @@ -121,6 +121,6 @@ Once this is done, you can continue to step 3 of "Basic Setup". [hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml [nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html -[jellyfin-lp]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-and-verify-lp-mode-on-linux -[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#known-issues-and-limitations +[jellyfin-lp]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#low-power-encoding +[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#known-issues-and-limitations-on-linux [libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index 9eb05383b3..9f094509f4 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -72,7 +72,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up. ### Nightly job -There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library managment page. +There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page. ## Usage diff --git a/docs/docs/features/ml-hardware-acceleration.md b/docs/docs/features/ml-hardware-acceleration.md index 8371e726b9..a94f8c8c64 100644 --- a/docs/docs/features/ml-hardware-acceleration.md +++ b/docs/docs/features/ml-hardware-acceleration.md @@ -42,7 +42,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - The GPU must have compute capability 5.2 or greater. - The server must have the official NVIDIA driver installed. -- The installed driver must be >= 535 (it must support CUDA 12.2). +- The installed driver must be >= 545 (it must support CUDA 12.3). - On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed. #### ROCm diff --git a/docs/docs/features/searching.md b/docs/docs/features/searching.md index 0ee1d01000..d7ebd1a468 100644 --- a/docs/docs/features/searching.md +++ b/docs/docs/features/searching.md @@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem'; Immich uses Postgres as its search database for both metadata and contextual CLIP search. -Contextual CLIP search is powered by the [pgvecto.rs](https://github.com/tensorchord/pgvecto.rs) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata. +Contextual CLIP search is powered by the [VectorChord](https://github.com/tensorchord/VectorChord) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata. ## Advanced Search Filters @@ -92,7 +92,7 @@ Memory and execution time estimates were obtained without acceleration on a 7800 **Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input. -**Memory (MiB)**: The peak RSS usage of the process afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors. +**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors. **Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets. diff --git a/docs/docs/guides/custom-map-styles.md b/docs/docs/guides/custom-map-styles.md index 3f52937432..1a61afc324 100644 --- a/docs/docs/guides/custom-map-styles.md +++ b/docs/docs/guides/custom-map-styles.md @@ -14,14 +14,14 @@ online generators you can use. 2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.) 3. Save your selections. Reload the map, and enjoy your custom map style! -## Use Maptiler to build a custom style +## Use MapTiler to build a custom style -Customizing the map style can be done easily using Maptiler, if you do not want to write an entire JSON document by hand. +Customizing the map style can be done easily using MapTiler, if you do not want to write an entire JSON document by hand. 1. Create a free account at https://cloud.maptiler.com 2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there. 3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer. 4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account. -5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. Maptiler will present an interactive side-by-side map with the original and your changes prior to publication.
![Maptiler Publication Settings](img/immich_map_styles_publish.webp) -6. Maptiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay. -7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to Maptiler. +5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. MapTiler will present an interactive side-by-side map with the original and your changes prior to publication.
![MapTiler Publication Settings](img/immich_map_styles_publish.webp) +6. MapTiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay. +7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to MapTiler. diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 89a4f07bc0..209f673993 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -1,7 +1,7 @@ # Database Queries :::danger -Keep in mind that mucking around in the database might set the moon on fire. Avoid modifying the database directly when possible, and always have current backups. +Keep in mind that mucking around in the database might set the Moon on fire. Avoid modifying the database directly when possible, and always have current backups. ::: :::tip diff --git a/docs/docs/install/docker-compose.mdx b/docs/docs/install/docker-compose.mdx index fe5c043d01..7a0b566f5d 100644 --- a/docs/docs/install/docker-compose.mdx +++ b/docs/docs/install/docker-compose.mdx @@ -2,53 +2,13 @@ sidebar_position: 30 --- -import CodeBlock from '@theme/CodeBlock'; -import ExampleEnv from '!!raw-loader!../../../docker/example.env'; - # Docker Compose [Recommended] Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose. -## Step 1 - Download the required files +import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx'; -Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files. - -```bash title="Move to the directory you created" -mkdir ./immich-app -cd ./immich-app -``` - -Download [`docker-compose.yml`][compose-file] and [`example.env`][env-file] by running the following commands: - -```bash title="Get docker-compose.yml file" -wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml -``` - -```bash title="Get .env file" -wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env -``` - -You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`. - -## Step 2 - Populate the .env file with custom values - - - {ExampleEnv} - - -- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space. -- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication. - To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this. -- Set your timezone by uncommenting the `TZ=` line. -- Populate custom database information if necessary. - -## Step 3 - Start the containers - -From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service: - -```bash title="Start the containers" -docker compose up -d -``` + :::info Docker version If you get an error such as `unknown shorthand flag: 'd' in -d` or `open : permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones. @@ -70,6 +30,3 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc ## Next Steps Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md). - -[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml -[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index e11547d240..d3ca49a0a4 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -72,20 +72,21 @@ Information on the current workers can be found [here](/docs/administration/jobs ## Database -| Variable | Description | Default | Containers | -| :---------------------------------- | :----------------------------------------------------------------------- | :----------: | :----------------------------- | -| `DB_URL` | Database URL | | server | -| `DB_HOSTNAME` | Database host | `database` | server | -| `DB_PORT` | Database port | `5432` | server | -| `DB_USERNAME` | Database user | `postgres` | server, database\*1 | -| `DB_PASSWORD` | Database password | `postgres` | server, database\*1 | -| `DB_DATABASE_NAME` | Database name | `immich` | server, database\*1 | -| `DB_VECTOR_EXTENSION`\*2 | Database vector extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server | -| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server | +| Variable | Description | Default | Containers | +| :---------------------------------- | :--------------------------------------------------------------------------- | :--------: | :----------------------------- | +| `DB_URL` | Database URL | | server | +| `DB_HOSTNAME` | Database host | `database` | server | +| `DB_PORT` | Database port | `5432` | server | +| `DB_USERNAME` | Database user | `postgres` | server, database\*1 | +| `DB_PASSWORD` | Database password | `postgres` | server, database\*1 | +| `DB_DATABASE_NAME` | Database name | `immich` | server, database\*1 | +| `DB_SSL_MODE` | Database SSL mode | | server | +| `DB_VECTOR_EXTENSION`\*2 | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server | +| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server | \*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`. -\*2: This setting cannot be changed after the server has successfully started up. +\*2: If not provided, the appropriate extension to use is auto-detected at startup by introspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvecto.rs, pgvector. :::info diff --git a/docs/docs/install/synology.md b/docs/docs/install/synology.md index 0ff6a3b85c..e8bca61489 100644 --- a/docs/docs/install/synology.md +++ b/docs/docs/install/synology.md @@ -29,7 +29,7 @@ Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/la ## Step 2 - Populate the .env file with custom values -Follow [Step 2 in Docker Compose](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue. +Follow [Step 2 in Docker Compose](/docs/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue. ## Step 3 - Create a new project in Container Manager diff --git a/docs/docs/install/truenas.md b/docs/docs/install/truenas.md index 3cd772de63..bd0dc7af5f 100644 --- a/docs/docs/install/truenas.md +++ b/docs/docs/install/truenas.md @@ -2,7 +2,7 @@ sidebar_position: 80 --- -# TrueNAS SCALE [Community] +# TrueNAS [Community] :::note This is a community contribution and not officially supported by the Immich team, but included here for convenience. @@ -12,17 +12,17 @@ Community support can be found in the dedicated channel on the [Discord Server]( **Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).** ::: -Immich can easily be installed on TrueNAS SCALE via the **Community** train application. -Consider reviewing the TrueNAS [Apps tutorial](https://www.truenas.com/docs/scale/scaletutorials/apps/) if you have not previously configured applications on your system. +Immich can easily be installed on TrueNAS Community Edition via the **Community** train application. +Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system. -TrueNAS SCALE makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries. +TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries. ## First Steps -The Immich app in TrueNAS SCALE installs, completes the initial configuration, then starts the Immich web portal. -When updates become available, SCALE alerts and provides easy updates. +The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal. +When updates become available, TrueNAS alerts and provides easy updates. -Before installing the Immich app in SCALE, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation. +Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation. You may also configure environment variables at any time after deploying the application. ### Setting up Storage Datasets @@ -126,9 +126,9 @@ className="border rounded-xl" Accept the default port `30041` in **WebUI Port** or enter a custom port number. :::info Allowed Port Numbers -Only numbers within the range 9000-65535 may be used on SCALE versions below TrueNAS Scale 24.10 Electric Eel. +Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel. -Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/references/defaultports/). +Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports). ::: ### Storage Configuration @@ -173,7 +173,7 @@ className="border rounded-xl" You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**. The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich. -The **Host Path** is the location on the TrueNAS SCALE server where your external library is located. +The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located. @@ -188,17 +188,17 @@ className="border rounded-xl" Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core). -Accept the default **Memory** limit of `4096` MB or specify the number of MB of RAM. If you're using Machine Learning you should probably set this above 8000 MB. +Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. -:::info Older SCALE Versions -Before TrueNAS SCALE version 24.10 Electric Eel: +:::info Older TrueNAS Versions +Before TrueNAS Community Edition version 24.10 Electric Eel: The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads. The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000` ::: -Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://www.truenas.com/docs/truenasapps/#gpu-passthrough) +Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) ### Install @@ -240,7 +240,7 @@ className="border rounded-xl" /> :::info -Some Environment Variables are not available for the TrueNAS SCALE app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). +Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. ::: @@ -251,7 +251,7 @@ Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ` Make sure to read the general [upgrade instructions](/docs/install/upgrading.md). ::: -When updates become available, SCALE alerts and provides easy updates. +When updates become available, TrueNAS alerts and provides easy updates. To update the app to the latest version: - Go to the **Installed Applications** screen and select Immich from the list of installed applications. diff --git a/docs/docs/overview/comparison.md b/docs/docs/overview/comparison.md index 5d5e68f839..b843562c6e 100644 --- a/docs/docs/overview/comparison.md +++ b/docs/docs/overview/comparison.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 --- # Comparison diff --git a/docs/docs/overview/img/social-preview-light.webp b/docs/docs/overview/img/social-preview-light.webp new file mode 100644 index 0000000000..3d088f6522 Binary files /dev/null and b/docs/docs/overview/img/social-preview-light.webp differ diff --git a/docs/docs/overview/quick-start.mdx b/docs/docs/overview/quick-start.mdx index 872c0a42a2..28cee15007 100644 --- a/docs/docs/overview/quick-start.mdx +++ b/docs/docs/overview/quick-start.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 2 --- # Quick start @@ -10,11 +10,20 @@ to install and use it. ## Requirements -Check the [requirements page](/docs/install/requirements) to get started. +- A system with at least 4GB of RAM and 2 CPU cores. +- [Docker](https://docs.docker.com/engine/install/) + +> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements). + +--- ## Set up the server -Follow the [Docker Compose (Recommended)](/docs/install/docker-compose) instructions to install the server. +import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx'; + + + +--- ## Try the web app @@ -26,6 +35,8 @@ Try uploading a picture from your browser. +--- + ## Try the mobile app ### Download the Mobile App @@ -56,6 +67,8 @@ You can select the **Jobs** tab to see Immich processing your photos. +--- + ## Review the database backup and restore process Immich has built-in database backups. You can refer to the @@ -65,6 +78,8 @@ Immich has built-in database backups. You can refer to the The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`. ::: +--- + ## Where to go from here? You may decide you'd like to install the server a different way; the Install category on the left menu provides many options. diff --git a/docs/docs/overview/introduction.mdx b/docs/docs/overview/welcome.mdx similarity index 90% rename from docs/docs/overview/introduction.mdx rename to docs/docs/overview/welcome.mdx index e09aac6ebf..93ce705369 100644 --- a/docs/docs/overview/introduction.mdx +++ b/docs/docs/overview/welcome.mdx @@ -2,9 +2,13 @@ sidebar_position: 1 --- -# Introduction +# Welcome to Immich -Immich - Self-hosted photos and videos backup tool +Immich - Self-hosted photos and videos backup tool ## Welcome! diff --git a/docs/docs/partials/_docker-compose-install-steps.mdx b/docs/docs/partials/_docker-compose-install-steps.mdx new file mode 100644 index 0000000000..c538a6051e --- /dev/null +++ b/docs/docs/partials/_docker-compose-install-steps.mdx @@ -0,0 +1,43 @@ +import CodeBlock from '@theme/CodeBlock'; +import ExampleEnv from '!!raw-loader!../../../docker/example.env'; + +### Step 1 - Download the required files + +Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files. + +```bash title="Move to the directory you created" +mkdir ./immich-app +cd ./immich-app +``` + +Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands: + +```bash title="Get docker-compose.yml file" +wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml +``` + +```bash title="Get .env file" +wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env +``` + +You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`. + +### Step 2 - Populate the .env file with custom values + + + {ExampleEnv} + + +- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space. +- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication. + To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this. +- Set your timezone by uncommenting the `TZ=` line. +- Populate custom database information if necessary. + +### Step 3 - Start the containers + +From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service: + +```bash title="Start the containers" +docker compose up -d +``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 7166611a2e..d612dda253 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -95,7 +95,7 @@ const config = { position: 'right', }, { - to: '/docs/overview/introduction', + to: '/docs/overview/welcome', position: 'right', label: 'Docs', }, @@ -124,6 +124,12 @@ const config = { label: 'Discord', position: 'right', }, + { + type: 'html', + position: 'right', + value: + '', + }, ], }, footer: { @@ -134,7 +140,7 @@ const config = { items: [ { label: 'Welcome', - to: '/docs/overview/introduction', + to: '/docs/overview/welcome', }, { label: 'Installation', diff --git a/docs/package.json b/docs/package.json index 27a7651f78..e13e85ecb3 100644 --- a/docs/package.json +++ b/docs/package.json @@ -57,6 +57,6 @@ "node": ">=20" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" } } diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index b30544d461..03a384162b 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -40,13 +40,9 @@ const projects: CommunityProjectProps[] = [ }, { title: 'Lightroom Immich Plugin: lrc-immich-plugin', - description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.', - url: 'https://github.com/bmachek/lrc-immich-plugin', - }, - { - title: 'Immich Duplicate Finder', - description: 'Webapp that uses machine learning to identify near-duplicate images.', - url: 'https://github.com/vale46n1/immich_duplicate_finder', + description: + 'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.', + url: 'https://blog.fokuspunk.de/lrc-immich-plugin/', }, { title: 'Immich-Tiktok-Remover', diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index fd3f199ce8..7f8c6d5761 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -7,14 +7,22 @@ @tailwind components; @tailwind utilities; -@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); - -body { - font-family: 'Be Vietnam Pro', serif; - font-optical-sizing: auto; - /* font-size: 1.125rem; +@font-face { + font-family: 'Overpass'; + src: url('/fonts/overpass/Overpass.ttf') format('truetype-variations'); + font-weight: 1 999; + font-style: normal; ascent-override: 106.25%; - size-adjust: 106.25%; */ + size-adjust: 106.25%; +} + +@font-face { + font-family: 'Overpass Mono'; + src: url('/fonts/overpass/OverpassMono.ttf') format('truetype-variations'); + font-weight: 1 999; + font-style: normal; + ascent-override: 106.25%; + size-adjust: 106.25%; } .breadcrumbs__link { @@ -29,6 +37,7 @@ img { /* You can override the default Infima variables here. */ :root { + font-family: 'Overpass', sans-serif; --ifm-color-primary: #4250af; --ifm-color-primary-dark: #4250af; --ifm-color-primary-darker: #4250af; @@ -59,14 +68,12 @@ div[class^='announcementBar_'] { } .menu__link { - padding: 10px; - padding-left: 16px; + padding: 10px 10px 10px 16px; border-radius: 24px; margin-right: 16px; } .menu__list-item-collapsible { - border-radius: 10px; margin-right: 16px; border-radius: 24px; } @@ -83,3 +90,12 @@ div[class*='navbar__items'] > li:has(a[class*='version-switcher-34ab39']) { code { font-weight: 600; } + +.buy-button { + padding: 8px 14px; + border: 1px solid transparent; + font-family: 'Overpass', sans-serif; + font-weight: 500; + cursor: pointer; + box-shadow: 0 0 5px 2px rgba(181, 206, 254, 0.4); +} diff --git a/docs/src/pages/cursed-knowledge.tsx b/docs/src/pages/cursed-knowledge.tsx index 1e5c724d16..6a0981a596 100644 --- a/docs/src/pages/cursed-knowledge.tsx +++ b/docs/src/pages/cursed-knowledge.tsx @@ -2,6 +2,7 @@ import { mdiBug, mdiCalendarToday, mdiCrosshairsOff, + mdiCrop, mdiDatabase, mdiLeadPencil, mdiLockOff, @@ -22,6 +23,18 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri type Item = Omit & { date: Date }; const items: Item[] = [ + { + icon: mdiCrop, + iconColor: 'tomato', + title: 'Image dimensions in EXIF metadata are cursed', + description: + 'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.', + link: { + url: 'https://github.com/immich-app/immich/pull/17974', + text: '#17974', + }, + date: new Date(2025, 5, 5), + }, { icon: mdiMicrosoftWindows, iconColor: '#357EC7', diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md new file mode 100644 index 0000000000..e9bf09770c --- /dev/null +++ b/docs/src/pages/errors.md @@ -0,0 +1,5 @@ +# Errors + +## TypeORM Upgrade + +The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 2ffe1debc7..277a1d0b46 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -4,6 +4,7 @@ import Layout from '@theme/Layout'; import { discordPath, discordViewBox } from '@site/src/components/svg-paths'; import ThemedImage from '@theme/ThemedImage'; import Icon from '@mdi/react'; + function HomepageHeader() { return (
@@ -12,11 +13,14 @@ function HomepageHeader() {
- + + + +

Self-hosted{' '} @@ -27,7 +31,7 @@ function HomepageHeader() { solution

-

+

Easily back up, organize, and manage your photos on your own server. Immich helps you browse, search and organize your photos and videos with ease, without sacrificing your privacy. @@ -35,27 +39,21 @@ function HomepageHeader() {

- Get started + Get Started - Demo - - - - Buy Merch + Open Demo
-
+ +
+ + +
+ + Download APK + +

This project is available under GNU AGPL v3 license.

-

Privacy should not be a luxury

+

Privacy should not be a luxury

); diff --git a/docs/src/pages/privacy-policy.tsx b/docs/src/pages/privacy-policy.tsx index 7bc113c0fc..36ac76945d 100644 --- a/docs/src/pages/privacy-policy.tsx +++ b/docs/src/pages/privacy-policy.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import Link from '@docusaurus/Link'; import Layout from '@theme/Layout'; function HomepageHeader() { return ( diff --git a/docs/src/pages/roadmap.tsx b/docs/src/pages/roadmap.tsx index b7ded8e8c9..1258000052 100644 --- a/docs/src/pages/roadmap.tsx +++ b/docs/src/pages/roadmap.tsx @@ -76,13 +76,18 @@ import { mdiWeb, mdiDatabaseOutline, mdiLinkEdit, + mdiTagFaces, mdiMovieOpenPlayOutline, + mdiCast, } from '@mdi/js'; import Layout from '@theme/Layout'; import React from 'react'; import { Item, Timeline } from '../components/timeline'; const releases = { + 'v1.133.0': new Date(2025, 4, 21), + 'v1.130.0': new Date(2025, 2, 25), + 'v1.127.0': new Date(2025, 1, 26), 'v1.122.0': new Date(2024, 11, 5), 'v1.120.0': new Date(2024, 10, 6), 'v1.114.0': new Date(2024, 8, 6), @@ -213,14 +218,6 @@ const roadmap: Item[] = [ iconColor: 'indianred', title: 'Stable release', description: 'Immich goes stable', - getDateLabel: () => 'Planned for early 2025', - }, - { - done: false, - icon: mdiLockOutline, - iconColor: 'sandybrown', - title: 'Private/locked photos', - description: 'Private assets with extra protections', getDateLabel: () => 'Planned for 2025', }, { @@ -242,6 +239,27 @@ const roadmap: Item[] = [ ]; const milestones: Item[] = [ + withRelease({ + icon: mdiCast, + iconColor: 'aqua', + title: 'Google Cast (web)', + description: 'Cast assets to Google Cast/Chromecast compatible devices', + release: 'v1.133.0', + }), + withRelease({ + icon: mdiLockOutline, + iconColor: 'sandybrown', + title: 'Private/locked photos', + description: 'Private assets with extra protections', + release: 'v1.133.0', + }), + withRelease({ + icon: mdiFolderMultiple, + iconColor: 'brown', + title: 'Folders view in the mobile app', + description: 'Browse your photos and videos in their folder structure inside the mobile app', + release: 'v1.130.0', + }), { icon: mdiStar, iconColor: 'gold', @@ -249,6 +267,14 @@ const milestones: Item[] = [ description: 'Reached 60K Stars on GitHub!', getDateLabel: withLanguage(new Date(2025, 2, 4)), }, + withRelease({ + icon: mdiTagFaces, + iconColor: 'teal', + title: 'Manual face tagging', + description: + 'Manually tag or remove faces in photos and videos, even when automatic detection misses or misidentifies them.', + release: 'v1.127.0', + }), withRelease({ icon: mdiLinkEdit, iconColor: 'crimson', @@ -266,8 +292,8 @@ const milestones: Item[] = [ withRelease({ icon: mdiDatabaseOutline, iconColor: 'brown', - title: 'Automatic database backups', - description: 'Database backups are now integrated into the Immich server', + title: 'Automatic database dumps', + description: 'Database dumps are now integrated into the Immich server', release: 'v1.120.0', }), { @@ -300,7 +326,7 @@ const milestones: Item[] = [ withRelease({ icon: mdiFolderMultiple, iconColor: 'brown', - title: 'Folders', + title: 'Folders view', description: 'Browse your photos and videos in their folder structure', release: 'v1.113.0', }), diff --git a/docs/static/.well-known/security.txt b/docs/static/.well-known/security.txt new file mode 100644 index 0000000000..5a8414c3e2 --- /dev/null +++ b/docs/static/.well-known/security.txt @@ -0,0 +1,5 @@ +Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md +Contact: mailto:security@immich.app +Preferred-Languages: en +Expires: 2026-05-01T23:59:00.000Z +Canonical: https://immich.app/.well-known/security.txt diff --git a/docs/static/_redirects b/docs/static/_redirects index 889b4e6961..6683c78077 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -30,3 +30,4 @@ /docs/guides/api-album-sync /docs/community-projects 307 /docs/guides/remove-offline-files /docs/community-projects 307 /milestones /roadmap 307 +/docs/overview/introduction /docs/overview/welcome 307 \ No newline at end of file diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 247d5749e9..31543e7abb 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,36 +1,28 @@ [ + { + "label": "v1.134.0", + "url": "https://v1.134.0.archive.immich.app" + }, + { + "label": "v1.133.1", + "url": "https://v1.133.1.archive.immich.app" + }, + { + "label": "v1.133.0", + "url": "https://v1.133.0.archive.immich.app" + }, + { + "label": "v1.132.3", + "url": "https://v1.132.3.archive.immich.app" + }, { "label": "v1.131.3", "url": "https://v1.131.3.archive.immich.app" }, - { - "label": "v1.131.2", - "url": "https://v1.131.2.archive.immich.app" - }, - { - "label": "v1.131.1", - "url": "https://v1.131.1.archive.immich.app" - }, - { - "label": "v1.131.0", - "url": "https://v1.131.0.archive.immich.app" - }, { "label": "v1.130.3", "url": "https://v1.130.3.archive.immich.app" }, - { - "label": "v1.130.2", - "url": "https://v1.130.2.archive.immich.app" - }, - { - "label": "v1.130.1", - "url": "https://v1.130.1.archive.immich.app" - }, - { - "label": "v1.130.0", - "url": "https://v1.130.0.archive.immich.app" - }, { "label": "v1.129.0", "url": "https://v1.129.0.archive.immich.app" @@ -47,46 +39,14 @@ "label": "v1.126.1", "url": "https://v1.126.1.archive.immich.app" }, - { - "label": "v1.126.0", - "url": "https://v1.126.0.archive.immich.app" - }, { "label": "v1.125.7", "url": "https://v1.125.7.archive.immich.app" }, - { - "label": "v1.125.6", - "url": "https://v1.125.6.archive.immich.app" - }, - { - "label": "v1.125.5", - "url": "https://v1.125.5.archive.immich.app" - }, - { - "label": "v1.125.3", - "url": "https://v1.125.3.archive.immich.app" - }, - { - "label": "v1.125.2", - "url": "https://v1.125.2.archive.immich.app" - }, - { - "label": "v1.125.1", - "url": "https://v1.125.1.archive.immich.app" - }, { "label": "v1.124.2", "url": "https://v1.124.2.archive.immich.app" }, - { - "label": "v1.124.1", - "url": "https://v1.124.1.archive.immich.app" - }, - { - "label": "v1.124.0", - "url": "https://v1.124.0.archive.immich.app" - }, { "label": "v1.123.0", "url": "https://v1.123.0.archive.immich.app" @@ -95,18 +55,6 @@ "label": "v1.122.3", "url": "https://v1.122.3.archive.immich.app" }, - { - "label": "v1.122.2", - "url": "https://v1.122.2.archive.immich.app" - }, - { - "label": "v1.122.1", - "url": "https://v1.122.1.archive.immich.app" - }, - { - "label": "v1.122.0", - "url": "https://v1.122.0.archive.immich.app" - }, { "label": "v1.121.0", "url": "https://v1.121.0.archive.immich.app" @@ -115,34 +63,14 @@ "label": "v1.120.2", "url": "https://v1.120.2.archive.immich.app" }, - { - "label": "v1.120.1", - "url": "https://v1.120.1.archive.immich.app" - }, - { - "label": "v1.120.0", - "url": "https://v1.120.0.archive.immich.app" - }, { "label": "v1.119.1", "url": "https://v1.119.1.archive.immich.app" }, - { - "label": "v1.119.0", - "url": "https://v1.119.0.archive.immich.app" - }, { "label": "v1.118.2", "url": "https://v1.118.2.archive.immich.app" }, - { - "label": "v1.118.1", - "url": "https://v1.118.1.archive.immich.app" - }, - { - "label": "v1.118.0", - "url": "https://v1.118.0.archive.immich.app" - }, { "label": "v1.117.0", "url": "https://v1.117.0.archive.immich.app" @@ -151,14 +79,6 @@ "label": "v1.116.2", "url": "https://v1.116.2.archive.immich.app" }, - { - "label": "v1.116.1", - "url": "https://v1.116.1.archive.immich.app" - }, - { - "label": "v1.116.0", - "url": "https://v1.116.0.archive.immich.app" - }, { "label": "v1.115.0", "url": "https://v1.115.0.archive.immich.app" @@ -171,18 +91,10 @@ "label": "v1.113.1", "url": "https://v1.113.1.archive.immich.app" }, - { - "label": "v1.113.0", - "url": "https://v1.113.0.archive.immich.app" - }, { "label": "v1.112.1", "url": "https://v1.112.1.archive.immich.app" }, - { - "label": "v1.112.0", - "url": "https://v1.112.0.archive.immich.app" - }, { "label": "v1.111.0", "url": "https://v1.111.0.archive.immich.app" @@ -195,14 +107,6 @@ "label": "v1.109.2", "url": "https://v1.109.2.archive.immich.app" }, - { - "label": "v1.109.1", - "url": "https://v1.109.1.archive.immich.app" - }, - { - "label": "v1.109.0", - "url": "https://v1.109.0.archive.immich.app" - }, { "label": "v1.108.0", "url": "https://v1.108.0.archive.immich.app" @@ -211,38 +115,14 @@ "label": "v1.107.2", "url": "https://v1.107.2.archive.immich.app" }, - { - "label": "v1.107.1", - "url": "https://v1.107.1.archive.immich.app" - }, - { - "label": "v1.107.0", - "url": "https://v1.107.0.archive.immich.app" - }, { "label": "v1.106.4", "url": "https://v1.106.4.archive.immich.app" }, - { - "label": "v1.106.3", - "url": "https://v1.106.3.archive.immich.app" - }, - { - "label": "v1.106.2", - "url": "https://v1.106.2.archive.immich.app" - }, - { - "label": "v1.106.1", - "url": "https://v1.106.1.archive.immich.app" - }, { "label": "v1.105.1", "url": "https://v1.105.1.archive.immich.app" }, - { - "label": "v1.105.0", - "url": "https://v1.105.0.archive.immich.app" - }, { "label": "v1.104.0", "url": "https://v1.104.0.archive.immich.app" @@ -251,26 +131,10 @@ "label": "v1.103.1", "url": "https://v1.103.1.archive.immich.app" }, - { - "label": "v1.103.0", - "url": "https://v1.103.0.archive.immich.app" - }, { "label": "v1.102.3", "url": "https://v1.102.3.archive.immich.app" }, - { - "label": "v1.102.2", - "url": "https://v1.102.2.archive.immich.app" - }, - { - "label": "v1.102.1", - "url": "https://v1.102.1.archive.immich.app" - }, - { - "label": "v1.102.0", - "url": "https://v1.102.0.archive.immich.app" - }, { "label": "v1.101.0", "url": "https://v1.101.0.archive.immich.app" diff --git a/docs/static/fonts/overpass/Overpass-Italic.ttf b/docs/static/fonts/overpass/Overpass-Italic.ttf new file mode 100644 index 0000000000..281dd742bb Binary files /dev/null and b/docs/static/fonts/overpass/Overpass-Italic.ttf differ diff --git a/docs/static/fonts/overpass/Overpass.ttf b/docs/static/fonts/overpass/Overpass.ttf new file mode 100644 index 0000000000..1cf730a5ad Binary files /dev/null and b/docs/static/fonts/overpass/Overpass.ttf differ diff --git a/docs/static/fonts/overpass/OverpassMono.ttf b/docs/static/fonts/overpass/OverpassMono.ttf new file mode 100644 index 0000000000..71ef818b33 Binary files /dev/null and b/docs/static/fonts/overpass/OverpassMono.ttf differ diff --git a/docs/static/img/download-apk-github.svg b/docs/static/img/download-apk-github.svg new file mode 100644 index 0000000000..3fad724350 --- /dev/null +++ b/docs/static/img/download-apk-github.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/static/img/logomark-dark-with-futo.svg b/docs/static/img/logomark-dark-with-futo.svg new file mode 100644 index 0000000000..5672d0f7fe --- /dev/null +++ b/docs/static/img/logomark-dark-with-futo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/img/logomark-light-with-futo.svg b/docs/static/img/logomark-light-with-futo.svg new file mode 100644 index 0000000000..9fa1ce3605 --- /dev/null +++ b/docs/static/img/logomark-light-with-futo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/e2e/.nvmrc b/e2e/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index e05d7734ed..29a17c795b 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -34,11 +34,11 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8 + image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa database: - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 - command: -c fsync=off -c shared_preload_libraries=vectors.so + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:e6d1209c1c13791c6f9fbf726c41865e3320dfe2445a6b4ffb03e25f904b3b37 + command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres diff --git a/e2e/package-lock.json b/e2e/package-lock.json index d09a7e9701..343295c60d 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.134.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.134.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -15,9 +15,9 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@types/oidc-provider": "^8.5.1", - "@types/pg": "^8.11.0", + "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", @@ -44,7 +44,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.61", + "version": "2.2.68", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -66,7 +66,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -93,14 +93,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "typescript": "^5.3.3" } }, @@ -194,9 +194,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", "cpu": [ "ppc64" ], @@ -211,9 +211,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", "cpu": [ "arm" ], @@ -228,9 +228,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", "cpu": [ "arm64" ], @@ -245,9 +245,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", "cpu": [ "x64" ], @@ -262,9 +262,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", "cpu": [ "arm64" ], @@ -279,9 +279,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", "cpu": [ "x64" ], @@ -296,9 +296,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", "cpu": [ "arm64" ], @@ -313,9 +313,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", "cpu": [ "x64" ], @@ -330,9 +330,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", "cpu": [ "arm" ], @@ -347,9 +347,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", "cpu": [ "arm64" ], @@ -364,9 +364,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", "cpu": [ "ia32" ], @@ -381,9 +381,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", "cpu": [ "loong64" ], @@ -398,9 +398,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", "cpu": [ "mips64el" ], @@ -415,9 +415,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", "cpu": [ "ppc64" ], @@ -432,9 +432,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", "cpu": [ "riscv64" ], @@ -449,9 +449,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", "cpu": [ "s390x" ], @@ -466,9 +466,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", "cpu": [ "x64" ], @@ -483,9 +483,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", "cpu": [ "arm64" ], @@ -500,9 +500,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", "cpu": [ "x64" ], @@ -517,9 +517,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", "cpu": [ "arm64" ], @@ -534,9 +534,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", "cpu": [ "x64" ], @@ -551,9 +551,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", "cpu": [ "x64" ], @@ -568,9 +568,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", "cpu": [ "arm64" ], @@ -585,9 +585,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", "cpu": [ "ia32" ], @@ -602,9 +602,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", "cpu": [ "x64" ], @@ -619,9 +619,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -686,9 +686,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -736,13 +736,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -756,32 +759,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1012,6 +1002,19 @@ "semver": "bin/semver.js" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1050,6 +1053,16 @@ "node": ">= 8" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@photostructure/tz-lookup": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-11.2.0.tgz", @@ -1082,13 +1095,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz", - "integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.51.1" + "playwright": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -1098,9 +1111,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", "cpu": [ "arm" ], @@ -1112,9 +1125,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", "cpu": [ "arm64" ], @@ -1126,9 +1139,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", "cpu": [ "arm64" ], @@ -1140,9 +1153,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", "cpu": [ "x64" ], @@ -1154,9 +1167,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", "cpu": [ "arm64" ], @@ -1168,9 +1181,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", "cpu": [ "x64" ], @@ -1182,9 +1195,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", "cpu": [ "arm" ], @@ -1196,9 +1209,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", "cpu": [ "arm" ], @@ -1210,9 +1223,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", "cpu": [ "arm64" ], @@ -1224,9 +1237,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", "cpu": [ "arm64" ], @@ -1238,9 +1251,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", "cpu": [ "loong64" ], @@ -1252,9 +1265,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", "cpu": [ "ppc64" ], @@ -1266,9 +1279,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", "cpu": [ "riscv64" ], @@ -1280,9 +1293,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", "cpu": [ "riscv64" ], @@ -1294,9 +1307,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", "cpu": [ "s390x" ], @@ -1308,9 +1321,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", "cpu": [ "x64" ], @@ -1322,9 +1335,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", - "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", "cpu": [ "x64" ], @@ -1336,9 +1349,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", "cpu": [ "arm64" ], @@ -1350,9 +1363,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", "cpu": [ "ia32" ], @@ -1364,9 +1377,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", "cpu": [ "x64" ], @@ -1584,9 +1597,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1613,9 +1626,9 @@ } }, "node_modules/@types/pg": { - "version": "8.11.13", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.13.tgz", - "integrity": "sha512-6kXByGkvRvwXLuyaWzsebs2du6+XuAB2CuMsuzP7uaihQahshVgSmB22Pmh0vQMkQ1h5+PZU0d+Di1o+WpVWJg==", + "version": "8.15.2", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.2.tgz", + "integrity": "sha512-+BKxo5mM6+/A1soSHBI7ufUglqYXntChLDyTbvcAn1Lawi9J7J9Ok3jt6w7I0+T/UDJ4CyhHk66+GZbwmkYxSg==", "dev": true, "license": "MIT", "dependencies": { @@ -1696,21 +1709,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1725,17 +1738,27 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -1751,14 +1774,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1769,16 +1792,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1793,9 +1816,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "license": "MIT", "engines": { @@ -1807,20 +1830,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1860,16 +1883,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1884,13 +1907,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1902,9 +1925,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz", + "integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==", "dev": true, "license": "MIT", "dependencies": { @@ -1917,7 +1940,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -1925,8 +1948,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.4", + "vitest": "3.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1935,14 +1958,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", + "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1951,13 +1974,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", + "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1978,9 +2001,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", + "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", "dev": true, "license": "MIT", "dependencies": { @@ -1991,13 +2014,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", + "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.4", "pathe": "^2.0.3" }, "funding": { @@ -2005,13 +2028,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", + "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -2020,9 +2043,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", + "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -2033,13 +2056,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2915,9 +2938,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -2951,9 +2974,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2964,31 +2987,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" } }, "node_modules/escalade": { @@ -3022,20 +3045,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3083,22 +3106,25 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz", - "integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", + "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", "dev": true, "license": "MIT", "dependencies": { @@ -3536,16 +3562,19 @@ } }, "node_modules/formidable": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", - "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^2.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -3805,9 +3834,9 @@ } }, "node_modules/globals": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", - "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "dev": true, "license": "MIT", "engines": { @@ -3932,16 +3961,6 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", - "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hosted-git-info": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", @@ -5235,23 +5254,23 @@ } }, "node_modules/pg": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", - "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", "dev": true, "license": "MIT", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.8.0", - "pg-protocol": "^1.8.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "pg-connection-string": "^2.9.0", + "pg-pool": "^3.10.0", + "pg-protocol": "^1.10.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" }, "engines": { "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -5263,17 +5282,17 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", + "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", "dev": true, "license": "MIT" }, @@ -5298,9 +5317,9 @@ } }, "node_modules/pg-pool": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", - "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", + "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5308,9 +5327,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", - "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", + "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", "dev": true, "license": "MIT" }, @@ -5424,13 +5443,13 @@ } }, "node_modules/playwright": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz", - "integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.51.1" + "playwright-core": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -5443,9 +5462,9 @@ } }, "node_modules/playwright-core": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz", - "integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5878,9 +5897,9 @@ } }, "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", "dev": true, "license": "MIT", "dependencies": { @@ -5894,26 +5913,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.0", - "@rollup/rollup-android-arm64": "4.40.0", - "@rollup/rollup-darwin-arm64": "4.40.0", - "@rollup/rollup-darwin-x64": "4.40.0", - "@rollup/rollup-freebsd-arm64": "4.40.0", - "@rollup/rollup-freebsd-x64": "4.40.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", - "@rollup/rollup-linux-arm-musleabihf": "4.40.0", - "@rollup/rollup-linux-arm64-gnu": "4.40.0", - "@rollup/rollup-linux-arm64-musl": "4.40.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-musl": "4.40.0", - "@rollup/rollup-linux-s390x-gnu": "4.40.0", - "@rollup/rollup-linux-x64-gnu": "4.40.0", - "@rollup/rollup-linux-x64-musl": "4.40.0", - "@rollup/rollup-win32-arm64-msvc": "4.40.0", - "@rollup/rollup-win32-ia32-msvc": "4.40.0", - "@rollup/rollup-win32-x64-msvc": "4.40.0", + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" } }, @@ -6423,9 +6442,9 @@ } }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", "dev": true, "license": "MIT", "dependencies": { @@ -6434,7 +6453,7 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -6444,14 +6463,14 @@ } }, "node_modules/supertest": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", - "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.1" }, "engines": { "node": ">=14.18.0" @@ -6570,6 +6589,51 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -6715,15 +6779,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6852,15 +6916,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -6924,15 +6991,15 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", + "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", + "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, @@ -6946,6 +7013,21 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6961,32 +7043,46 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", + "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.4", + "@vitest/mocker": "3.1.4", + "@vitest/pretty-format": "^3.1.4", + "@vitest/runner": "3.1.4", + "@vitest/snapshot": "3.1.4", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -7002,8 +7098,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.4", + "@vitest/ui": "3.1.4", "happy-dom": "*", "jsdom": "*" }, diff --git a/e2e/package.json b/e2e/package.json index f141430c97..495b119ccf 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.131.3", + "version": "1.134.0", "description": "", "main": "index.js", "type": "module", @@ -25,9 +25,9 @@ "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@types/luxon": "^3.4.2", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@types/oidc-provider": "^8.5.1", - "@types/pg": "^8.11.0", + "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", @@ -52,6 +52,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" } } diff --git a/e2e/src/api/specs/activity.e2e-spec.ts b/e2e/src/api/specs/activity.e2e-spec.ts index ee75d6070b..70c32313f1 100644 --- a/e2e/src/api/specs/activity.e2e-spec.ts +++ b/e2e/src/api/specs/activity.e2e-spec.ts @@ -46,38 +46,6 @@ describe('/activities', () => { }); describe('GET /activities', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/activities'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require an albumId', async () => { - const { status, body } = await request(app) - .get('/activities') - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); - }); - - it('should reject an invalid albumId', async () => { - const { status, body } = await request(app) - .get('/activities') - .query({ albumId: uuidDto.invalid }) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); - }); - - it('should reject an invalid assetId', async () => { - const { status, body } = await request(app) - .get('/activities') - .query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid }) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['assetId must be a UUID']))); - }); - it('should start off empty', async () => { const { status, body } = await request(app) .get('/activities') @@ -192,30 +160,6 @@ describe('/activities', () => { }); describe('POST /activities', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/activities'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require an albumId', async () => { - const { status, body } = await request(app) - .post('/activities') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: uuidDto.invalid }); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); - }); - - it('should require a comment when type is comment', async () => { - const { status, body } = await request(app) - .post('/activities') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ albumId: uuidDto.notFound, type: 'comment', comment: null }); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(['comment must be a string', 'comment should not be empty'])); - }); - it('should add a comment to an album', async () => { const { status, body } = await request(app) .post('/activities') @@ -330,20 +274,6 @@ describe('/activities', () => { }); describe('DELETE /activities/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).delete(`/activities/${uuidDto.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid uuid', async () => { - const { status, body } = await request(app) - .delete(`/activities/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should remove a comment from an album', async () => { const reaction = await createActivity({ albumId: album.id, diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index cede49f469..65a94122fa 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -9,7 +9,7 @@ import { LoginResponseDto, SharedLinkType, } from '@immich/sdk'; -import { createUserDto, uuidDto } from 'src/fixtures'; +import { createUserDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; import { app, asBearerAuth, utils } from 'src/utils'; import request from 'supertest'; @@ -128,28 +128,6 @@ describe('/albums', () => { }); describe('GET /albums', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/albums'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should reject an invalid shared param', async () => { - const { status, body } = await request(app) - .get('/albums?shared=invalid') - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value'])); - }); - - it('should reject an invalid assetId param', async () => { - const { status, body } = await request(app) - .get('/albums?assetId=invalid') - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID'])); - }); - it("should not show other users' favorites", async () => { const { status, body } = await request(app) .get(`/albums/${user1Albums[0].id}?withoutAssets=false`) @@ -323,12 +301,6 @@ describe('/albums', () => { }); describe('GET /albums/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/albums/${user1Albums[0].id}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should return album info for own album', async () => { const { status, body } = await request(app) .get(`/albums/${user1Albums[0].id}?withoutAssets=false`) @@ -421,12 +393,6 @@ describe('/albums', () => { }); describe('GET /albums/statistics', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/albums/statistics'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should return total count of albums the user has access to', async () => { const { status, body } = await request(app) .get('/albums/statistics') @@ -438,12 +404,6 @@ describe('/albums', () => { }); describe('POST /albums', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/albums').send({ albumName: 'New album' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should create an album', async () => { const { status, body } = await request(app) .post('/albums') @@ -471,12 +431,6 @@ describe('/albums', () => { }); describe('PUT /albums/:id/assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/assets`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should be able to add own asset to own album', async () => { const asset = await utils.createAsset(user1.accessToken); const { status, body } = await request(app) @@ -526,14 +480,6 @@ describe('/albums', () => { }); describe('PATCH /albums/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .patch(`/albums/${uuidDto.notFound}`) - .send({ albumName: 'New album name' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should update an album', async () => { const album = await utils.createAlbum(user1.accessToken, { albumName: 'New album', @@ -576,15 +522,6 @@ describe('/albums', () => { }); describe('DELETE /albums/:id/assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .delete(`/albums/${user1Albums[0].id}/assets`) - .send({ ids: [user1Asset1.id] }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should require authorization', async () => { const { status, body } = await request(app) .delete(`/albums/${user1Albums[1].id}/assets`) @@ -679,13 +616,6 @@ describe('/albums', () => { }); }); - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should be able to add user to own album', async () => { const { status, body } = await request(app) .put(`/albums/${album.id}/users`) diff --git a/e2e/src/api/specs/api-key.e2e-spec.ts b/e2e/src/api/specs/api-key.e2e-spec.ts index 1748276625..ad03571869 100644 --- a/e2e/src/api/specs/api-key.e2e-spec.ts +++ b/e2e/src/api/specs/api-key.e2e-spec.ts @@ -1,5 +1,5 @@ import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk'; -import { createUserDto, uuidDto } from 'src/fixtures'; +import { createUserDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; import { app, asBearerAuth, utils } from 'src/utils'; import request from 'supertest'; @@ -24,12 +24,6 @@ describe('/api-keys', () => { }); describe('POST /api-keys', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/api-keys').send({ name: 'API Key' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should not work without permission', async () => { const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]); const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' }); @@ -99,12 +93,6 @@ describe('/api-keys', () => { }); describe('GET /api-keys', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/api-keys'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should start off empty', async () => { const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`); expect(body).toEqual([]); @@ -125,12 +113,6 @@ describe('/api-keys', () => { }); describe('GET /api-keys/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/api-keys/${uuidDto.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should require authorization', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) @@ -140,14 +122,6 @@ describe('/api-keys', () => { expect(body).toEqual(errorDto.badRequest('API Key not found')); }); - it('should require a valid uuid', async () => { - const { status, body } = await request(app) - .get(`/api-keys/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should get api key details', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) @@ -165,42 +139,30 @@ describe('/api-keys', () => { }); describe('PUT /api-keys/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/api-keys/${uuidDto.notFound}`).send({ name: 'new name' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should require authorization', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) .put(`/api-keys/${apiKey.id}`) - .send({ name: 'new name' }) + .send({ name: 'new name', permissions: [Permission.All] }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest('API Key not found')); }); - it('should require a valid uuid', async () => { - const { status, body } = await request(app) - .put(`/api-keys/${uuidDto.invalid}`) - .send({ name: 'new name' }) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should update api key details', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) .put(`/api-keys/${apiKey.id}`) - .send({ name: 'new name' }) + .send({ + name: 'new name', + permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate], + }) .set('Authorization', `Bearer ${user.accessToken}`); expect(status).toBe(200); expect(body).toEqual({ id: expect.any(String), name: 'new name', - permissions: [Permission.All], + permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate], createdAt: expect.any(String), updatedAt: expect.any(String), }); @@ -208,12 +170,6 @@ describe('/api-keys', () => { }); describe('DELETE /api-keys/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).delete(`/api-keys/${uuidDto.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should require authorization', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) @@ -223,14 +179,6 @@ describe('/api-keys', () => { expect(body).toEqual(errorDto.badRequest('API Key not found')); }); - it('should require a valid uuid', async () => { - const { status, body } = await request(app) - .delete(`/api-keys/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should delete an api key', async () => { const { apiKey } = await create(user.accessToken, [Permission.All]); const { status } = await request(app) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 01129b3299..4673db5426 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -3,6 +3,7 @@ import { AssetMediaStatus, AssetResponseDto, AssetTypeEnum, + AssetVisibility, getAssetInfo, getMyUser, LoginResponseDto, @@ -22,27 +23,9 @@ import { app, asBearerAuth, tempDir, TEN_TIMES, testAssetDir, utils } from 'src/ import request from 'supertest'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -const makeUploadDto = (options?: { omit: string }): Record => { - const dto: Record = { - deviceAssetId: 'example-image', - deviceId: 'TEST', - fileCreatedAt: new Date().toISOString(), - fileModifiedAt: new Date().toISOString(), - isFavorite: 'testing', - duration: '0:00:00.000000', - }; - - const omit = options?.omit; - if (omit) { - delete dto[omit]; - } - - return dto; -}; - const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`; const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`; -const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`; +const facesAssetDir = `${testAssetDir}/metadata/faces`; const readTags = async (bytes: Buffer, filename: string) => { const filepath = join(tempDir, filename); @@ -137,9 +120,9 @@ describe('/asset', () => { // stats utils.createAsset(statsUser.accessToken), utils.createAsset(statsUser.accessToken, { isFavorite: true }), - utils.createAsset(statsUser.accessToken, { isArchived: true }), + utils.createAsset(statsUser.accessToken, { visibility: AssetVisibility.Archive }), utils.createAsset(statsUser.accessToken, { - isArchived: true, + visibility: AssetVisibility.Archive, isFavorite: true, assetData: { filename: 'example.mp4' }, }), @@ -160,13 +143,6 @@ describe('/asset', () => { }); describe('GET /assets/:id/original', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}/original`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should download the file', async () => { const response = await request(app) .get(`/assets/${user1Assets[0].id}/original`) @@ -178,20 +154,6 @@ describe('/asset', () => { }); describe('GET /assets/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}`); - expect(body).toEqual(errorDto.unauthorized); - expect(status).toBe(401); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .get(`/assets/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should require access', async () => { const { status, body } = await request(app) .get(`/assets/${user2Assets[0].id}`) @@ -224,31 +186,22 @@ describe('/asset', () => { }); }); - it('should get the asset faces', async () => { - const config = await utils.getSystemConfig(admin.accessToken); - config.metadata.faces.import = true; - await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) }); - - // asset faces - const facesAsset = await utils.createAsset(admin.accessToken, { - assetData: { + describe('faces', () => { + const metadataFaceTests = [ + { + description: 'without orientation', filename: 'portrait.jpg', - bytes: await readFile(facesAssetFilepath), }, - }); - - await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id }); - - const { status, body } = await request(app) - .get(`/assets/${facesAsset.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(200); - expect(body.id).toEqual(facesAsset.id); - expect(body.people).toMatchObject([ + { + description: 'adjusting face regions to orientation', + filename: 'portrait-orientation-6.jpg', + }, + ]; + // should produce same resulting face region coordinates for any orientation + const expectedFaces = [ { name: 'Marie Curie', birthDate: null, - thumbnailPath: '', isHidden: false, faces: [ { @@ -265,7 +218,6 @@ describe('/asset', () => { { name: 'Pierre Curie', birthDate: null, - thumbnailPath: '', isHidden: false, faces: [ { @@ -279,7 +231,30 @@ describe('/asset', () => { }, ], }, - ]); + ]; + + it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => { + const config = await utils.getSystemConfig(admin.accessToken); + config.metadata.faces.import = true; + await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) }); + + const facesAsset = await utils.createAsset(admin.accessToken, { + assetData: { + filename, + bytes: await readFile(`${facesAssetDir}/${filename}`), + }, + }); + + await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id }); + + const { status, body } = await request(app) + .get(`/assets/${facesAsset.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body.id).toEqual(facesAsset.id); + expect(body.people).toMatchObject(expectedFaces); + }); }); it('should work with a shared link', async () => { @@ -333,7 +308,7 @@ describe('/asset', () => { }); it('disallows viewing archived assets', async () => { - const asset = await utils.createAsset(user1.accessToken, { isArchived: true }); + const asset = await utils.createAsset(user1.accessToken, { visibility: AssetVisibility.Archive }); const { status } = await request(app) .get(`/assets/${asset.id}`) @@ -354,13 +329,6 @@ describe('/asset', () => { }); describe('GET /assets/statistics', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/assets/statistics'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should return stats of all assets', async () => { const { status, body } = await request(app) .get('/assets/statistics') @@ -384,7 +352,7 @@ describe('/asset', () => { const { status, body } = await request(app) .get('/assets/statistics') .set('Authorization', `Bearer ${statsUser.accessToken}`) - .query({ isArchived: true }); + .query({ visibility: AssetVisibility.Archive }); expect(status).toBe(200); expect(body).toEqual({ images: 1, videos: 1, total: 2 }); @@ -394,7 +362,7 @@ describe('/asset', () => { const { status, body } = await request(app) .get('/assets/statistics') .set('Authorization', `Bearer ${statsUser.accessToken}`) - .query({ isFavorite: true, isArchived: true }); + .query({ isFavorite: true, visibility: AssetVisibility.Archive }); expect(status).toBe(200); expect(body).toEqual({ images: 0, videos: 1, total: 1 }); @@ -404,7 +372,7 @@ describe('/asset', () => { const { status, body } = await request(app) .get('/assets/statistics') .set('Authorization', `Bearer ${statsUser.accessToken}`) - .query({ isFavorite: false, isArchived: false }); + .query({ isFavorite: false, visibility: AssetVisibility.Timeline }); expect(status).toBe(200); expect(body).toEqual({ images: 1, videos: 0, total: 1 }); @@ -425,13 +393,6 @@ describe('/asset', () => { await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration'); }); - it('should require authentication', async () => { - const { status, body } = await request(app).get('/assets/random'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it.each(TEN_TIMES)('should return 1 random assets', async () => { const { status, body } = await request(app) .get('/assets/random') @@ -467,31 +428,9 @@ describe('/asset', () => { expect(status).toBe(200); expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]); }); - - it('should return error', async () => { - const { status } = await request(app) - .get('/assets/random?count=ABC') - .set('Authorization', `Bearer ${user1.accessToken}`); - - expect(status).toBe(400); - }); }); describe('PUT /assets/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/assets/:${uuidDto.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid id', async () => { - const { status, body } = await request(app) - .put(`/assets/${uuidDto.invalid}`) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); - }); - it('should require access', async () => { const { status, body } = await request(app) .put(`/assets/${user2Assets[0].id}`) @@ -519,7 +458,7 @@ describe('/asset', () => { const { status, body } = await request(app) .put(`/assets/${user1Assets[0].id}`) .set('Authorization', `Bearer ${user1.accessToken}`) - .send({ isArchived: true }); + .send({ visibility: AssetVisibility.Archive }); expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true }); expect(status).toEqual(200); }); @@ -619,28 +558,6 @@ describe('/asset', () => { expect(status).toEqual(200); }); - it('should reject invalid gps coordinates', async () => { - for (const test of [ - { latitude: 12 }, - { longitude: 12 }, - { latitude: 12, longitude: 'abc' }, - { latitude: 'abc', longitude: 12 }, - { latitude: null, longitude: 12 }, - { latitude: 12, longitude: null }, - { latitude: 91, longitude: 12 }, - { latitude: -91, longitude: 12 }, - { latitude: 12, longitude: -181 }, - { latitude: 12, longitude: 181 }, - ]) { - const { status, body } = await request(app) - .put(`/assets/${user1Assets[0].id}`) - .send(test) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest()); - } - }); - it('should update gps data', async () => { const { status, body } = await request(app) .put(`/assets/${user1Assets[0].id}`) @@ -712,17 +629,6 @@ describe('/asset', () => { expect(status).toEqual(200); }); - it('should reject invalid rating', async () => { - for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) { - const { status, body } = await request(app) - .put(`/assets/${user1Assets[0].id}`) - .send(test) - .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest()); - } - }); - it('should return tagged people', async () => { const { status, body } = await request(app) .put(`/assets/${user1Assets[0].id}`) @@ -746,25 +652,6 @@ describe('/asset', () => { }); describe('DELETE /assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .delete(`/assets`) - .send({ ids: [uuidDto.notFound] }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should require a valid uuid', async () => { - const { status, body } = await request(app) - .delete(`/assets`) - .send({ ids: [uuidDto.invalid] }) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID'])); - }); - it('should throw an error when the id is not found', async () => { const { status, body } = await request(app) .delete(`/assets`) @@ -877,13 +764,6 @@ describe('/asset', () => { }); describe('GET /assets/:id/thumbnail', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/assets/${locationAsset.id}/thumbnail`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should not include gps data for webp thumbnails', async () => { await utils.waitForWebsocketEvent({ event: 'assetUpload', @@ -919,13 +799,6 @@ describe('/asset', () => { }); describe('GET /assets/:id/original', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/assets/${locationAsset.id}/original`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should download the original', async () => { const { status, body, type } = await request(app) .get(`/assets/${locationAsset.id}/original`) @@ -946,43 +819,9 @@ describe('/asset', () => { }); }); - describe('PUT /assets', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put('/assets'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - }); - describe('POST /assets', () => { beforeAll(setupTests, 30_000); - it('should require authentication', async () => { - const { status, body } = await request(app).post(`/assets`); - expect(body).toEqual(errorDto.unauthorized); - expect(status).toBe(401); - }); - - it.each([ - { should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } }, - { should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } }, - { should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } }, - { should: 'require `fileModifiedAt`', dto: { ...makeUploadDto({ omit: 'fileModifiedAt' }) } }, - { should: 'require `duration`', dto: { ...makeUploadDto({ omit: 'duration' }) } }, - { should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } }, - { should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } }, - { should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } }, - ])('should $should', async ({ dto }) => { - const { status, body } = await request(app) - .post('/assets') - .set('Authorization', `Bearer ${user1.accessToken}`) - .attach('assetData', makeRandomImage(), 'example.png') - .field(dto); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest()); - }); - const tests = [ { input: 'formats/avif/8bit-sRGB.avif', @@ -1244,31 +1083,21 @@ describe('/asset', () => { }, ]; - it(`should upload and generate a thumbnail for different file types`, async () => { - // upload in parallel - const assets = await Promise.all( - tests.map(async ({ input }) => { - const filepath = join(testAssetDir, input); - return utils.createAsset(admin.accessToken, { - assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, - }); - }), - ); + it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => { + const filepath = join(testAssetDir, input); + const response = await utils.createAsset(admin.accessToken, { + assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, + }); - for (const { id, status } of assets) { - expect(status).toBe(AssetMediaStatus.Created); - // longer timeout as the thumbnail generation from full-size raw files can take a while - await utils.waitForWebsocketEvent({ event: 'assetUpload', id }); - } + expect(response.status).toBe(AssetMediaStatus.Created); + const id = response.id; + // longer timeout as the thumbnail generation from full-size raw files can take a while + await utils.waitForWebsocketEvent({ event: 'assetUpload', id }); - for (const [i, { id }] of assets.entries()) { - const { expected } = tests[i]; - const asset = await utils.getAssetInfo(admin.accessToken, id); - - expect(asset.exifInfo).toBeDefined(); - expect(asset.exifInfo).toMatchObject(expected.exifInfo); - expect(asset).toMatchObject(expected); - } + const asset = await utils.getAssetInfo(admin.accessToken, id); + expect(asset.exifInfo).toBeDefined(); + expect(asset.exifInfo).toMatchObject(expected.exifInfo); + expect(asset).toMatchObject(expected); }); it('should handle a duplicate', async () => { diff --git a/e2e/src/api/specs/audit.e2e-spec.ts b/e2e/src/api/specs/audit.e2e-spec.ts deleted file mode 100644 index c6a2adbb0a..0000000000 --- a/e2e/src/api/specs/audit.e2e-spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk'; -import { asBearerAuth, utils } from 'src/utils'; -import { beforeAll, describe, expect, it } from 'vitest'; - -describe('/audits', () => { - let admin: LoginResponseDto; - - beforeAll(async () => { - await utils.resetDatabase(); - await utils.resetFilesystem(); - - admin = await utils.adminSetup(); - }); - - // TODO: Enable these tests again once #7436 is resolved as these were flaky - describe.skip('GET :/file-report', () => { - it('excludes assets without issues from report', async () => { - const [trashedAsset, archivedAsset] = await Promise.all([ - utils.createAsset(admin.accessToken), - utils.createAsset(admin.accessToken), - utils.createAsset(admin.accessToken), - ]); - - await Promise.all([ - deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }), - updateAsset( - { - id: archivedAsset.id, - updateAssetDto: { isArchived: true }, - }, - { headers: asBearerAuth(admin.accessToken) }, - ), - ]); - - const body = await getAuditFiles({ - headers: asBearerAuth(admin.accessToken), - }); - - expect(body.orphans).toHaveLength(0); - expect(body.extras).toHaveLength(0); - }); - }); -}); diff --git a/e2e/src/api/specs/auth.e2e-spec.ts b/e2e/src/api/specs/auth.e2e-spec.ts index 1b653a781f..0f407f4ba7 100644 --- a/e2e/src/api/specs/auth.e2e-spec.ts +++ b/e2e/src/api/specs/auth.e2e-spec.ts @@ -19,17 +19,6 @@ describe(`/auth/admin-sign-up`, () => { expect(body).toEqual(signupResponseDto.admin); }); - it('should sign up the admin with a local domain', async () => { - const { status, body } = await request(app) - .post('/auth/admin-sign-up') - .send({ ...signupDto.admin, email: 'admin@local' }); - expect(status).toEqual(201); - expect(body).toEqual({ - ...signupResponseDto.admin, - email: 'admin@local', - }); - }); - it('should not allow a second admin to sign up', async () => { await signUpAdmin({ signUpDto: signupDto.admin }); @@ -57,22 +46,6 @@ describe('/auth/*', () => { expect(body).toEqual(errorDto.incorrectLogin); }); - for (const key of Object.keys(loginDto.admin)) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(app) - .post('/auth/login') - .send({ ...loginDto.admin, [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest()); - }); - - it('should reject an invalid email', async () => { - const { status, body } = await request(app).post('/auth/login').send({ email: [], password }); - expect(status).toBe(400); - expect(body).toEqual(errorDto.invalidEmail); - }); - } - it('should accept a correct password', async () => { const { status, body, headers } = await request(app).post('/auth/login').send({ email, password }); expect(status).toBe(201); @@ -127,14 +100,6 @@ describe('/auth/*', () => { }); describe('POST /auth/change-password', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .post(`/auth/change-password`) - .send({ password, newPassword: 'Password1234' }); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should require the current password', async () => { const { status, body } = await request(app) .post(`/auth/change-password`) diff --git a/e2e/src/api/specs/download.e2e-spec.ts b/e2e/src/api/specs/download.e2e-spec.ts index 3d3e6c7650..4dcb6934af 100644 --- a/e2e/src/api/specs/download.e2e-spec.ts +++ b/e2e/src/api/specs/download.e2e-spec.ts @@ -1,6 +1,5 @@ import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk'; import { readFile, writeFile } from 'node:fs/promises'; -import { errorDto } from 'src/responses'; import { app, tempDir, utils } from 'src/utils'; import request from 'supertest'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -17,15 +16,6 @@ describe('/download', () => { }); describe('POST /download/info', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .post(`/download/info`) - .send({ assetIds: [asset1.id] }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should download info', async () => { const { status, body } = await request(app) .post('/download/info') @@ -42,15 +32,6 @@ describe('/download', () => { }); describe('POST /download/archive', () => { - it('should require authentication', async () => { - const { status, body } = await request(app) - .post(`/download/archive`) - .send({ assetIds: [asset1.id, asset2.id] }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should download an archive', async () => { const { status, body } = await request(app) .post('/download/archive') diff --git a/e2e/src/api/specs/map.e2e-spec.ts b/e2e/src/api/specs/map.e2e-spec.ts index da5f779cff..977638aa24 100644 --- a/e2e/src/api/specs/map.e2e-spec.ts +++ b/e2e/src/api/specs/map.e2e-spec.ts @@ -1,4 +1,4 @@ -import { LoginResponseDto } from '@immich/sdk'; +import { AssetVisibility, LoginResponseDto } from '@immich/sdk'; import { readFile } from 'node:fs/promises'; import { basename, join } from 'node:path'; import { Socket } from 'socket.io-client'; @@ -44,7 +44,7 @@ describe('/map', () => { it('should get map markers for all non-archived assets', async () => { const { status, body } = await request(app) .get('/map/markers') - .query({ isArchived: false }) + .query({ visibility: AssetVisibility.Timeline }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(status).toBe(200); diff --git a/e2e/src/api/specs/oauth.e2e-spec.ts b/e2e/src/api/specs/oauth.e2e-spec.ts index 9cd5f0252a..9e4d64892e 100644 --- a/e2e/src/api/specs/oauth.e2e-spec.ts +++ b/e2e/src/api/specs/oauth.e2e-spec.ts @@ -6,6 +6,7 @@ import { startOAuth, updateConfig, } from '@immich/sdk'; +import { createHash, randomBytes } from 'node:crypto'; import { errorDto } from 'src/responses'; import { OAuthClient, OAuthUser } from 'src/setup/auth-server'; import { app, asBearerAuth, baseUrl, utils } from 'src/utils'; @@ -21,18 +22,30 @@ const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redire const redirect = async (url: string, cookies?: string[]) => { const { headers } = await request(url) - .get('/') + .get('') .set('Cookie', cookies || []); return { cookies: (headers['set-cookie'] as unknown as string[]) || [], location: headers.location }; }; +// Function to generate a code challenge from the verifier +const generateCodeChallenge = async (codeVerifier: string): Promise => { + const hashed = createHash('sha256').update(codeVerifier).digest(); + return hashed.toString('base64url'); +}; + const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => { - const { url } = await startOAuth({ oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login` } }); + const state = randomBytes(16).toString('base64url'); + const codeVerifier = randomBytes(64).toString('base64url'); + const codeChallenge = await generateCodeChallenge(codeVerifier); + + const { url } = await startOAuth({ + oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login`, state, codeChallenge }, + }); // login const response1 = await redirect(url.replace(authServer.internal, authServer.external)); const response2 = await request(authServer.external + response1.location) - .post('/') + .post('') .set('Cookie', response1.cookies) .type('form') .send({ prompt: 'login', login: sub, password: 'password' }); @@ -40,7 +53,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => // approve const response3 = await redirect(response2.header.location, response1.cookies); const response4 = await request(authServer.external + response3.location) - .post('/') + .post('') .type('form') .set('Cookie', response3.cookies) .send({ prompt: 'consent' }); @@ -51,9 +64,9 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => expect(redirectUrl).toBeDefined(); const params = new URL(redirectUrl).searchParams; expect(params.get('code')).toBeDefined(); - expect(params.get('state')).toBeDefined(); + expect(params.get('state')).toBe(state); - return redirectUrl; + return { url: redirectUrl, state, codeVerifier }; }; const setupOAuth = async (token: string, dto: Partial) => { @@ -119,9 +132,42 @@ describe(`/oauth`, () => { expect(body).toEqual(errorDto.badRequest(['url should not be empty'])); }); - it('should auto register the user by default', async () => { - const url = await loginWithOAuth('oauth-auto-register'); + it(`should throw an error if the state is not provided`, async () => { + const { url } = await loginWithOAuth('oauth-auto-register'); const { status, body } = await request(app).post('/oauth/callback').send({ url }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('OAuth state is missing')); + }); + + it(`should throw an error if the state mismatches`, async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { state } = await loginWithOAuth('oauth-auto-register'); + const { status } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, state }); + expect(status).toBeGreaterThanOrEqual(400); + }); + + it(`should throw an error if the codeVerifier is not provided`, async () => { + const { url, state } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send({ url, state }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest('OAuth code verifier is missing')); + }); + + it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { codeVerifier } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, codeVerifier }); + console.log(body); + expect(status).toBeGreaterThanOrEqual(400); + }); + + it('should auto register the user by default', async () => { + const callbackParams = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -132,16 +178,30 @@ describe(`/oauth`, () => { }); }); + it('should allow passing state and codeVerifier via cookies', async () => { + const { url, state, codeVerifier } = await loginWithOAuth('oauth-auto-register'); + const { status, body } = await request(app) + .post('/oauth/callback') + .set('Cookie', [`immich_oauth_state=${state}`, `immich_oauth_code_verifier=${codeVerifier}`]) + .send({ url }); + expect(status).toBe(201); + expect(body).toMatchObject({ + accessToken: expect.any(String), + userId: expect.any(String), + userEmail: 'oauth-auto-register@immich.app', + }); + }); + it('should handle a user without an email', async () => { - const url = await loginWithOAuth(OAuthUser.NO_EMAIL); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.NO_EMAIL); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest('OAuth profile does not have an email address')); }); it('should set the quota from a claim', async () => { - const url = await loginWithOAuth(OAuthUser.WITH_QUOTA); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.WITH_QUOTA); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -154,8 +214,8 @@ describe(`/oauth`, () => { }); it('should set the storage label from a claim', async () => { - const url = await loginWithOAuth(OAuthUser.WITH_USERNAME); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth(OAuthUser.WITH_USERNAME); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -176,8 +236,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', signingAlgorithm: 'RS256', }); - const url = await loginWithOAuth('oauth-RS256-token'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-RS256-token'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), @@ -196,8 +256,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', profileSigningAlgorithm: 'RS256', }); - const url = await loginWithOAuth('oauth-signed-profile'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-signed-profile'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ userId: expect.any(String), @@ -213,8 +273,8 @@ describe(`/oauth`, () => { buttonText: 'Login with Immich', signingAlgorithm: 'something-that-does-not-work', }); - const url = await loginWithOAuth('oauth-signed-bad'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-signed-bad'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(500); expect(body).toMatchObject({ error: 'Internal Server Error', @@ -235,8 +295,8 @@ describe(`/oauth`, () => { }); it('should not auto register the user', async () => { - const url = await loginWithOAuth('oauth-no-auto-register'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-no-auto-register'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.')); }); @@ -247,8 +307,8 @@ describe(`/oauth`, () => { email: 'oauth-user3@immich.app', password: 'password', }); - const url = await loginWithOAuth('oauth-user3'); - const { status, body } = await request(app).post('/oauth/callback').send({ url }); + const callbackParams = await loginWithOAuth('oauth-user3'); + const { status, body } = await request(app).post('/oauth/callback').send(callbackParams); expect(status).toBe(201); expect(body).toMatchObject({ userId, @@ -286,13 +346,15 @@ describe(`/oauth`, () => { }); it('should auto register the user by default', async () => { - const url = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback'); - expect(url).toEqual(expect.stringContaining(mobileOverrideRedirectUri)); + const callbackParams = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback'); + expect(callbackParams.url).toEqual(expect.stringContaining(mobileOverrideRedirectUri)); // simulate redirecting back to mobile app - const redirectUri = url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback'); + const url = callbackParams.url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback'); - const { status, body } = await request(app).post('/oauth/callback').send({ url: redirectUri }); + const { status, body } = await request(app) + .post('/oauth/callback') + .send({ ...callbackParams, url }); expect(status).toBe(201); expect(body).toMatchObject({ accessToken: expect.any(String), diff --git a/e2e/src/api/specs/person.e2e-spec.ts b/e2e/src/api/specs/person.e2e-spec.ts index 6e7eba74ba..1826002af6 100644 --- a/e2e/src/api/specs/person.e2e-spec.ts +++ b/e2e/src/api/specs/person.e2e-spec.ts @@ -5,22 +5,6 @@ import { app, asBearerAuth, utils } from 'src/utils'; import request from 'supertest'; import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; -const invalidBirthday = [ - { - birthDate: 'false', - response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'], - }, - { - birthDate: '123567', - response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'], - }, - { - birthDate: 123_567, - response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'], - }, - { birthDate: '9999-01-01', response: ['Birth date cannot be in the future'] }, -]; - describe('/people', () => { let admin: LoginResponseDto; let visiblePerson: PersonResponseDto; @@ -58,14 +42,6 @@ describe('/people', () => { describe('GET /people', () => { beforeEach(async () => {}); - - it('should require authentication', async () => { - const { status, body } = await request(app).get('/people'); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should return all people (including hidden)', async () => { const { status, body } = await request(app) .get('/people') @@ -117,13 +93,6 @@ describe('/people', () => { }); describe('GET /people/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/people/${uuidDto.notFound}`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should throw error if person with id does not exist', async () => { const { status, body } = await request(app) .get(`/people/${uuidDto.notFound}`) @@ -144,13 +113,6 @@ describe('/people', () => { }); describe('GET /people/:id/statistics', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get(`/people/${multipleAssetsPerson.id}/statistics`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should throw error if person with id does not exist', async () => { const { status, body } = await request(app) .get(`/people/${uuidDto.notFound}/statistics`) @@ -171,23 +133,6 @@ describe('/people', () => { }); describe('POST /people', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post(`/people`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - for (const { birthDate, response } of invalidBirthday) { - it(`should not accept an invalid birth date [${birthDate}]`, async () => { - const { status, body } = await request(app) - .post(`/people`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ birthDate }); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(response)); - }); - } - it('should create a person', async () => { const { status, body } = await request(app) .post(`/people`) @@ -223,39 +168,6 @@ describe('/people', () => { }); describe('PUT /people/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).put(`/people/${uuidDto.notFound}`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - for (const { key, type } of [ - { key: 'name', type: 'string' }, - { key: 'featureFaceAssetId', type: 'string' }, - { key: 'isHidden', type: 'boolean value' }, - { key: 'isFavorite', type: 'boolean value' }, - ]) { - it(`should not allow null ${key}`, async () => { - const { status, body } = await request(app) - .put(`/people/${visiblePerson.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ [key]: null }); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest([`${key} must be a ${type}`])); - }); - } - - for (const { birthDate, response } of invalidBirthday) { - it(`should not accept an invalid birth date [${birthDate}]`, async () => { - const { status, body } = await request(app) - .put(`/people/${visiblePerson.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ birthDate }); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(response)); - }); - } - it('should update a date of birth', async () => { const { status, body } = await request(app) .put(`/people/${visiblePerson.id}`) @@ -312,12 +224,6 @@ describe('/people', () => { }); describe('POST /people/:id/merge', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post(`/people/${uuidDto.notFound}/merge`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should not supporting merging a person into themselves', async () => { const { status, body } = await request(app) .post(`/people/${visiblePerson.id}/merge`) diff --git a/e2e/src/api/specs/search.e2e-spec.ts b/e2e/src/api/specs/search.e2e-spec.ts index 1031390ee9..2f6ea75f77 100644 --- a/e2e/src/api/specs/search.e2e-spec.ts +++ b/e2e/src/api/specs/search.e2e-spec.ts @@ -1,9 +1,15 @@ -import { AssetMediaResponseDto, AssetResponseDto, deleteAssets, LoginResponseDto, updateAsset } from '@immich/sdk'; +import { + AssetMediaResponseDto, + AssetResponseDto, + AssetVisibility, + deleteAssets, + LoginResponseDto, + updateAsset, +} from '@immich/sdk'; import { DateTime } from 'luxon'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { Socket } from 'socket.io-client'; -import { errorDto } from 'src/responses'; import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils'; import request from 'supertest'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; @@ -50,7 +56,7 @@ describe('/search', () => { { filename: '/formats/motionphoto/samsung-one-ui-6.heic' }, { filename: '/formats/motionphoto/samsung-one-ui-5.jpg' }, - { filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } }, + { filename: '/metadata/gps-position/thompson-springs.jpg', dto: { visibility: AssetVisibility.Archive } }, // used for search suggestions { filename: '/formats/png/density_plot.png' }, @@ -141,65 +147,6 @@ describe('/search', () => { }); describe('POST /search/metadata', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/search/metadata'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - const badTests = [ - { - should: 'should reject page as a string', - dto: { page: 'abc' }, - expected: ['page must not be less than 1', 'page must be an integer number'], - }, - { - should: 'should reject page as a decimal', - dto: { page: 1.5 }, - expected: ['page must be an integer number'], - }, - { - should: 'should reject page as a negative number', - dto: { page: -10 }, - expected: ['page must not be less than 1'], - }, - { - should: 'should reject page as 0', - dto: { page: 0 }, - expected: ['page must not be less than 1'], - }, - { - should: 'should reject size as a string', - dto: { size: 'abc' }, - expected: [ - 'size must not be greater than 1000', - 'size must not be less than 1', - 'size must be an integer number', - ], - }, - { - should: 'should reject an invalid size', - dto: { size: -1.5 }, - expected: ['size must not be less than 1', 'size must be an integer number'], - }, - ...['isArchived', 'isFavorite', 'isEncoded', 'isOffline', 'isMotion', 'isVisible'].map((value) => ({ - should: `should reject ${value} not a boolean`, - dto: { [value]: 'immich' }, - expected: [`${value} must be a boolean value`], - })), - ]; - - for (const { should, dto, expected } of badTests) { - it(should, async () => { - const { status, body } = await request(app) - .post('/search/metadata') - .set('Authorization', `Bearer ${admin.accessToken}`) - .send(dto); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(expected)); - }); - } - const searchTests = [ { should: 'should get my assets', @@ -231,12 +178,12 @@ describe('/search', () => { deferred: () => ({ dto: { size: 1, isFavorite: false }, assets: [assetLast] }), }, { - should: 'should search by isArchived (true)', - deferred: () => ({ dto: { isArchived: true }, assets: [assetSprings] }), + should: 'should search by visibility (AssetVisibility.Archive)', + deferred: () => ({ dto: { visibility: AssetVisibility.Archive }, assets: [assetSprings] }), }, { - should: 'should search by isArchived (false)', - deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }), + should: 'should search by visibility (AssetVisibility.Timeline)', + deferred: () => ({ dto: { size: 1, visibility: AssetVisibility.Timeline }, assets: [assetLast] }), }, { should: 'should search by type (image)', @@ -245,7 +192,7 @@ describe('/search', () => { { should: 'should search by type (video)', deferred: () => ({ - dto: { type: 'VIDEO' }, + dto: { type: 'VIDEO', visibility: AssetVisibility.Hidden }, assets: [ // the three live motion photos { id: expect.any(String) }, @@ -289,13 +236,6 @@ describe('/search', () => { should: 'should search by takenAfter (no results)', deferred: () => ({ dto: { takenAfter: today.plus({ hour: 1 }).toJSDate() }, assets: [] }), }, - // { - // should: 'should search by originalPath', - // deferred: () => ({ - // dto: { originalPath: asset1.originalPath }, - // assets: [asset1], - // }), - // }, { should: 'should search by originalFilename', deferred: () => ({ @@ -325,7 +265,7 @@ describe('/search', () => { deferred: () => ({ dto: { city: '', - isVisible: true, + visibility: AssetVisibility.Timeline, includeNull: true, }, assets: [assetLast], @@ -336,7 +276,7 @@ describe('/search', () => { deferred: () => ({ dto: { city: null, - isVisible: true, + visibility: AssetVisibility.Timeline, includeNull: true, }, assets: [assetLast], @@ -357,7 +297,7 @@ describe('/search', () => { deferred: () => ({ dto: { state: '', - isVisible: true, + visibility: AssetVisibility.Timeline, withExif: true, includeNull: true, }, @@ -369,7 +309,7 @@ describe('/search', () => { deferred: () => ({ dto: { state: null, - isVisible: true, + visibility: AssetVisibility.Timeline, includeNull: true, }, assets: [assetLast, assetNotocactus], @@ -390,7 +330,7 @@ describe('/search', () => { deferred: () => ({ dto: { country: '', - isVisible: true, + visibility: AssetVisibility.Timeline, includeNull: true, }, assets: [assetLast], @@ -401,7 +341,7 @@ describe('/search', () => { deferred: () => ({ dto: { country: null, - isVisible: true, + visibility: AssetVisibility.Timeline, includeNull: true, }, assets: [assetLast], @@ -454,14 +394,6 @@ describe('/search', () => { } }); - describe('POST /search/smart', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).post('/search/smart'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - }); - describe('POST /search/random', () => { beforeAll(async () => { await Promise.all([ @@ -476,13 +408,6 @@ describe('/search', () => { await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration'); }); - it('should require authentication', async () => { - const { status, body } = await request(app).post('/search/random').send({ size: 1 }); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it.each(TEN_TIMES)('should return 1 random assets', async () => { const { status, body } = await request(app) .post('/search/random') @@ -512,12 +437,6 @@ describe('/search', () => { }); describe('GET /search/explore', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/search/explore'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should get explore data', async () => { const { status, body } = await request(app) .get('/search/explore') @@ -528,12 +447,6 @@ describe('/search', () => { }); describe('GET /search/places', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/search/places'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should get relevant places', async () => { const name = 'Paris'; @@ -552,12 +465,6 @@ describe('/search', () => { }); describe('GET /search/cities', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/search/cities'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should get all cities', async () => { const { status, body } = await request(app) .get('/search/cities') @@ -576,12 +483,6 @@ describe('/search', () => { }); describe('GET /search/suggestions', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).get('/search/suggestions'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - it('should get suggestions for country (including null)', async () => { const { status, body } = await request(app) .get('/search/suggestions?type=country&includeNull=true') diff --git a/e2e/src/api/specs/timeline.e2e-spec.ts b/e2e/src/api/specs/timeline.e2e-spec.ts index bf330e994a..5db184bf76 100644 --- a/e2e/src/api/specs/timeline.e2e-spec.ts +++ b/e2e/src/api/specs/timeline.e2e-spec.ts @@ -1,4 +1,10 @@ -import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk'; +import { + AssetMediaResponseDto, + AssetVisibility, + LoginResponseDto, + SharedLinkType, + TimeBucketAssetResponseDto, +} from '@immich/sdk'; import { DateTime } from 'luxon'; import { createUserDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; @@ -19,7 +25,8 @@ describe('/timeline', () => { let user: LoginResponseDto; let timeBucketUser: LoginResponseDto; - let userAssets: AssetMediaResponseDto[]; + let user1Assets: AssetMediaResponseDto[]; + let user2Assets: AssetMediaResponseDto[]; beforeAll(async () => { await utils.resetDatabase(); @@ -29,7 +36,7 @@ describe('/timeline', () => { utils.userSetup(admin.accessToken, createUserDto.create('time-bucket')), ]); - userAssets = await Promise.all([ + user1Assets = await Promise.all([ utils.createAsset(user.accessToken), utils.createAsset(user.accessToken), utils.createAsset(user.accessToken, { @@ -42,17 +49,20 @@ describe('/timeline', () => { utils.createAsset(user.accessToken), ]); - await Promise.all([ + user2Assets = await Promise.all([ utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }), utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-10').toISOString() }), utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }), utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-11').toISOString() }), + utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-02-12').toISOString() }), ]); + + await utils.deleteAssets(timeBucketUser.accessToken, [user2Assets[4].id]); }); describe('GET /timeline/buckets', () => { it('should require authentication', async () => { - const { status, body } = await request(app).get('/timeline/buckets').query({ size: TimeBucketSize.Month }); + const { status, body } = await request(app).get('/timeline/buckets'); expect(status).toBe(401); expect(body).toEqual(errorDto.unauthorized); }); @@ -60,8 +70,7 @@ describe('/timeline', () => { it('should get time buckets by month', async () => { const { status, body } = await request(app) .get('/timeline/buckets') - .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Month }); + .set('Authorization', `Bearer ${timeBucketUser.accessToken}`); expect(status).toBe(200); expect(body).toEqual( @@ -75,36 +84,20 @@ describe('/timeline', () => { it('should not allow access for unrelated shared links', async () => { const sharedLink = await utils.createSharedLink(user.accessToken, { type: SharedLinkType.Individual, - assetIds: userAssets.map(({ id }) => id), + assetIds: user1Assets.map(({ id }) => id), }); - const { status, body } = await request(app) - .get('/timeline/buckets') - .query({ key: sharedLink.key, size: TimeBucketSize.Month }); + const { status, body } = await request(app).get('/timeline/buckets').query({ key: sharedLink.key }); expect(status).toBe(400); expect(body).toEqual(errorDto.noPermission); }); - it('should get time buckets by day', async () => { - const { status, body } = await request(app) - .get('/timeline/buckets') - .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Day }); - - expect(status).toBe(200); - expect(body).toEqual([ - { count: 2, timeBucket: '1970-02-11T00:00:00.000Z' }, - { count: 1, timeBucket: '1970-02-10T00:00:00.000Z' }, - { count: 1, timeBucket: '1970-01-01T00:00:00.000Z' }, - ]); - }); - it('should return error if time bucket is requested with partners asset and archived', async () => { const req1 = await request(app) .get('/timeline/buckets') .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Month, withPartners: true, isArchived: true }); + .query({ withPartners: true, visibility: AssetVisibility.Archive }); expect(req1.status).toBe(400); expect(req1.body).toEqual(errorDto.badRequest()); @@ -112,7 +105,7 @@ describe('/timeline', () => { const req2 = await request(app) .get('/timeline/buckets') .set('Authorization', `Bearer ${user.accessToken}`) - .query({ size: TimeBucketSize.Month, withPartners: true, isArchived: undefined }); + .query({ withPartners: true, visibility: undefined }); expect(req2.status).toBe(400); expect(req2.body).toEqual(errorDto.badRequest()); @@ -122,7 +115,7 @@ describe('/timeline', () => { const req1 = await request(app) .get('/timeline/buckets') .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: true }); + .query({ withPartners: true, isFavorite: true }); expect(req1.status).toBe(400); expect(req1.body).toEqual(errorDto.badRequest()); @@ -130,7 +123,7 @@ describe('/timeline', () => { const req2 = await request(app) .get('/timeline/buckets') .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: false }); + .query({ withPartners: true, isFavorite: false }); expect(req2.status).toBe(400); expect(req2.body).toEqual(errorDto.badRequest()); @@ -140,7 +133,7 @@ describe('/timeline', () => { const req = await request(app) .get('/timeline/buckets') .set('Authorization', `Bearer ${user.accessToken}`) - .query({ size: TimeBucketSize.Month, withPartners: true, isTrashed: true }); + .query({ withPartners: true, isTrashed: true }); expect(req.status).toBe(400); expect(req.body).toEqual(errorDto.badRequest()); @@ -150,7 +143,6 @@ describe('/timeline', () => { describe('GET /timeline/bucket', () => { it('should require authentication', async () => { const { status, body } = await request(app).get('/timeline/bucket').query({ - size: TimeBucketSize.Month, timeBucket: '1900-01-01', }); @@ -161,11 +153,27 @@ describe('/timeline', () => { it('should handle 5 digit years', async () => { const { status, body } = await request(app) .get('/timeline/bucket') - .query({ size: TimeBucketSize.Month, timeBucket: '012345-01-01' }) + .query({ timeBucket: '012345-01-01' }) .set('Authorization', `Bearer ${timeBucketUser.accessToken}`); expect(status).toBe(200); - expect(body).toEqual([]); + expect(body).toEqual({ + city: [], + country: [], + duration: [], + id: [], + visibility: [], + isFavorite: [], + isImage: [], + isTrashed: [], + livePhotoVideoId: [], + localDateTime: [], + ownerId: [], + projectionType: [], + ratio: [], + status: [], + thumbhash: [], + }); }); // TODO enable date string validation while still accepting 5 digit years @@ -173,7 +181,7 @@ describe('/timeline', () => { // const { status, body } = await request(app) // .get('/timeline/bucket') // .set('Authorization', `Bearer ${user.accessToken}`) - // .query({ size: TimeBucketSize.Month, timeBucket: 'foo' }); + // .query({ timeBucket: 'foo' }); // expect(status).toBe(400); // expect(body).toEqual(errorDto.badRequest); @@ -183,10 +191,38 @@ describe('/timeline', () => { const { status, body } = await request(app) .get('/timeline/bucket') .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) - .query({ size: TimeBucketSize.Month, timeBucket: '1970-02-10' }); + .query({ timeBucket: '1970-02-10' }); expect(status).toBe(200); - expect(body).toEqual([]); + expect(body).toEqual({ + city: [], + country: [], + duration: [], + id: [], + visibility: [], + isFavorite: [], + isImage: [], + isTrashed: [], + livePhotoVideoId: [], + localDateTime: [], + ownerId: [], + projectionType: [], + ratio: [], + status: [], + thumbhash: [], + }); + }); + + it('should return time bucket in trash', async () => { + const { status, body } = await request(app) + .get('/timeline/bucket') + .set('Authorization', `Bearer ${timeBucketUser.accessToken}`) + .query({ timeBucket: '1970-02-01T00:00:00.000Z', isTrashed: true }); + + expect(status).toBe(200); + + const timeBucket: TimeBucketAssetResponseDto = body; + expect(timeBucket.isTrashed).toEqual([true]); }); }); }); diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts index 9299e62b79..1fbee84c3f 100644 --- a/e2e/src/api/specs/user-admin.e2e-spec.ts +++ b/e2e/src/api/specs/user-admin.e2e-spec.ts @@ -215,6 +215,19 @@ describe('/admin/users', () => { const user = await getMyUser({ headers: asBearerAuth(token.accessToken) }); expect(user).toMatchObject({ email: nonAdmin.userEmail }); }); + + it('should update the avatar color', async () => { + const { status, body } = await request(app) + .put(`/admin/users/${admin.userId}`) + .send({ avatarColor: 'orange' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ avatarColor: 'orange' }); + + const after = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); + expect(after).toMatchObject({ avatarColor: 'orange' }); + }); }); describe('PUT /admin/users/:id/preferences', () => { @@ -240,19 +253,6 @@ describe('/admin/users', () => { expect(after).toMatchObject({ memories: { enabled: false } }); }); - it('should update the avatar color', async () => { - const { status, body } = await request(app) - .put(`/admin/users/${admin.userId}/preferences`) - .send({ avatar: { color: 'orange' } }) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toMatchObject({ avatar: { color: 'orange' } }); - - const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); - expect(after).toMatchObject({ avatar: { color: 'orange' } }); - }); - it('should update download archive size', async () => { const { status, body } = await request(app) .put(`/admin/users/${admin.userId}/preferences`) diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts index 54d11e5049..b9eb140c56 100644 --- a/e2e/src/api/specs/user.e2e-spec.ts +++ b/e2e/src/api/specs/user.e2e-spec.ts @@ -139,6 +139,19 @@ describe('/users', () => { profileChangedAt: expect.anything(), }); }); + + it('should update avatar color', async () => { + const { status, body } = await request(app) + .put(`/users/me`) + .send({ avatarColor: 'blue' }) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(200); + expect(body).toMatchObject({ avatarColor: 'blue' }); + + const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) }); + expect(after).toMatchObject({ avatarColor: 'blue' }); + }); }); describe('PUT /users/me/preferences', () => { @@ -158,19 +171,6 @@ describe('/users', () => { expect(after).toMatchObject({ memories: { enabled: false } }); }); - it('should update avatar color', async () => { - const { status, body } = await request(app) - .put(`/users/me/preferences`) - .send({ avatar: { color: 'blue' } }) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(200); - expect(body).toMatchObject({ avatar: { color: 'blue' } }); - - const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) }); - expect(after).toMatchObject({ avatar: { color: 'blue' } }); - }); - it('should require an integer for download archive size', async () => { const { status, body } = await request(app) .put(`/users/me/preferences`) diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 08b29a4a11..1d5004d385 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -3,6 +3,7 @@ import { AssetMediaCreateDto, AssetMediaResponseDto, AssetResponseDto, + AssetVisibility, CheckExistingAssetsDto, CreateAlbumDto, CreateLibraryDto, @@ -429,7 +430,10 @@ export const utils = { }, archiveAssets: (accessToken: string, ids: string[]) => - updateAssets({ assetBulkUpdateDto: { ids, isArchived: true } }, { headers: asBearerAuth(accessToken) }), + updateAssets( + { assetBulkUpdateDto: { ids, visibility: AssetVisibility.Archive } }, + { headers: asBearerAuth(accessToken) }, + ), deleteAssets: (accessToken: string, ids: string[]) => deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }), diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index e89f17a4e9..74bee64e0a 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -25,7 +25,7 @@ test.describe('Registration', () => { // login await expect(page).toHaveTitle(/Login/); - await page.goto('/auth/login'); + await page.goto('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('admin@immich.app'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Login' }).click(); @@ -59,7 +59,7 @@ test.describe('Registration', () => { await context.clearCookies(); // login - await page.goto('/auth/login'); + await page.goto('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('user@immich.cloud'); await page.getByLabel('Password').fill('password'); await page.getByRole('button', { name: 'Login' }).click(); @@ -72,7 +72,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Change password' }).click(); // login with new password - await expect(page).toHaveURL('/auth/login'); + await expect(page).toHaveURL('/auth/login?autoLaunch=0'); await page.getByLabel('Email').fill('user@immich.cloud'); await page.getByLabel('Password').fill('new-password'); await page.getByRole('button', { name: 'Login' }).click(); diff --git a/e2e/src/web/specs/photo-viewer.e2e-spec.ts b/e2e/src/web/specs/photo-viewer.e2e-spec.ts index 4871e7522c..c8a9b42b2a 100644 --- a/e2e/src/web/specs/photo-viewer.e2e-spec.ts +++ b/e2e/src/web/specs/photo-viewer.e2e-spec.ts @@ -21,23 +21,9 @@ test.describe('Photo Viewer', () => { test.beforeEach(async ({ context, page }) => { // before each test, login as user await utils.setAuthCookies(context, admin.accessToken); - await page.goto('/photos'); await page.waitForLoadState('networkidle'); }); - test('initially shows a loading spinner', async ({ page }) => { - await page.route(`/api/assets/${asset.id}/thumbnail**`, async (route) => { - // slow down the request for thumbnail, so spinner has chance to show up - await new Promise((f) => setTimeout(f, 2000)); - await route.continue(); - }); - await page.goto(`/photos/${asset.id}`); - await page.waitForLoadState('load'); - // this is the spinner - await page.waitForSelector('svg[role=status]'); - await expect(page.getByTestId('loading-spinner')).toBeVisible(); - }); - test('loads original photo when zoomed', async ({ page }) => { await page.goto(`/photos/${asset.id}`); await expect.poll(async () => await imageLocator(page).getAttribute('src')).toContain('thumbnail'); diff --git a/e2e/src/web/specs/shared-link.e2e-spec.ts b/e2e/src/web/specs/shared-link.e2e-spec.ts index 562a0b4e8c..017bc0fcb2 100644 --- a/e2e/src/web/specs/shared-link.e2e-spec.ts +++ b/e2e/src/web/specs/shared-link.e2e-spec.ts @@ -47,16 +47,13 @@ test.describe('Shared Links', () => { await page.locator(`[data-asset-id="${asset.id}"]`).hover(); await page.waitForSelector('[data-group] svg'); await page.getByRole('checkbox').click(); - await page.getByRole('button', { name: 'Download' }).click(); - await page.waitForEvent('download'); + await Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]); }); test('download all from shared link', async ({ page }) => { await page.goto(`/share/${sharedLink.key}`); await page.getByRole('heading', { name: 'Test Album' }).waitFor(); - await page.getByRole('button', { name: 'Download' }).click(); - await page.getByText('DOWNLOADING', { exact: true }).waitFor(); - await page.waitForEvent('download'); + await Promise.all([page.waitForEvent('download'), page.getByRole('button', { name: 'Download' }).click()]); }); test('enter password for a shared link', async ({ page }) => { diff --git a/e2e/test-assets b/e2e/test-assets index 9e3b964b08..8885d6d01c 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 9e3b964b080dca6f035b29b86e66454ae8aeda78 +Subproject commit 8885d6d01c12242785b6ea68f4a277334f60bc90 diff --git a/i18n/ar.json b/i18n/ar.json index d6e26985cb..5dedbd2ea5 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -14,7 +14,6 @@ "add_a_location": "ØĨØļØ§ŲØŠ Ų…ŲˆŲ‚Øš", "add_a_name": "ØĨØļØ§ŲØŠ ØĨØŗŲ…", "add_a_title": "ØĨØļØ§ŲØŠ ØšŲ†ŲˆØ§Ų†", - "add_endpoint": "Add endpoint", "add_exclusion_pattern": "ØĨØļØ§ŲØŠ Ų†Ų…Øˇ ØĨØŗØĒØĢŲ†Ø§ØĄ", "add_import_path": "ØĨØļØ§ŲØŠ Ų…ØŗØ§Øą Ø§Ų„ØĨØŗØĒŲŠØąØ§Ø¯", "add_location": "ØĨØļØ§ŲØŠ Ų…ŲˆŲ‚Øš", @@ -187,20 +186,13 @@ "oauth_auto_register": "Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊", "oauth_auto_register_description": "Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„ØŦدد بؚد ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… OAuth", "oauth_button_text": "Ų†Øĩ Ø§Ų„Ø˛Øą", - "oauth_client_id": "Ų…ØšØąŲ Ø§Ų„ØšŲ…ŲŠŲ„", - "oauth_client_secret": "Ø§Ų„ØąŲ…Ø˛ Ø§Ų„ØŗØąŲŠ Ų„Ų„ØšŲ…ŲŠŲ„", "oauth_enable_description": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… OAuth", - "oauth_issuer_url": "ØšŲ†ŲˆØ§Ų† URL Ø§Ų„ØŽØ§Øĩ بØŦŲ‡ØŠ Ø§Ų„ØĨØĩØ¯Ø§Øą", "oauth_mobile_redirect_uri": "ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", "oauth_mobile_redirect_uri_override": "ØĒØŦØ§ŲˆØ˛ ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", "oauth_mobile_redirect_uri_override_description": "Ų‚Ų… بØĒŲØšŲŠŲ„Ų‡ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ŲŠØŗŲ…Ø­ Ų…ŲˆŲØą OAuth Ø¨Ų…ØšØąŲ URI Ų„Ų„ØŦŲˆØ§Ų„ØŒ Ų…ØĢŲ„ '{callback}'", - "oauth_profile_signing_algorithm": "ØŽŲˆØ§ØąØ˛Ų…ŲŠØŠ ØĒŲˆŲ‚ŲŠØš Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊", - "oauth_profile_signing_algorithm_description": "Ø§Ų„ØŽŲˆØ§ØąØ˛Ų…ŲŠØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ØŠ Ų„Ų„ØĒŲˆŲ‚ŲŠØš ØšŲ„Ų‰ ؅؄؁ ØĒØšØąŲŠŲ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų….", - "oauth_scope": "Ø§Ų„Ų†ØˇØ§Ų‚", "oauth_settings": "OAuth", "oauth_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ OAuth", "oauth_settings_more_details": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø­ŲˆŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Ø§Ų„ŲˆØĢاØĻŲ‚.", - "oauth_signing_algorithm": "ØŽŲˆØ§ØąØ˛Ų…ŲŠØŠ Ø§Ų„ØĒŲˆŲ‚ŲŠØš", "oauth_storage_label_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ بØĒØĩŲ†ŲŠŲ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "oauth_storage_label_claim_description": "Ų‚Ų… ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ بØĒØšŲŠŲŠŲ† ØĒØĩŲ†ŲŠŲ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„ØŽØ§Øĩ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ØšŲ„Ų‰ Ų‚ŲŠŲ…ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ.", "oauth_storage_quota_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ بحØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", @@ -366,12 +358,8 @@ "admin_password": "ŲƒŲ„Ų…ØŠ ØŗØą Ø§Ų„Ų…Ø´ØąŲ", "administration": "Ø§Ų„ØĨØ¯Ø§ØąØŠ", "advanced": "Ų…ØĒŲ‚Ø¯Ų…", - "advanced_settings_log_level_title": "Log level: {}", "advanced_settings_prefer_remote_subtitle": "ØĒŲƒŲˆŲ† بؚØļ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø¨ØˇŲŠØĻØŠ Ų„Ų„ØēØ§ŲŠØŠ ؁؊ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛. Ų‚Ų… بØĒŲ†Ø´ŲŠØˇ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ø¨ØšŲŠØ¯ØŠ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† Ø°Ų„Ųƒ.", "advanced_settings_prefer_remote_title": "ØĒ؁ØļŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ø¨ØšŲŠØ¯ØŠ", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", "advanced_settings_self_signed_ssl_title": "Ø§Ų„ØŗŲ…Ø§Ø­ Ø¨Ø´Ų‡Ø§Ø¯Ø§ØĒ SSL Ø§Ų„Ų…ŲˆŲ‚ØšØŠ ذاØĒŲŠŲ‹Ø§", "advanced_settings_tile_subtitle": "ØĨؚداداØĒ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø§Ų„Ų…ØĒŲ‚Ø¯Ų…ØŠ", "advanced_settings_troubleshooting_subtitle": "ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĨØļØ§ŲŲŠØŠ Ų„Ø§ØŗØĒŲƒØ´Ø§Ų Ø§Ų„ØŖØŽØˇØ§ØĄ ؈ØĨØĩŲ„Ø§Ø­Ų‡Ø§", @@ -395,9 +383,7 @@ "album_remove_user_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØŠ {user}؟", "album_share_no_users": "ŲŠØ¨Ø¯Ųˆ ØŖŲ†Ųƒ Ų‚Ų…ØĒ Ø¨Ų…Ø´Ø§ØąŲƒØŠ Ų‡Ø°Ø§ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ų…Øš ØŦŲ…ŲŠØš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØŖŲˆ Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØŖŲŠ Ų…ØŗØĒØŽØ¯Ų… Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…ØšŲ‡.", "album_thumbnail_card_item": "ØšŲ†ØĩØą ŲˆØ§Ø­Ø¯", - "album_thumbnail_card_items": "{} items", "album_thumbnail_card_shared": " ¡ . Ų…Ø´ØĒØąŲƒ", - "album_thumbnail_shared_by": "Shared by {}", "album_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_updated_setting_description": "ØĒŲ„Ų‚ŲŠ ØĨØ´ØšØ§ØąŲ‹Ø§ ØšØ¨Øą Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØšŲ†Ø¯Ų…Ø§ ŲŠØ­ØĒ؈؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø§Ų„Ų…Ø´ØĒØąŲƒ ØšŲ„Ų‰ Ų…Ø­ØĒŲˆŲŠØ§ØĒ ØŦØ¯ŲŠØ¯ØŠ", "album_user_left": "ØĒŲ… ØĒØąŲƒ {album}", @@ -435,10 +421,8 @@ "archive": "Ø§Ų„ØŖØąØ´ŲŠŲ", "archive_or_unarchive_photo": "ØŖØąØ´ŲØŠ Ø§Ų„ØĩŲˆØąØŠ ØŖŲˆ ØĨŲ„ØēØ§ØĄ ØŖØąØ´ŲØĒŲ‡Ø§", "archive_page_no_archived_assets": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø¤ØąØ´ŲØŠ", - "archive_page_title": "Archive ({})", "archive_size": "Ø­ØŦŲ… Ø§Ų„ØŖØąØ´ŲŠŲ", "archive_size_description": "ØĒŲƒŲˆŲŠŲ† Ø­ØŦŲ… Ø§Ų„ØŖØąØ´ŲŠŲ Ų„Ų„ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ (Ø¨Ø§Ų„ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ)", - "archived": "Archived", "archived_count": "{count, plural, other {Ø§Ų„ØŖØąØ´ŲŠŲ #}}", "are_these_the_same_person": "Ų‡Ų„ Ų‡Ø¤Ų„Ø§ØĄ Ų‡Ų… Ų†ŲØŗ Ø§Ų„Ø´ØŽØĩ؟", "are_you_sure_to_do_this": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØŖŲ† ØĒŲØšŲ„ Ų‡Ø°Ø§ØŸ", @@ -460,39 +444,26 @@ "asset_list_settings_title": "Ø´Ø¨ŲƒØŠ Ø§Ų„ØĩŲˆØą", "asset_offline": "Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ ØēŲŠØą اØĒØĩØ§Ų„", "asset_offline_description": "Ų„Ų… ŲŠØšØ¯ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ Ø§Ų„ØŽØ§ØąØŦ؊ Ų…ŲˆØŦŲˆØ¯Ų‹Ø§ ØšŲ„Ų‰ Ø§Ų„Ų‚ØąØĩ. ŲŠØąØŦŲ‰ Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ų…ØŗØ¤ŲˆŲ„ Immich Ų„Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØ§ØšØ¯ØŠ.", - "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "ØĒŲ… ØĒØŽØˇŲŠŲ‡", "asset_skipped_in_trash": "؁؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "asset_uploaded": "ØĒŲ… Ø§Ų„ØąŲØš", "asset_uploading": "ØŦØ§ØąŲ Ø§Ų„ØąŲØšâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", "asset_viewer_settings_title": "ØšØ§ØąØļ Ø§Ų„ØŖØĩŲˆŲ„", "assets": "Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", "assets_added_count": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", "assets_added_to_album_count": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {count, plural, one {# Ø§Ų„ØŖØĩŲ„} other {# Ø§Ų„ØŖØĩŲˆŲ„}} ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "assets_added_to_name_count": "ØĒŲ… ØĨØļØ§ŲØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ }} ØĨŲ„Ų‰ {hasName, select, true {{name}} other {ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯}}", "assets_count": "{count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "ØĒŲ… Ų†Ų‚Ų„ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "assets_permanently_deleted_count": "ØĒŲ… Ø­Ø°Ų {count, plural, one {# Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {# Ų‡Ø°Ų‡ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} Ø¨Ø´ŲƒŲ„ داØĻŲ…", "assets_removed_count": "ØĒŲ…ØĒ ØĨØ˛Ø§Ų„ØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", "assets_restore_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§ØŗØĒؚاد؊ ØŦŲ…ŲŠØš Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠØŸ Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ! Ų„Ø§Ø­Ø¸ ØŖŲ†Ų‡ Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§ØŗØĒؚاد؊ ØŖŲŠ ØŖØĩŲˆŲ„ ØēŲŠØą Ų…ØĒØĩŲ„ØŠ Ø¨Ų‡Ø°Ų‡ Ø§Ų„ØˇØąŲŠŲ‚ØŠ.", "assets_restored_count": "ØĒŲ…ØĒ Ø§ØŗØĒؚاد؊ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", "assets_trashed_count": "ØĒŲ… ØĨØąØŗØ§Ų„ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {Ų‡Ø°Ų‡ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø¨Ø§Ų„ŲØšŲ„", "authorized_devices": "Ø§Ų„ØŖØŦŲ‡Ø˛Ų‡ Ø§Ų„Ų…ØŽŲˆŲ„ØŠ", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "ØŽŲ„Ų", "back_close_deselect": "Ø§Ų„ØąØŦŲˆØš ØŖŲˆ Ø§Ų„ØĨØēŲ„Ø§Ų‚ ØŖŲˆ ØĨŲ„ØēØ§ØĄ Ø§Ų„ØĒØ­Ø¯ŲŠØ¯", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", "backup_album_selection_page_albums_tap": "Ø§Ų†Ų‚Øą Ų„Ų„ØĒØļŲ…ŲŠŲ†ØŒ ŲˆØ§Ų†Ų‚Øą Ų†Ų‚ØąŲ‹Ø§ Ų…Ø˛Ø¯ŲˆØŦŲ‹Ø§ Ų„Ų„Ø§ØŗØĒØĢŲ†Ø§ØĄ", "backup_album_selection_page_assets_scatter": "ŲŠŲ…ŲƒŲ† ØŖŲ† ØĒŲ†ØĒØ´Øą Ø§Ų„ØŖØĩŲˆŲ„ ØšØ¨Øą ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ų…ØĒؚدد؊. ŲˆØ¨Ø§Ų„ØĒØ§Ų„ŲŠØŒ ŲŠŲ…ŲƒŲ† ØĒØļŲ…ŲŠŲ† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ ØŖŲˆ Ø§ØŗØĒØ¨ØšØ§Ø¯Ų‡Ø§ ØŖØĢŲ†Ø§ØĄ ØšŲ…Ų„ŲŠØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ.", "backup_album_selection_page_select_albums": "حدد Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", @@ -501,11 +472,9 @@ "backup_all": "Ø§Ų„ØŦŲ…ŲŠØš", "backup_background_service_backup_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ...", "backup_background_service_connection_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ...", - "backup_background_service_current_upload_notification": "Uploading {}", "backup_background_service_default_notification": "Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ...", "backup_background_service_error_title": "ØŽØˇØŖ ؁؊ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_background_service_in_progress_notification": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ...", - "backup_background_service_upload_failure_notification": "Failed to upload {}", "backup_controller_page_albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ احØĒŲŠØ§ØˇŲŠØŠ", "backup_controller_page_background_app_refresh_disabled_content": "Ų‚Ų… بØĒŲ…ŲƒŲŠŲ† ØĒØ­Ø¯ŲŠØĢ ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŽŲ„ŲŲŠØŠ ؁؊ Ø§Ų„ØĨؚداداØĒ > ØšØ§Ų… > ØĒØ­Ø¯ŲŠØĢ ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ.", "backup_controller_page_background_app_refresh_disabled_title": "ØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ", @@ -516,7 +485,6 @@ "backup_controller_page_background_battery_info_title": "ØĒØ­ØŗŲŠŲ† Ø§Ų„Ø¨ØˇØ§ØąŲŠØŠ", "backup_controller_page_background_charging": "ŲŲ‚Øˇ ØŖØĢŲ†Ø§ØĄ Ø§Ų„Ø´Ø­Ų†", "backup_controller_page_background_configure_error": "ŲØ´Ų„ ؁؊ ØĒŲƒŲˆŲŠŲ† ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", - "backup_controller_page_background_delay": "Delay new assets backup: {}", "backup_controller_page_background_description": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ų„ØĨØŦØąØ§ØĄ Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠ Ų„ØŖŲŠ ØŖØĩŲˆŲ„ ØŦØ¯ŲŠØ¯ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ Ø¯ŲˆŲ† Ø§Ų„Ø­Ø§ØŦØŠ ØĨŲ„Ų‰ ؁ØĒØ­ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "backup_controller_page_background_is_off": "ØĒŲ… ØĨŲŠŲ‚Ø§Ų Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„ØŽŲ„ŲŲŠØŠ", "backup_controller_page_background_is_on": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„ØŽŲ„ŲŲŠØŠ Ų‚ŲŠØ¯ Ø§Ų„ØĒØ´ØēŲŠŲ„", @@ -526,12 +494,8 @@ "backup_controller_page_backup": "Ø¯ØšŲ…", "backup_controller_page_backup_selected": "Ø§Ų„Ų…Ø­Ø¯Ø¯: ", "backup_controller_page_backup_sub": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ", - "backup_controller_page_created": "Created on: {}", "backup_controller_page_desc_backup": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØŖŲ…Ø§Ų…ŲŠ Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ØĨŲ„Ų‰ Ø§Ų„ØŽØ§Ø¯Ų… ØšŲ†Ø¯ ؁ØĒØ­ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚.", "backup_controller_page_excluded": "Ų…ØŗØĒبؚد: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", "backup_controller_page_info": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_controller_page_none_selected": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØĒØ­Ø¯ŲŠØ¯", "backup_controller_page_remainder": "Ø¨Ų‚ŲŠØŠ", @@ -540,7 +504,6 @@ "backup_controller_page_start_backup": "Ø¨Ø¯ØĄ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_controller_page_status_off": "Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ ØēŲŠØą ŲØšØ§Ų„ØŠ", "backup_controller_page_status_on": "Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ ŲØšØ§Ų„ØŠ", - "backup_controller_page_storage_format": "{} of {} used", "backup_controller_page_to_backup": "Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ", "backup_controller_page_total_sub": "ØŦŲ…ŲŠØš Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ŲØąŲŠØ¯ØŠ Ų…Ų† ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ų…ØŽØĒØ§ØąØŠ", "backup_controller_page_turn_off": "Ų‚Ų… بØĨŲŠŲ‚Ø§Ų ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„Ų…Ų‚Ø¯Ų…ØŠ", @@ -553,7 +516,6 @@ "backup_manual_success": "Ų†ØŦاح", "backup_manual_title": "Ø­Ø§Ų„ØŠ Ø§Ų„ØĒØ­Ų…ŲŠŲ„", "backup_options_page_title": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Ø§Ų„Ų‰ Ø§Ų„ŲˆØąØ§ØĄ", "birthdate_saved": "ØĒŲ… Ø­ŲØ¸ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ Ø¨Ų†ØŦاح", "birthdate_set_description": "؊ØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų… ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ Ų„Ø­ØŗØ§Ø¨ ØšŲ…Øą Ų‡Ø°Ø§ Ø§Ų„Ø´ØŽØĩ ŲˆŲ‚ØĒ Ø§Ų„ØĒŲ‚Ø§Øˇ Ø§Ų„ØĩŲˆØąØŠ.", @@ -565,21 +527,16 @@ "bulk_keep_duplicates_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų€ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰ Ų…ŲƒØąØą} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ų…ŲƒØąØąØŠ}}؟ ØŗŲŠØ¤Ø¯ŲŠ Ų‡Ø°Ø§ ØĨŲ„Ų‰ Ø­Ų„ ØŦŲ…ŲŠØš Ų…ØŦŲ…ŲˆØšØ§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ Ø¯ŲˆŲ† Ø­Ø°Ų ØŖŲŠ Ø´ŲŠØĄ.", "bulk_trash_duplicates_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØąØŗØ§Ų„ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰ Ų…ŲƒØąØą} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ų…ŲƒØąØąØŠ}} ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ ؟ ØŗŲŠØ­ØĒŲØ¸ Ų‡Ø°Ø§ Ø¨ØŖŲƒØ¨Øą Ų…Ø­ØĒŲˆŲ‰ Ų…Ų† ŲƒŲ„ Ų…ØŦŲ…ŲˆØšØŠ ŲˆŲŠØąØŗŲ„ ØŦŲ…ŲŠØš Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ Ø§Ų„ØŖØŽØąŲ‰ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ.", "buy": "Ø´ØąØ§ØĄ immich", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", "cache_settings_clear_cache_button": "Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", "cache_settings_clear_cache_button_title": "ŲŠŲ‚ŲˆŲ… Ø¨Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„Ų„ØĒØˇØ¨ŲŠŲ‚.ØŗŲŠØ¤ØĢØą Ų‡Ø°Ø§ Ø¨Ø´ŲƒŲ„ ŲƒØ¨ŲŠØą ØšŲ„Ų‰ ØŖØ¯Ø§ØĄ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø­ØĒŲ‰ ØĨؚاد؊ Ø¨Ų†Ø§ØĄ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ.", "cache_settings_duplicated_assets_clear_button": "ŲˆØ§ØļØ­", "cache_settings_duplicated_assets_subtitle": "Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų„ØĒ؊ ØĒŲ… ØĒØŦØ§Ų‡Ų„Ų‡Ø§ Ø§Ų„Ų…Ø¯ØąØŦØŠ ؁؊ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", "cache_settings_statistics_album": "Ų…ŲƒØĒØ¨Ų‡ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąŲ‡", - "cache_settings_statistics_assets": "{} assets ({})", "cache_settings_statistics_full": "ØĩŲˆØą ŲƒØ§Ų…Ų„ØŠ", "cache_settings_statistics_shared": "ØĩŲˆØąØŠ ØŖŲ„Ø¨ŲˆŲ… Ų…Ø´ØĒØąŲƒØŠ", "cache_settings_statistics_thumbnail": "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ų…ØĩØēØąØŠ", "cache_settings_statistics_title": "Ø§ØŗØĒØŽØ¯Ø§Ų… Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", "cache_settings_subtitle": "ØĒØ­ŲƒŲ… ؁؊ ØŗŲ„ŲˆŲƒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŦŲˆØ§Ų„.", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", "cache_settings_tile_subtitle": "Ø§Ų„ØĒØ­ŲƒŲ… ؁؊ ØŗŲ„ŲˆŲƒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø­Ų„ŲŠ", "cache_settings_tile_title": "Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø­Ų„ŲŠ", "cache_settings_title": "ØĨؚداداØĒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", @@ -588,12 +545,10 @@ "camera_model": "ØˇØąØ§Ø˛ Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§", "cancel": "ØĨŲ„ØēØ§ØĄ", "cancel_search": "Ø§Ų„Øē؊ Ø§Ų„Ø¨Ø­ØĢ", - "canceled": "Canceled", "cannot_merge_people": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø¯Ų…ØŦ Ø§Ų„ØŖØ´ØŽØ§Øĩ", "cannot_undo_this_action": "Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ!", "cannot_update_the_description": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØĩ؁", "change_date": "ØēŲŠŲ‘Øą Ø§Ų„ØĒØ§ØąŲŠØŽ", - "change_display_order": "Change display order", "change_expiration_time": "ØĒØēŲŠŲŠØą ŲˆŲ‚ØĒ Ø§Ų†ØĒŲ‡Ø§ØĄ Ø§Ų„ØĩŲ„Ø§Ø­ŲŠØŠ", "change_location": "ØēŲŠŲ‘Øą Ø§Ų„Ų…ŲˆŲ‚Øš", "change_name": "ØĒØēŲŠŲŠØą Ø§Ų„ØĨØŗŲ…", @@ -605,12 +560,10 @@ "change_password_form_new_password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ", "change_password_form_password_mismatch": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą ØēŲŠØą Ų…ØˇØ§Ø¨Ų‚ØŠ", "change_password_form_reenter_new_password": "ØŖØšØ¯ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą ØŦØ¯ŲŠØ¯ØŠ", + "change_pin_code": "ØĒØēŲŠŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", "change_your_password": "ØēŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", "changed_visibility_successfully": "ØĒŲ… ØĒØēŲŠŲŠØą Ø§Ų„ØąØ¤ŲŠØŠ Ø¨Ų†ØŦاح", "check_all": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ŲƒŲ„", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŗØŦŲ„Ø§ØĒ", "choose_matching_people_to_merge": "ا؎ØĒØą Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ų…ØĒØˇØ§Ø¨Ų‚ŲŠŲ† Ų„Ø¯Ų…ØŦŲ‡Ų…", "city": "Ø§Ų„Ų…Ø¯ŲŠŲ†ØŠ", @@ -619,14 +572,6 @@ "clear_all_recent_searches": "Ų…ØŗØ­ ØŦŲ…ŲŠØš ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„ØŖØŽŲŠØąØŠ", "clear_message": "ØĨØŽŲ„Ø§ØĄ Ø§Ų„ØąØŗØ§Ų„ØŠ", "clear_value": "ØĨØŽŲ„Ø§ØĄ Ø§Ų„Ų‚ŲŠŲ…ØŠ", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "clockwise": "باØĒØŦØ§Ų‡ ØšŲ‚Ø§ØąØ¨ Ø§Ų„ØŗØ§ØšØŠ", "close": "ØĨØēŲ„Ø§Ų‚", "collapse": "ØˇŲŠ", @@ -639,23 +584,21 @@ "comments_are_disabled": "Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ Ų…ØšØˇŲ„ØŠ", "common_create_new_album": "ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", "common_server_error": "ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† اØĒØĩØ§Ų„ Ø§Ų„Ø´Ø¨ŲƒØŠ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ، ŲˆØ§Ų„ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ† Ø§Ų„ØŦŲ‡Ø§Ø˛ Ų‚Ø§Ø¨Ų„ Ų„Ų„ŲˆØĩŲˆŲ„ ؈ØĨØĩØ¯Ø§ØąØ§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚/Ø§Ų„ØŦŲ‡Ø§Ø˛ Ų…ØĒŲˆØ§ŲŲ‚ØŠ.", - "completed": "Completed", "confirm": "ØĒØŖŲƒŲŠØ¯", "confirm_admin_password": "ØĒØŖŲƒŲŠØ¯ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą Ø§Ų„Ų…ØŗØ¤ŲˆŲ„", "confirm_delete_face": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† Ø­Ø°Ų ؈ØŦŲ‡ {name} Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ØŸ", "confirm_delete_shared_link": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų‡Ø°Ø§ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŸ", "confirm_keep_this_delete_others": "ØŗŲŠØĒŲ… Ø­Ø°Ų ØŦŲ…ŲŠØš Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŖØŽØąŲ‰ ؁؊ Ø§Ų„Ų…ØŦŲ…ŲˆØšØŠ Ø¨Ø§ØŗØĒØĢŲ†Ø§ØĄ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„. Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§Ų„Ų…ØĒابؚ؊؟", + "confirm_new_pin_code": "ØĢبØĒ Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„ØŦØ¯ŲŠØ¯", "confirm_password": "ØĒØŖŲƒŲŠØ¯ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "contain": "Ų…Ø­ØĒŲˆØ§ØŠ", "context": "Ø§Ų„ØŗŲŠØ§Ų‚", "continue": "Ų…ØĒابؚ؊", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", "control_bottom_app_bar_create_new_album": "ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", "control_bottom_app_bar_delete_from_immich": " Ø­Ø°Ų Ų…Ų†Ø§Ų„ ØĒØˇØ¨ŲŠŲ‚", "control_bottom_app_bar_delete_from_local": "Ø­Ø°Ų Ų…Ų† Ø§Ų„ØŦŲ‡Ø§Ø˛", "control_bottom_app_bar_edit_location": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲˆØŦŲ‡ØŠ", "control_bottom_app_bar_edit_time": "ØĒØ­ØąŲŠØą Ø§Ų„ØĒØ§ØąŲŠØŽ ŲˆØ§Ų„ŲˆŲ‚ØĒ", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "Ų…Ø´Ø§ØąŲƒØŠ ØĨŲ„Ų‰", "control_bottom_app_bar_trash_from_immich": "Ø­Ø°ŲŲ‡ ŲˆŲ†Ų‚Ų„Ų‡ ؁؊ ØŗŲ„Ų‡ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "copied_image_to_clipboard": "ØĒŲ… Ų†ØŗØŽ Ø§Ų„ØĩŲˆØąØŠ ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ.", @@ -677,7 +620,6 @@ "create_link": "ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ", "create_link_to_share": "ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ", "create_link_to_share_description": "Ø§Ų„ØŗŲ…Ø§Ø­ Ų„ØŖŲŠ Ø´ØŽØĩ Ų„Ø¯ŲŠŲ‡ Ø§Ų„ØąØ§Ø¨Øˇ Ø¨Ų…Ø´Ø§Ų‡Ø¯ØŠ Ø§Ų„ØĩŲˆØąØŠ (Ø§Ų„ØĩŲˆØą) Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ", - "create_new": "CREATE NEW", "create_new_person": "ØĨŲ†Ø´Ø§ØĄ Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", "create_new_person_hint": "ØĒØšŲŠŲŠŲ† Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ Ų„Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", "create_new_user": "ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų… ØŦØ¯ŲŠØ¯", @@ -687,10 +629,9 @@ "create_tag_description": "ØŖŲ†Ø´ØĻ ØšŲ„Ø§Ų…ØŠ ØŦØ¯ŲŠØ¯ØŠ. Ø¨Ø§Ų„Ų†ØŗØ¨ØŠ Ų„Ų„ØšŲ„Ø§Ų…Ø§ØĒ Ø§Ų„Ų…ØĒØ¯Ø§ØŽŲ„ØŠØŒ ŲŠØąØŦŲ‰ ØĨØ¯ØŽØ§Ų„ Ø§Ų„Ų…ØŗØ§Øą Ø§Ų„ŲƒØ§Ų…Ų„ Ų„Ų„ØšŲ„Ø§Ų…ØŠ Ø¨Ų…Ø§ ؁؊ Ø°Ų„Ųƒ Ø§Ų„ØŽØˇŲˆØˇ Ø§Ų„Ų…Ø§ØĻŲ„ØŠ Ų„Ų„ØŖŲ…Ø§Ų….", "create_user": "ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų…", "created": "ØĒŲ… Ø§Ų„ØĨŲ†Ø´Ø§ØĄ", - "crop": "Crop", "curated_object_page_title": "ØŖØ´ŲŠØ§ØĄ", "current_device": "Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ø­Ø§Ų„ŲŠ", - "current_server_address": "Current server address", + "current_pin_code": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„Ø­Ø§Ų„ŲŠ", "custom_locale": "Ų„ØēØŠ Ų…ØŽØĩØĩØŠ", "custom_locale_description": "ØĒŲ†ØŗŲŠŲ‚ Ø§Ų„ØĒŲˆØ§ØąŲŠØŽ ŲˆØ§Ų„ØŖØąŲ‚Ø§Ų… Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ Ø§Ų„Ų„ØēØŠ ŲˆØ§Ų„Ų…Ų†ØˇŲ‚ØŠ", "daily_title_text_date": "E ، MMM DD", @@ -741,7 +682,6 @@ "direction": "Ø§Ų„ØĨØĒØŦØ§Ų‡", "disabled": "Ų…ØšØˇŲ„", "disallow_edits": "Ų…Ų†Øš Ø§Ų„ØĒØšØ¯ŲŠŲ„Ø§ØĒ", - "discord": "Discord", "discover": "Ø§ŲƒØĒØ´Ų", "dismiss_all_errors": "ØĒØŦØ§Ų‡Ų„ ŲƒØ§ŲØŠ Ø§Ų„ØŖØŽØˇØ§ØĄ", "dismiss_error": "ØĒØŦØ§Ų‡Ų„ Ø§Ų„ØŽØˇØŖ", @@ -753,26 +693,12 @@ "documentation": "Ø§Ų„ŲˆØĢاØĻŲ‚", "done": "ØĒŲ…", "download": "ØĒŲ†Ø˛ŲŠŲ„", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", "download_include_embedded_motion_videos": "Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø¯Ų…ØŦØŠ", "download_include_embedded_motion_videos_description": "ØĒØļŲ…ŲŠŲ† Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ØļŲ…Ų†ØŠ ؁؊ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒØŠ ŲƒŲ…Ų„Ų ؅؆؁ØĩŲ„", - "download_notfound": "Download not found", - "download_paused": "Download paused", "download_settings": "Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ", "download_settings_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ų…ØĒØšŲ„Ų‚ØŠ بØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", "downloading": "ØŦØ§ØąŲ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "downloading_asset_filename": "{filename} Ų‚ŲŠØ¯ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", - "downloading_media": "Downloading media", "drop_files_to_upload": "Ų‚Ų… بØĨØŗŲ‚Ø§Øˇ Ø§Ų„Ų…Ų„ŲØ§ØĒ ؁؊ ØŖŲŠ Ų…ŲƒØ§Ų† Ų„ØąŲØšŲ‡Ø§", "duplicates": "Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "duplicates_description": "Ų‚Ų… Ø¨Ø­Ų„ ŲƒŲ„ Ų…ØŦŲ…ŲˆØšØŠ Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„ØĨØ´Ø§ØąØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ، ØĨŲ† ؈ØŦدØĒ", @@ -802,19 +728,15 @@ "editor_crop_tool_h2_aspect_ratios": "Ų†ØŗØ¨ Ø§Ų„ØšØąØļ ØĨŲ„Ų‰ Ø§Ų„Ø§ØąØĒŲØ§Øš", "editor_crop_tool_h2_rotation": "Ø§Ų„ØĒØ¯ŲˆŲŠØą", "email": "Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ", - "empty_folder": "This folder is empty", "empty_trash": "ØŖŲØąØē ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "empty_trash_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨŲØąØ§Øē ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ؟ ØŗŲŠØ¤Ø¯ŲŠ Ų‡Ø°Ø§ ØĨŲ„Ų‰ ØĨØ˛Ø§Ų„ØŠ ØŦŲ…ŲŠØš Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ؁؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊ Ų…Ų† Immich.\nŲ„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ!", "enable": "ØĒŲØšŲŠŲ„", "enabled": "Ų…ŲØšŲ„", "end_date": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨŲ†ØĒŲ‡Ø§ØĄ", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "ØŽØˇØŖ", - "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "حدØĢ ØŽØˇØŖ ؁؊ Ø­Ø°Ų Ø§Ų„ŲˆØŦŲ‡ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„", "error_loading_image": "حدØĢ ØŽØˇØŖ ØŖØĢŲ†Ø§ØĄ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ", - "error_saving_image": "Error: {}", "error_title": "ØŽØˇØŖ - حدØĢ ØŽŲ„Ų„ŲŒ Ų…Ø§", "errors": { "cannot_navigate_next_asset": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø§Ų†ØĒŲ‚Ø§Ų„ ØĨŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ØĒØ§Ų„ŲŠ", @@ -948,10 +870,6 @@ "exif_bottom_sheet_location": "Ų…ŲˆŲ‚Øš", "exif_bottom_sheet_people": "Ø§Ų„Ų†Ø§Øŗ", "exif_bottom_sheet_person_add_person": "اØļ؁ Ø§ØŗŲ…Ø§", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "ØŽØąŲˆØŦ Ų…Ų† Ø§Ų„ØšØąØļ Ø§Ų„ØĒŲ‚Ø¯ŲŠŲ…ŲŠ", "expand_all": "ØĒŲˆØŗŲŠØš Ø§Ų„ŲƒŲ„", "experimental_settings_new_asset_list_subtitle": "ØŖØšŲ…Ø§Ų„ ØŦØ§ØąŲŠØŠ", @@ -968,12 +886,9 @@ "extension": "Ø§Ų„ØĨŲ…ØĒداد", "external": "ØŽØ§ØąØŦ؊", "external_libraries": "Ø§Ų„Ų…ŲƒØĒباØĒ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "ØēŲŠØą Ų…ØšŲŠŲ†", - "failed": "Failed", "failed_to_load_assets": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲˆŲ„", - "failed_to_load_folder": "Failed to load folder", "favorite": "؅؁ØļŲ„", "favorite_or_unfavorite_photo": "ØĒ؁ØļŲŠŲ„ ØŖŲˆ ØĨŲ„ØēØ§ØĄ ØĒ؁ØļŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ", "favorites": "Ø§Ų„Ų…ŲØļŲ„ØŠ", @@ -985,23 +900,18 @@ "file_name_or_extension": "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų ØŖŲˆ Ø§Ų…ØĒØ¯Ø§Ø¯Ų‡", "filename": "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų", "filetype": "Ų†ŲˆØš Ø§Ų„Ų…Ų„Ų", - "filter": "Filter", "filter_people": "ØĒØĩŲŲŠØŠ Ø§Ų„Ø§Ø´ØŽØ§Øĩ", "find_them_fast": "ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØšØĢŲˆØą ØšŲ„ŲŠŲ‡Ø§ Ø¨ØŗØąØšØŠ Ø¨Ø§Ų„Ø§ØŗŲ… Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„Ø¨Ø­ØĢ", "fix_incorrect_match": "ØĨØĩŲ„Ø§Ø­ Ø§Ų„Ų…ØˇØ§Ø¨Ų‚ØŠ ØēŲŠØą Ø§Ų„ØĩØ­ŲŠØ­ØŠ", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Ø§Ų„Ų…ØŦŲ„Ø¯Ø§ØĒ", "folders_feature_description": "ØĒØĩŲØ­ ØšØąØļ Ø§Ų„Ų…ØŦŲ„Ø¯ Ų„Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ØšŲ„Ų‰ Ų†Ø¸Ø§Ų… Ø§Ų„Ų…Ų„ŲØ§ØĒ", "forward": "ØĨŲ„Ų‰ Ø§Ų„ØŖŲ…Ø§Ų…", "general": "ØšØ§Ų…", "get_help": "Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØ§ØšØ¯ØŠ", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Ø§Ų„Ø¨Ø¯ØĄ", "go_back": "Ø§Ų„ØąØŦŲˆØš Ų„Ų„ØŽŲ„Ų", "go_to_folder": "Ø§Ø°Ų‡Ø¨ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯", "go_to_search": "Ø§Ø°Ų‡Ø¨ ØĨŲ„Ų‰ Ø§Ų„Ø¨Ø­ØĢ", - "grant_permission": "Grant permission", "group_albums_by": "ØĒØŦŲ…ŲŠØš Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø­ØŗØ¨...", "group_country": "Ų…ØŦŲ…ŲˆØšØŠ Ø§Ų„Ø¨Ų„Ø¯", "group_no": "Ø¨Ø¯ŲˆŲ† ØĒØŦŲ…ŲŠØš", @@ -1011,12 +921,6 @@ "haptic_feedback_switch": "ØĒŲ…ŲƒŲŠŲ† ØąØ¯ŲˆØ¯ Ø§Ų„ŲØšŲ„ Ø§Ų„Ų„Ų…ØŗŲŠØŠ", "haptic_feedback_title": "ØąØ¯ŲˆØ¯ ŲØšŲ„ Ų„Ų…ØŗŲŠØŠ", "has_quota": "Ų…Ø­Ø¯Ø¯ بحØĩØŠ", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "Ų…ØąØ­Ø¨Ø§ {name} ({email})", "hide_all_people": "ØĨØŽŲØ§ØĄ ØŦŲ…ŲŠØš Ø§Ų„ØŖØ´ØŽØ§Øĩ", "hide_gallery": "Ø§ØŽŲØ§ØĄ Ø§Ų„Ų…ØšØąØļ", @@ -1040,8 +944,6 @@ "home_page_upload_err_limit": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨŲ„Ø§ ØĒØ­Ų…ŲŠŲ„ 30 ØŖØ­Ø¯ Ø§Ų„ØŖØĩŲˆŲ„ ؁؊ ŲˆŲ‚ØĒ ŲˆØ§Ø­Ø¯ ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "host": "Ø§Ų„Ų…Øļ؊؁", "hour": "ØŗØ§ØšØŠ", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "ØĩŲˆØąØŠ", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ Ų…Øš {person1} ؁؊ {date}", @@ -1053,7 +955,6 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}، {country} Ų…Øš {person1} ؈{person2} ؁؊ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}، {country} Ų…Øš {person1}، {person2}، ؈{person3} ؁؊ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}, {country} with {person1}, {person2}, Ų…Øš {additionalCount, number} ØĸØŽØąŲŠŲ† ؁؊ {date}", - "image_saved_successfully": "Image saved", "image_viewer_page_state_provider_download_started": "Ø¨Ø¯ØŖ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "image_viewer_page_state_provider_download_success": "ØĒŲ… Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø¨Ų†ØŦاح", "image_viewer_page_state_provider_share_error": "ØŽØˇØŖ ؁؊ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", @@ -1075,8 +976,6 @@ "night_at_midnight": "ŲƒŲ„ Ų„ŲŠŲ„ØŠ ØšŲ†Ø¯ Ų…Ų†ØĒØĩ؁ Ø§Ų„Ų„ŲŠŲ„", "night_at_twoam": "ŲƒŲ„ Ų„ŲŠŲ„ØŠ Ø§Ų„ØŗØ§ØšØŠ 2 Øĩباحا" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "Ø¯ØšŲˆØŠ Ø§Ų„ØŖØ´ØŽØ§Øĩ", "invite_to_album": "Ø¯ØšŲˆØŠ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "items_count": "{count, plural, one {# ØšŲ†ØĩØą} other {# ØšŲ†Ø§ØĩØą}}", @@ -1112,9 +1011,6 @@ "list": "Ų‚Ø§ØĻŲ…ØŠ", "loading": "ØĒØ­Ų…ŲŠŲ„", "loading_search_results_failed": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", "location_picker_choose_on_map": "ا؎ØĒØą ØšŲ„Ų‰ Ø§Ų„ØŽØąŲŠØˇØŠ", "location_picker_latitude_error": "ØŖØ¯ØŽŲ„ ØŽØˇ ØšØąØļ ØĩØ§Ų„Ø­", @@ -1130,7 +1026,6 @@ "login_form_api_exception": " Ø§ØŗØĒØĢŲ†Ø§ØĄ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ. ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŽØ§Ø¯Ų… ŲˆØ§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ Ų…ØąØŠ ØŖØŽØąŲ‰ ", "login_form_back_button_text": "Ø§Ų„ØąØŦŲˆØš Ų„Ų„ØŽŲ„Ų", "login_form_email_hint": "yoursemail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "url Ų†Ų‚ØˇØŠ Ų†Ų‡Ø§ŲŠØŠ Ø§Ų„ØŽØ§Ø¯Ų…", "login_form_err_http": "ŲŠØąØŦŲ‰ ØĒØ­Ø¯ŲŠØ¯ http:// ØŖŲˆ https://", "login_form_err_invalid_email": "Ø¨ØąŲŠØ¯ ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØŽØ§ØˇØĻ", @@ -1164,8 +1059,6 @@ "manage_your_devices": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø§Ų„ØĒ؊ ØĒŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ ØĨŲ„ŲŠŲ‡Ø§", "manage_your_oauth_connection": "ØĨØ¯Ø§ØąØŠ اØĒØĩØ§Ų„ OAuth Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "map": "Ø§Ų„ØŽØąŲŠØˇØŠ", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", "map_cannot_get_user_location": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ų…ŲˆŲ‚Øš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "map_location_dialog_yes": "Ų†ØšŲ…", "map_location_picker_page_use_location": "Ø§ØŗØĒØŽØ¯Ų… Ų‡Ø°Ø§ Ø§Ų„Ų…ŲˆŲ‚Øš", @@ -1179,9 +1072,7 @@ "map_settings": "ØĨؚداداØĒ Ø§Ų„ØŽØąŲŠØˇØŠ", "map_settings_dark_mode": "Ø§Ų„ŲˆØļØš Ø§Ų„Ų…Ø¸Ų„Ų…", "map_settings_date_range_option_day": "24 ØŗØ§ØšØŠ Ø§Ų„Ų…Ø§ØļŲŠØŠ", - "map_settings_date_range_option_days": "Past {} days", "map_settings_date_range_option_year": "Ø§Ų„ØŗŲ†ØŠ Ø§Ų„ŲØ§ØĻØĒØŠ", - "map_settings_date_range_option_years": "Past {} years", "map_settings_dialog_title": "ØĨؚداداØĒ Ø§Ų„ØŽØąŲŠØˇØŠ", "map_settings_include_show_archived": "ØĒØ´Ų…Ų„ Ø§Ų„ØŖØąØ´ŲØŠ", "map_settings_include_show_partners": "ØĒØļŲ…ŲŠŲ† Ø§Ų„Ø´ØąŲƒØ§ØĄ", @@ -1196,8 +1087,6 @@ "memories_setting_description": "ØĨØ¯Ø§ØąØŠ Ų…Ø§ ØĒØąØ§Ų‡ ؁؊ Ø°ŲƒØąŲŠØ§ØĒ؃", "memories_start_over": "Ø§Ø¨Ø¯ØŖ Ų…Ų† ØŦØ¯ŲŠØ¯", "memories_swipe_to_close": "Ø§ØŗØ­Ø¨ Ų„ØŖØšŲ„Ų‰ Ų„Ų„ØĨØēŲ„Ø§Ų‚", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Ø°ŲƒØąŲ‰", "memory_lane_title": "Ø°ŲƒØąŲŠØ§ØĒ، Ų…Ų† {title}", "menu": "Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ", @@ -1221,13 +1110,12 @@ "my_albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ؊", "name": "Ø§Ų„Ø§ØŗŲ…", "name_or_nickname": "Ø§Ų„Ø§ØŗŲ… ØŖŲˆ Ø§Ų„Ų„Ų‚Ø¨", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "ØŖØ¨Ø¯Ø§Ų‹", "new_album": "Ø§Ų„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", "new_api_key": "؅؁ØĒاح API ØŦØ¯ŲŠØ¯", "new_password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ", "new_person": "Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", + "new_pin_code": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„ØŦØ¯ŲŠØ¯", "new_user_created": "ØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų… ØŦØ¯ŲŠØ¯", "new_version_available": "ØĨØĩØ¯Ø§Øą ØŦØ¯ŲŠØ¯ Ų…ØĒاح", "newest_first": "Ø§Ų„ØŖØ­Ø¯ØĢ ØŖŲˆŲ„Ø§Ų‹", @@ -1251,7 +1139,6 @@ "no_results_description": "ØŦØąØ¨ ŲƒŲ„Ų…ØŠ ØąØĻŲŠØŗŲŠØŠ Ų…ØąØ§Ø¯ŲØŠ ØŖŲˆ ØŖŲƒØĢØą ØšŲ…ŲˆŲ…ŲŠØŠ", "no_shared_albums_message": "Ų‚Ų… بØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… Ų„Ų…Ø´Ø§ØąŲƒØŠ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…Øš Ø§Ų„ØŖØ´ØŽØ§Øĩ ؁؊ Ø´Ø¨ŲƒØĒ؃", "not_in_any_album": "Ų„ŲŠØŗØĒ ؁؊ ØŖŲŠ ØŖŲ„Ø¨ŲˆŲ…", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„ØĒØˇØ¨ŲŠŲ‚ ØĒØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ Ų…ØŗØ¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„", "notes": "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ", "notification_permission_dialog_content": "Ų„ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĨØŽØˇØ§ØąØ§ØĒ ، Ø§Ų†ØĒŲ‚Ų„ ØĨŲ„Ų‰ Ø§Ų„ØĨؚداداØĒ ؈ ا؎ØĒØ§Øą Ø§Ų„ØŗŲ…Ø§Ø­.", @@ -1261,14 +1148,12 @@ "notification_toggle_setting_description": "ØĒŲØšŲŠŲ„ ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ", "notifications": "ØĨØ´ØšØ§ØąØ§ØĒ", "notifications_setting_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", - "oauth": "OAuth", "official_immich_resources": "Ø§Ų„Ų…ŲˆØ§ØąØ¯ Ø§Ų„ØąØŗŲ…ŲŠØŠ Ų„Ø´ØąŲƒØŠ Immich", "offline": "ØēŲŠØą Ų…ØĒØĩŲ„", "offline_paths": "Ų…ØŗØ§ØąØ§ØĒ ØēŲŠØą Ų…ØĒØĩŲ„ØŠ", "offline_paths_description": "Ų‚Ø¯ ØĒŲƒŲˆŲ† Ų‡Ø°Ų‡ Ø§Ų„Ų†ØĒاØĻØŦ Ø¨ØŗØ¨Ø¨ Ø§Ų„Ø­Ø°Ų Ø§Ų„ŲŠØ¯ŲˆŲŠ Ų„Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØĒ؊ Ų„Ø§ ØĒØ´ŲƒŲ„ ØŦØ˛ØĄŲ‹Ø§ Ų…Ų† Ų…ŲƒØĒب؊ ØŽØ§ØąØŦŲŠØŠ.", "ok": "Ų†ØšŲ…", "oldest_first": "Ø§Ų„ØŖŲ‚Ø¯Ų… ØŖŲˆŲ„Ø§", - "on_this_device": "On this device", "onboarding": "Ø§Ų„ØĨؚداد Ø§Ų„ØŖŲˆŲ„ŲŠ", "onboarding_privacy_description": "ØĒØšØĒŲ…Ø¯ Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØ§Ų„ŲŠØŠ (ا؎ØĒŲŠØ§ØąŲŠ) ØšŲ„Ų‰ ØŽØ¯Ų…Ø§ØĒ ØŽØ§ØąØŦŲŠØŠØŒ ŲˆŲŠŲ…ŲƒŲ† ØĒØšØˇŲŠŲ„Ų‡Ø§ ؁؊ ØŖŲŠ ŲˆŲ‚ØĒ ؁؊ ØĨؚداداØĒ Ø§Ų„ØĨØ¯Ø§ØąØŠ.", "onboarding_theme_description": "ا؎ØĒØą Ų†ØŗŲ‚ Ø§Ų„ØŖŲ„ŲˆØ§Ų† Ų„Ų„Ų†ØŗØŽØŠ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ. ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØą Ø°Ų„Ųƒ Ų„Ø§Ø­Ų‚Ų‹Ø§ ؁؊ ØĨؚداداØĒ؃.", @@ -1292,14 +1177,12 @@ "partner_can_access": "ŲŠØŗØĒØˇŲŠØš {partner} Ø§Ų„ŲˆØĩŲˆŲ„", "partner_can_access_assets": "ØŦŲ…ŲŠØš Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ Ø¨Ø§ØŗØĒØĢŲ†Ø§ØĄ ØĒŲ„Ųƒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ؁؊ Ø§Ų„Ų…Ø¤ØąØ´ŲØŠ ŲˆØ§Ų„Ų…Ø­Ø°ŲˆŲØŠ", "partner_can_access_location": "Ø§Ų„Ų…ŲˆŲ‚Øš Ø§Ų„Ø°ŲŠ ØĒŲ… Ø§Ų„ØĒŲ‚Ø§Øˇ ØĩŲˆØąŲƒ ŲŲŠŲ‡", - "partner_list_user_photos": "{user}'s photos", "partner_list_view_all": "ØšØąØļ Ø§Ų„ŲƒŲ„", "partner_page_empty_message": "Ų„Ų… ؊ØĒŲ… Ų…Ø´Ø§ØąŲƒØŠ ØĩŲˆØąŲƒ بؚد Ų…Øš ØŖŲŠ Ø´ØąŲŠŲƒ.", "partner_page_no_more_users": "Ų„Ø§ Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų„ØĨØļØ§ŲØŠ", "partner_page_partner_add_failed": "ŲØ´Ų„ ؁؊ ØĨØļØ§ŲØŠ Ø´ØąŲŠŲƒ", "partner_page_select_partner": "حدد Ø´ØąŲŠŲƒŲ‹Ø§", "partner_page_shared_to_title": "Ų…Ø´ØĒØąŲƒ Ų„", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", "partner_sharing": "Ų…Ø´Ø§ØąŲƒØŠ Ø§Ų„Ø´ØąŲƒØ§ØĄ", "partners": "Ø§Ų„Ø´ØąŲƒØ§ØĄ", "password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", @@ -1345,6 +1228,9 @@ "photos_count": "{count, plural, one {{count, number} ØĩŲˆØąØŠ} other {{count, number} ØĩŲˆØą}}", "photos_from_previous_years": "ØĩŲˆØą Ų…Ų† Ø§Ų„ØŗŲ†ŲˆØ§ØĒ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ", "pick_a_location": "ا؎ØĒØą Ų…ŲˆŲ‚ØšŲ‹Ø§", + "pin_code_changed_successfully": "ØĒŲ… ØĒØēŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", + "pin_code_reset_successfully": "ØĒŲ… اؚاد؊ ØĒØšŲŠŲŠŲ† Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", + "pin_code_setup_successfully": "ØĒŲ… Ø§Ų†Ø´Ø§ØĄ ØąŲ‚Ų… ØŗØąŲŠ", "place": "Ų…ŲƒØ§Ų†", "places": "Ø§Ų„ØŖŲ…Ø§ŲƒŲ†", "places_count": "{count, plural, one {{count, number} Ų…ŲƒØ§Ų†} other {{count, number} ØŖŲ…Ø§ŲƒŲ†}}", @@ -1353,7 +1239,6 @@ "play_motion_photo": "ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒØŠ", "play_or_pause_video": "ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ØŖŲˆ ØĨŲŠŲ‚Ø§ŲŲ‡ Ų…Ø¤Ų‚ØĒŲ‹Ø§", "port": "Ø§Ų„Ų…Ų†ŲØ°", - "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "Ø§Ų„ØĒ؁ØļŲŠŲ„Ø§ØĒ", "preset": "Ø§Ų„ØĨؚداد Ø§Ų„Ų…ØŗØ¨Ų‚", "preview": "Ų…ØšØ§ŲŠŲ†ØŠ", @@ -1375,7 +1260,7 @@ "public_share": "Ų…Ø´Ø§ØąŲƒØŠ ØšØ§Ų…ØŠ", "purchase_account_info": "Ø¯Ø§ØšŲ…", "purchase_activated_subtitle": "Ø´ŲƒØąŲ‹Ø§ Ų„Ųƒ ØšŲ„Ų‰ Ø¯ØšŲ…Ųƒ Ų„Ų€ Immich ŲˆØ§Ų„Ø¨ØąŲ…ØŦŲŠØ§ØĒ ؅؁ØĒŲˆØ­ØŠ Ø§Ų„Ų…ØĩØ¯Øą", - "purchase_activated_time": "ØĒŲ… Ø§Ų„ØĒŲØšŲŠŲ„ ؁؊ {date, date}", + "purchase_activated_time": "ØĒŲ… Ø§Ų„ØĒŲØšŲŠŲ„ ؁؊ {date}", "purchase_activated_title": "Ų„Ų‚Ø¯ ØĒŲ… ØĒŲØšŲŠŲ„ ؅؁ØĒØ§Ø­Ųƒ Ø¨Ų†ØŦاح", "purchase_button_activate": "ØĒŲ†Ø´ŲŠØˇ", "purchase_button_buy": "Ø´ØąØ§ØĄ", @@ -1418,7 +1303,6 @@ "recent": "Ø­Ø¯ŲŠØĢ", "recent-albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ø­Ø¯ŲŠØĢØŠ", "recent_searches": "ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„ØŖØŽŲŠØąØŠ", - "recently_added": "Recently added", "recently_added_page_title": "ØŖØļ؊؁ Ų…Ø¤ØŽØąØ§", "refresh": "ØĒØ­Ø¯ŲŠØĢ", "refresh_encoded_videos": "ØĒØ­Ø¯ŲŠØĢ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø´ŲØąØŠ", @@ -1476,7 +1360,6 @@ "role_editor": "Ø§Ų„Ų…Ø­ØąØą", "role_viewer": "Ø§Ų„ØšØ§ØąØļ", "save": "Ø­ŲØ¸", - "save_to_gallery": "Save to gallery", "saved_api_key": "ØĒŲ… Ø­ŲØ¸ ؅؁ØĒاح Ø§Ų„Ų€ API", "saved_profile": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„Ų…Ų„Ų", "saved_settings": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„ØĨؚداداØĒ", @@ -1498,31 +1381,17 @@ "search_city": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ø§Ų„Ų…Ø¯ŲŠŲ†ØŠ...", "search_country": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ø§Ų„Ø¯ŲˆŲ„ØŠ...", "search_filter_apply": "ا؎ØĒØ§Øą Ø§Ų„ŲŲ„ØĒØą ", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", "search_filter_display_option_not_in_album": "Ų„ŲŠØŗ ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", "search_for": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ†", "search_for_existing_person": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ø´ØŽØĩ Ų…ŲˆØŦŲˆØ¯", - "search_no_more_result": "No more results", "search_no_people": "Ų„Ø§ ؊؈ØŦد ØŖØ´ØŽØ§Øĩ", "search_no_people_named": "Ų„Ø§ ؊؈ØŦد ØŖØ´ØŽØ§Øĩ Ø¨Ø§Ų„Ø§ØŗŲ… \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ", "search_page_categories": "؁ØĻاØĒ", "search_page_motion_photos": "Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒŲ‡", "search_page_no_objects": "Ų„Ø§ ØĒ؈ØŦد Ų…ØšŲ„ŲˆŲ…Ø§ØĒ ØšŲ† ØŖØ´ŲŠØ§ØĄ Ų…ØĒاح؊", "search_page_no_places": "Ų„Ø§ ØĒ؈ØŦد Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ų…ØĒŲˆŲØąØŠ Ų„Ų„ØŖŲ…Ø§ŲƒŲ†", "search_page_screenshots": "Ų„Ų‚ØˇØ§ØĒ Ø§Ų„Ø´Ø§Ø´ØŠ", - "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": " ØĩŲˆØą ذاØĒŲŠŲ‡", "search_page_things": "ØŖØ´ŲŠØ§ØĄ", "search_page_view_all_button": "ØšØąØļ Ø§Ų„ŲƒŲ„", @@ -1561,7 +1430,6 @@ "selected_count": "{count, plural, other {# Ų…Ø­Ø¯Ø¯ØŠ }}", "send_message": "‏ØĨØąØŗØ§Ų„ ØąØŗØ§Ų„ØŠ", "send_welcome_email": "ØĨØąØŗØ§Ų„ Ø¨ØąŲŠØ¯Ų‹Ø§ ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠŲ‹Ø§ ØĒØąØ­ŲŠØ¨ŲŠŲ‹Ø§", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "Ų†ØŗØŽØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "server_info_box_server_url": "ØšŲ†ŲˆØ§Ų† URL Ø§Ų„ØŽØ§Ø¯Ų…", "server_offline": "Ø§Ų„ØŽØ§Ø¯Ų… ØēŲŠØą Ų…ØĒØĩŲ„", @@ -1582,28 +1450,21 @@ "setting_image_viewer_preview_title": "ØĒØ­Ų…ŲŠŲ„ ØĩŲˆØąØŠ Ų…ØšØ§ŲŠŲ†ØŠ", "setting_image_viewer_title": "Ø§Ų„ØĩŲˆØą", "setting_languages_apply": "ØĒØēŲŠŲŠØą Ø§Ų„ØĨؚداداØĒ", - "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "Ø§Ų„Ų„ØēاØĒ", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", "setting_notifications_notify_immediately": "؁؊ Ø§Ų„Ø­Ø§Ų„", - "setting_notifications_notify_minutes": "{} minutes", "setting_notifications_notify_never": "ØŖØ¨Ø¯Ø§Ų‹", - "setting_notifications_notify_seconds": "{} seconds", "setting_notifications_single_progress_subtitle": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒ؁ØĩŲŠŲ„ŲŠØŠ ØĒØ­Ų…ŲŠŲ„ Ų„ŲƒŲ„ ØŖØĩŲ„", "setting_notifications_single_progress_title": "ØĨØ¸Ų‡Ø§Øą ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", "setting_notifications_subtitle": "اØļØ¨Øˇ ØĒ؁ØļŲŠŲ„Ø§ØĒ Ø§Ų„ØĨØŽØˇØ§Øą", "setting_notifications_total_progress_subtitle": "Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØšØ§Ų… (ØĒŲ… Ø§Ų„Ų‚ŲŠØ§Ų… Ø¨Ų‡/ØĨØŦŲ…Ø§Ų„ŲŠ Ø§Ų„ØŖØĩŲˆŲ„)", "setting_notifications_total_progress_title": "ØĨØ¸Ų‡Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„Ų…Ø­ØąØ˛", "setting_video_viewer_looping_title": "ØĒŲƒØąØ§Øą Ų…Ų‚ØˇØš ŲŲŠØ¯ŲŠŲˆ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Ø§Ų„ØĨؚداداØĒ", "settings_require_restart": "ŲŠØąØŦŲ‰ ØĨؚاد؊ ØĒØ´ØēŲŠŲ„ Ų„ØĒØˇØ¨ŲŠŲ‚ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد", "settings_saved": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„ØĨؚداداØĒ", + "setup_pin_code": "ØĒØ­Ø¯ŲŠØ¯ ØąŲ‚Ų… ØŗØąŲŠ", "share": "Ų…Ø´Ø§ØąŲƒØŠ", "share_add_photos": "ØĨØļØ§ŲØŠ Ø§Ų„ØĩŲˆØą", - "share_assets_selected": "{} selected", "share_dialog_preparing": "ØĒØ­ØļŲŠØą...", "shared": "Ų…ŲØ´ØĒŲŽØąŲƒ", "shared_album_activities_input_disable": "Ø§Ų„ØĒØšŲ„ŲŠŲ‚ Ų…ØšØˇŲ„", @@ -1617,40 +1478,22 @@ "shared_by_user": "ØĒŲ…ØĒ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ø¨ŲˆØ§ØŗØˇØŠ {user}", "shared_by_you": "ØĒŲ…ØĒ Ų…Ø´Ø§ØąŲƒØĒŲ‡ Ų…Ų† Ų‚ŲØ¨Ų„Ųƒ", "shared_from_partner": "ØĩŲˆØą Ų…Ų† {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "ØąŲˆØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒØŠ", "shared_link_clipboard_copied_massage": "Ų†ØŗØŽ ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", "shared_link_create_error": "ØŽØˇØŖ ØŖØĢŲ†Ø§ØĄ ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒ", "shared_link_edit_description_hint": "ØŖØ¯ØŽŲ„ ؈Øĩ؁ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", "shared_link_edit_expire_after_option_day": "ŲŠŲˆŲ… 1", - "shared_link_edit_expire_after_option_days": "{} days", "shared_link_edit_expire_after_option_hour": "1 ØŗØ§ØšØŠ", - "shared_link_edit_expire_after_option_hours": "{} hours", "shared_link_edit_expire_after_option_minute": "1 Ø¯Ų‚ŲŠŲ‚ØŠ", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", "shared_link_edit_password_hint": "ØŖØ¯ØŽŲ„ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", "shared_link_edit_submit_button": "ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØąØ§Ø¨Øˇ", "shared_link_error_server_url_fetch": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŦŲ„Ø¨ ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŽØ§Ø¯Ų…", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", "shared_link_expires_never": "ØĒŲ†ØĒŲ‡ŲŠ ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØąŲˆØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŠ", "shared_link_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "shared_links": "ØąŲˆØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒØŠ", "shared_links_description": "؈Øĩ؁ Ø§Ų„ØąŲˆØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŠ", "shared_photos_and_videos_count": "{assetCount, plural, other {# Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲØ´Ø§ØąŲŽŲƒØŠ.}}", - "shared_with_me": "Shared with me", "shared_with_partner": "ØĒŲ…ØĒ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…Øš {partner}", "sharing": "Ų…Ø´Ø§ØąŲƒØŠ", "sharing_enter_password": "Ø§Ų„ØąØŦØ§ØĄ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ų„ØšØąØļ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲØ­ØŠ.", @@ -1726,9 +1569,6 @@ "support_third_party_description": "ØĒŲ… Ø­Ø˛Ų… ØĒØĢØ¨ŲŠØĒ immich Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ø¨ŲˆØ§ØŗØˇØŠ ØŦŲ‡ØŠ ØŽØ§ØąØŦŲŠØŠ. Ų‚Ø¯ ØĒŲƒŲˆŲ† Ø§Ų„Ų…Ø´ŲƒŲ„Ø§ØĒ Ø§Ų„ØĒ؊ ØĒŲˆØ§ØŦŲ‡Ų‡Ø§ Ų†Ø§ØŦŲ…ØŠ ØšŲ† Ų‡Ø°Ų‡ Ø§Ų„Ø­Ø˛Ų…ØŠØŒ Ų„Ø°Ø§ ŲŠØąØŦŲ‰ ØˇØąØ­ Ø§Ų„Ų…Ø´ŲƒŲ„Ø§ØĒ Ų…ØšŲ‡Ų… ؁؊ Ø§Ų„Ų…Ų‚Ø§Ų… Ø§Ų„ØŖŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØąŲˆØ§Ø¨Øˇ ØŖØ¯Ų†Ø§Ų‡.", "swap_merge_direction": "ØĒØ¨Ø¯ŲŠŲ„ اØĒØŦØ§Ų‡ Ø§Ų„Ø¯Ų…ØŦ", "sync": "Ų…Ø˛Ø§Ų…Ų†ØŠ", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Ø§Ų„ØšŲ„Ø§Ų…ØŠ", "tag_assets": "ØŖØĩŲˆŲ„ Ø§Ų„ØšŲ„Ø§Ų…ØŠ", "tag_created": "ØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØšŲ„Ø§Ų…ØŠ: {tag}", @@ -1743,14 +1583,8 @@ "theme_selection": "ا؎ØĒŲŠØ§Øą Ø§Ų„ØŗŲ…ØŠ", "theme_selection_description": "Ų‚Ų… بØĒØšŲŠŲŠŲ† Ø§Ų„ØŗŲ…ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ØšŲ„Ų‰ Ø§Ų„Ų„ŲˆŲ† Ø§Ų„ŲØ§ØĒØ­ ØŖŲˆ Ø§Ų„Ø¯Ø§ŲƒŲ† Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ ØĒ؁ØļŲŠŲ„Ø§ØĒ Ų†Ø¸Ø§Ų… Ø§Ų„Ų…ØĒØĩŲØ­ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "theme_setting_asset_list_storage_indicator_title": "ØšØąØļ Ų…Ø¤Ø´Øą Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø¨Ų„Ø§Øˇ Ø§Ų„ØŖØĩŲˆŲ„", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", "theme_setting_image_viewer_quality_subtitle": "اØļØ¨Øˇ ØŦŲˆØ¯ØŠ ØšØ§ØąØļ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØĒ؁ØĩŲŠŲ„ŲŠØŠ", "theme_setting_image_viewer_quality_title": "ØŦŲˆØ¯ØŠ ØšØ§ØąØļ Ø§Ų„ØĩŲˆØąØŠ", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "ØĒŲ„Ų‚Ø§ØĻ؊ (اØĒبؚ ØĨؚداد Ø§Ų„Ų†Ø¸Ø§Ų…)", "theme_setting_theme_subtitle": "ا؎ØĒØą ØĨؚداداØĒ Ų…Ø¸Ų‡Øą Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "theme_setting_three_stage_loading_subtitle": "Ų‚Ø¯ ŲŠØ˛ŲŠØ¯ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ Ų…Ų† ØĢŲ„Ø§ØĢ Ų…ØąØ§Ø­Ų„ Ų…Ų† ØŖØ¯Ø§ØĄ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ ŲˆŲ„ŲƒŲ†Ų‡ ŲŠØŗØ¨Ø¨ ØĒØ­Ų…ŲŠŲ„ Ø´Ø¨ŲƒØŠ ØŖØšŲ„Ų‰ Ø¨ŲƒØĢŲŠØą", @@ -1774,17 +1608,16 @@ "trash_all": "Ų†Ų‚Ų„ Ø§Ų„ŲƒŲ„ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_count": "ØŗŲ„ØŠ Ø§Ų„Ų…Ø­Ų…Ų„Ø§ØĒ {count, number}", "trash_delete_asset": "Ø­Ø°Ų/Ų†Ų‚Ų„ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", - "trash_emptied": "Emptied trash", "trash_no_results_message": "ØŗØĒØ¸Ų‡Øą Ų‡Ų†Ø§ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠ.", "trash_page_delete_all": "Ø­Ø°Ų Ø§Ų„ŲƒŲ„", "trash_page_empty_trash_dialog_content": "Ų‡Ų„ ØĒØąŲŠØ¯ ØĒŲØąŲŠØē ØŖØĩŲˆŲ„Ųƒ Ø§Ų„Ų…Ų‡Ų…Ų„ØŠØŸ ØŗØĒØĒŲ… ØĨØ˛Ø§Ų„ØŠ Ų‡Ø°Ų‡ Ø§Ų„ØšŲ†Ø§ØĩØą Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§ Ų…Ų† Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", - "trash_page_info": "Trashed items will be permanently deleted after {} days", "trash_page_no_assets": "Ų„Ø§ ØĒ؈ØŦد اØĩŲˆŲ„ ؁؊ ØŗŲ„Ų‡ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_page_restore_all": "Ø§ØŗØĒؚاد؊ Ø§Ų„ŲƒŲ„", "trash_page_select_assets_btn": "ا؎ØĒØą Ø§Ų„ØŖØĩŲˆŲ„ ", - "trash_page_title": "Trash ({})", "trashed_items_will_be_permanently_deleted_after": "ØŗŲŠØĒŲ… Ø­Ø°ŲŲ Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠ Ų†ŲŲ‡Ø§ØĻŲŠŲ‹Ø§ بؚد {days, plural, one {# ŲŠŲˆŲ…} other {# ØŖŲŠØ§Ų… }}.", "type": "Ø§Ų„Ų†ŲˆØš", + "unable_to_change_pin_code": "ØĒŲŲŠŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ ØēŲŠØą Ų…Ų…ŲƒŲ†", + "unable_to_setup_pin_code": "Ø§Ų†Ø´Ø§ØĄ Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ ØēŲŠØą Ų…Ų…ŲƒŲ†", "unarchive": "ØŖØŽØąØŦ Ų…Ų† Ø§Ų„ØŖØąØ´ŲŠŲ", "unarchived_count": "{count, plural, other {ØēŲŠØą Ų…Ø¤ØąØ´ŲØŠ #}}", "unfavorite": "ØŖØ˛Ų„ Ø§Ų„ØĒ؁ØļŲŠŲ„", @@ -1820,15 +1653,14 @@ "upload_status_errors": "Ø§Ų„ØŖØŽØˇØ§ØĄ", "upload_status_uploaded": "ØĒŲ… Ø§Ų„ØąŲØš", "upload_success": "ØĒŲ… Ø§Ų„ØąŲØš Ø¨Ų†ØŦاح، Ų‚Ų… بØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĩŲØ­ØŠ Ų„ØąØ¤ŲŠØŠ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…ØąŲŲˆØšØŠ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", "url": "ØšŲ†ŲˆØ§Ų† URL", "usage": "Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų…", - "use_current_connection": "use current connection", "use_custom_date_range": "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ų†ØˇØ§Ų‚ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„Ų…ØŽØĩØĩ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† Ø°Ų„Ųƒ", "user": "Ų…ØŗØĒØŽØ¯Ų…", "user_id": "Ų…ØšØąŲ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_liked": "Ų‚Ø§Ų… {user} Ø¨Ø§Ų„ØĨØšØŦاب {type, select, photo {Ø¨Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØąØŠ} video {Ø¨Ų‡Ø°Ø§ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ} asset {Ø¨Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {Ø¨Ų‡Ø§}}", + "user_pin_code_settings": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", + "user_pin_code_settings_description": "ØĒØēŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", "user_purchase_settings": "Ø§Ų„Ø´ØąØ§ØĄ", "user_purchase_settings_description": "ØĨØ¯Ø§ØąØŠ ØšŲ…Ų„ŲŠØŠ Ø§Ų„Ø´ØąØ§ØĄ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", "user_role_set": "Ų‚Ų… بØĒØšŲŠŲŠŲ† {user} ŲƒŲ€ {role}", @@ -1839,7 +1671,6 @@ "users": "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†", "utilities": "ØŖØ¯ŲˆØ§ØĒ", "validate": "ØĒØ­Ų‚Ų’Ų‚", - "validate_endpoint_error": "Please enter a valid URL", "variables": "Ø§Ų„Ų…ØĒØēŲŠØąØ§ØĒ", "version": "Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_announcement_closing": "ØĩØ¯ŲŠŲ‚ŲƒØŒ ØŖŲ„ŲŠŲƒØŗ", @@ -1847,7 +1678,6 @@ "version_announcement_overlay_release_notes": "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_announcement_overlay_text_1": "Ų…ØąØ­Ø¨Ų‹Ø§ ŲŠØ§ ØĩØ¯ŲŠŲ‚ŲŠ ، Ų‡Ų†Ø§Ųƒ ØĨØĩØ¯Ø§Øą ØŦØ¯ŲŠØ¯", "version_announcement_overlay_text_2": "Ų…Ų† ؁ØļŲ„Ųƒ ؎ذ ŲˆŲ‚ØĒ؃ Ų„Ø˛ŲŠØ§ØąØŠ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", "version_announcement_overlay_title": "Ų†ØŗØŽŲ‡ ØŦØ¯ŲŠØ¯Ų‡ Ų…ØĒØ§Ø­Ų‡ Ų„Ų„ØŽØ§Ø¯Ų… ", "version_history": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_history_item": "ØĒŲ… ØĒØĢØ¨ŲŠØĒ {version} ؁؊ {date}", diff --git a/i18n/az.json b/i18n/az.json index fe53041b69..0b787b317f 100644 --- a/i18n/az.json +++ b/i18n/az.json @@ -76,7 +76,6 @@ "library_watching_settings_description": "Dəyişdirilən fayllarÄą avtomatik olaraq yoxla", "logging_enable_description": "JurnalÄą aktivləşdir", "logging_level_description": "Aktiv edildikdə hansÄą jurnal səviyyəsi istifadə olunur.", - "logging_settings": "", "machine_learning_clip_model": "CLIP modeli", "machine_learning_clip_model_description": "Buradaqeyd olunan CLIP modelinin adÄą. Modeli dəyişdirdikdən sonra bÃŧtÃŧn şəkillər ÃŧçÃŧn 'AğıllÄą AxtarÄąÅŸ' funksiyasÄąnÄą yenidən işə salmalÄąsÄąnÄąz.", "machine_learning_duplicate_detection": "Dublikat Aşkarlama", diff --git a/i18n/be.json b/i18n/be.json index 8377ec5383..b8898d8aaf 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -4,6 +4,7 @@ "account_settings": "НаĐģĐ°Đ´Ņ‹ ŅžĐģŅ–ĐēĐžĐ˛Đ°ĐŗĐ° СаĐŋŅ–ŅŅƒ", "acknowledge": "ĐŸĐ°Ņ†Đ˛ĐĩŅ€Đ´ĐˇŅ–Ņ†ŅŒ", "action": "ДзĐĩŅĐŊĐŊĐĩ", + "action_common_update": "АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ", "actions": "ДзĐĩŅĐŊĐŊŅ–", "active": "АĐēŅ‚Ņ‹ŅžĐŊŅ‹", "activity": "АĐēŅ‚Ņ‹ŅžĐŊĐ°ŅŅ†ŅŒ", @@ -13,6 +14,7 @@ "add_a_location": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐŧĐĩŅŅ†Đ°", "add_a_name": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ–ĐŧŅ", "add_a_title": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐˇĐ°ĐŗĐ°ĐģОваĐē", + "add_endpoint": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐēŅ€ĐžĐŋĐē҃ Đ´ĐžŅŅ‚ŅƒĐŋ҃", "add_exclusion_pattern": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đ˛Ņ‹ĐēĐģŅŽŅ‡ŅĐŊĐŊŅ", "add_import_path": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Ņƒ", "add_location": "Đ”Đ°Đ´Đ°ĐšŅ†Đĩ ĐŧĐĩŅŅ†Đ°", @@ -20,8 +22,10 @@ "add_partner": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "add_path": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҈ĐģŅŅ…", "add_photos": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ„ĐžŅ‚Đ°", - "add_to": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃...", + "add_to": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃â€Ļ", "add_to_album": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ аĐģŅŒĐąĐžĐŧ", + "add_to_album_bottom_sheet_added": "ДададзĐĩĐŊа да {album}", + "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐž СĐŊĐ°Ņ…ĐžĐ´ĐˇŅ–Ņ†Ņ†Đ° Ņž {album}", "add_to_shared_album": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ Đ°ĐŗŅƒĐģҌĐŊŅ‹ аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”Đ°Đ´Đ°Ņ†ŅŒ URL", "added_to_archive": "ДададзĐĩĐŊа Ņž Đ°Ņ€Ņ…Ņ–Ņž", @@ -39,8 +43,9 @@ "backup_database_enable_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Ņ€ŅĐˇĐĩŅ€Đ˛Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅", "backup_keep_last_amount": "КоĐģҌĐēĐ°ŅŅ†ŅŒ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅ–Ņ… Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Ņ‹Ņ… ĐēĐžĐŋŅ–Đš Đ´ĐģŅ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊĐŊŅ", "backup_settings": "НаĐģĐ°Đ´Ņ‹ Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅ–ŅĐ˛Đ°ĐŊĐŊŅ", - "backup_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадĐēаĐŧŅ– Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅ–ŅĐ˛Đ°ĐŊĐŊŅ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅", + "backup_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– даĐŧĐŋа ĐąĐ°ĐˇŅ‹ дадСĐĩĐŊҋ҅. Đ—Đ°ŅžĐ˛Đ°ĐŗĐ°: ĐŗŅŅ‚Ņ‹Ņ ĐˇĐ°Đ´Đ°Ņ‡Ņ‹ ĐŊĐĩ ĐēаĐŊŅ‚Ņ€Đ°ĐģŅŽŅŽŅ†Ņ†Đ°, Ņ– Ņž Đ˛Ņ‹ĐŋадĐē҃ ĐŊŅŅžĐ´Đ°Ņ‡Ņ‹ ĐŋавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ адĐŋŅ€Đ°ŅžĐģĐĩĐŊа ĐŊĐĩ ĐąŅƒĐ´ĐˇĐĩ.", "check_all": "ĐŸŅ€Đ°Đ˛ĐĩŅ€Ņ‹Ņ†ŅŒ ҃ҁĐĩ", + "cleanup": "ĐŅ‡Ņ‹ŅŅ‚Đēа", "cleared_jobs": "ĐŅ‡Ņ‹ŅˆŅ‡Đ°ĐŊŅ‹ СадаĐŊĐŊŅ– Đ´ĐģŅ: {job}", "config_set_by_file": "КаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ Ņž ĐˇĐ°Ņ€Đ°Đˇ ŅƒŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊа ĐŋŅ€Đ°Đˇ Ņ„Đ°ĐšĐģ ĐēаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ–", "confirm_delete_library": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ {library} ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃?", @@ -58,8 +63,18 @@ "external_library_created_at": "ЗĐŊĐĩ҈ĐŊŅŅ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēа (ŅŅ‚Đ˛ĐžŅ€Đ°ĐŊа {date})", "external_library_management": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ СĐŊĐĩ҈ĐŊŅĐš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēаК", "face_detection": "Đ’Ņ‹ŅŅžĐģĐĩĐŊĐŊĐĩ Ņ‚Đ˛Đ°Ņ€Đ°Ņž", + "face_detection_description": "Đ’Ņ‹ŅŅžĐģŅŅ†ŅŒ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐŊа Ņ„ĐžŅ‚Đ°ĐˇĐ´Ņ‹ĐŧĐēĐ°Ņ… Ņ– Đ˛Ņ–Đ´ŅĐ° С даĐŋаĐŧĐžĐŗĐ°Đš ĐŧĐ°ŅˆŅ‹ĐŊĐŊĐ°ĐŗĐ° ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊŅ. ДĐģŅ Đ˛Ņ–Đ´ŅĐ° ŅžĐģŅ–Ņ‡Đ˛Đ°ĐĩŅ†Ņ†Đ° Ņ‚ĐžĐģҌĐēŅ– ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€Đ°. \"АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ\" (ĐŋĐĩŅ€Đ°)аĐŋŅ€Đ°Ņ†ĐžŅžĐ˛Đ°Đĩ ŅžŅĐĩ ĐŧĐĩĐ´Ņ‹Ņ. \"ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ\" Đ´Đ°Đ´Đ°Ņ‚ĐēОва Đ°Ņ‡Ņ‹ŅˆŅ‡Đ°Đĩ ŅžŅĐĩ ĐąŅĐŗŅƒŅ‡Ņ‹Ņ дадСĐĩĐŊŅ‹Ņ ĐŋŅ€Đ° Ņ‚Đ˛Đ°Ņ€Ņ‹. \"ĐĐ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ\" ŅŅ‚Đ°Đ˛Ņ–Ņ†ŅŒ ҃ Ņ‡Đ°Ņ€ĐŗŅƒ ĐŧĐĩĐ´Ņ‹Ņ, ŅĐēŅ–Ņ ŅŅˆŅ‡Ņ ĐŊĐĩ ĐąŅ‹ĐģŅ– аĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°ĐŊŅ‹Ņ. Đ’Ņ‹ŅŅžĐģĐĩĐŊŅ‹Ņ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐąŅƒĐ´ŅƒŅ†ŅŒ ĐŋĐ°ŅŅ‚Đ°ŅžĐģĐĩĐŊŅ‹ Ņž Ņ‡Đ°Ņ€ĐŗŅƒ Đ´ĐģŅ Ņ€Đ°ŅĐŋаСĐŊаваĐŊĐŊŅ Đ°ŅĐžĐą ĐŋĐ°ŅĐģŅ ĐˇĐ°Đ˛ŅŅ€ŅˆŅĐŊĐŊŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ Ņ‚Đ˛Đ°Ņ€Đ°Ņž, С ĐŗŅ€ŅƒĐŋаваĐŊĐŊĐĩĐŧ Ņ–Ņ… Đŋа ҖҁĐŊŅƒŅŽŅ‡Ņ‹Ņ… айО ĐŊĐžĐ˛Ņ‹Ņ… ĐģŅŽĐ´ĐˇŅŅ….", + "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋĐ°Đ˛Đ°Ņ†ŅŒ Đ˛Ņ‹ŅŅžĐģĐĩĐŊŅ‹Ņ Ņ‚Đ˛Đ°Ņ€Ņ‹ Đŋа Đ°ŅĐžĐąĐ°Ņ…. Đ“ŅŅ‚Ņ‹ ŅŅ‚Đ°Đŋ Đ˛Ņ‹ĐēĐžĐŊваĐĩŅ†Ņ†Đ° ĐŋĐ°ŅĐģŅ ĐˇĐ°Đ˛ŅŅ€ŅˆŅĐŊĐŊŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ Ņ‚Đ˛Đ°Ņ€Đ°Ņž. \"ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ\" (ĐŋĐ°ŅžŅ‚ĐžŅ€ĐŊа) ĐŋĐĩŅ€Đ°ĐŗŅ€ŅƒĐŋĐžŅžĐ˛Đ°Đĩ ŅžŅĐĩ Ņ‚Đ˛Đ°Ņ€Ņ‹. \"ĐĐ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ\" ŅŅ‚Đ°Đ˛Ņ–Ņ†ŅŒ ҃ Ņ‡Đ°Ņ€ĐŗŅƒ Ņ‚Đ˛Đ°Ņ€Ņ‹, ŅĐēŅ–Ņ ŅŅˆŅ‡Ņ ĐŊĐĩ ĐŋҀҋĐŋŅ–ŅĐ°ĐŊŅ‹Ņ да ŅĐēОК-ĐŊĐĩĐąŅƒĐ´ĐˇŅŒ Đ°ŅĐžĐąŅ‹.", + "failed_job_command": "КаĐŧаĐŊда {command} ĐŊĐĩ Đ˛Ņ‹ĐēаĐŊаĐģĐ°ŅŅ Đ´ĐģŅ СадаĐŊĐŊŅ: {job}", "force_delete_user_warning": "ĐŸĐĐŸĐ¯Đ Đ­Đ”Đ–ĐĐĐĐ•: Đ“ŅŅ‚Đ° дСĐĩŅĐŊĐŊĐĩ ĐŊĐĩадĐēĐģадĐŊа Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа Ņ– ŅžŅĐĩ ай'ĐĩĐē҂ҋ. Đ“ŅŅ‚Đ° дСĐĩŅĐŊĐŊĐĩ ĐŊĐĩ ĐŧĐžĐļа ĐąŅ‹Ņ†ŅŒ Đ°Đ´Ņ€ĐžĐąĐģĐĩĐŊа Ņ– Ņ„Đ°ĐšĐģŅ‹ ĐŊĐĩĐŧĐ°ĐŗŅ‡Ņ‹Đŧа ĐąŅƒĐ´ĐˇĐĩ адĐŊĐ°Đ˛Ņ–Ņ†ŅŒ.", + "forcing_refresh_library_files": "ĐŸŅ€Ņ‹ĐŧŅƒŅĐžĐ˛Đ°Đĩ айĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ŅžŅŅ–Ņ… Ņ„Đ°ĐšĐģĐ°Ņž ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–", "image_format": "Đ¤Đ°Ņ€ĐŧĐ°Ņ‚", + "image_format_description": "WebP ŅŅ‚Đ˛Đ°Ņ€Đ°Đĩ ĐŧĐĩĐŊŅˆŅ‹Ņ Ņ„Đ°ĐšĐģŅ‹, ҇ҋĐŧ JPEG, аĐģĐĩ ĐŋавОĐģҌĐŊĐĩĐš ĐēĐ°Đ´ŅƒĐĩ.", + "image_fullsize_description": "Đ’Ņ‹ŅĐ˛Đ° Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ ĐąĐĩС ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊҋ҅, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŋҀҋ ĐŋавĐĩĐģŅ–Ņ‡ŅĐŊĐŊŅ–", + "image_fullsize_enabled": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ŅŅ‚Đ˛Đ°Ņ€ŅĐŊĐŊĐĩ Đ˛Ņ‹ŅĐ˛Ņ‹ Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ", + "image_fullsize_enabled_description": "ĐĄŅ‚Đ˛Đ°Ņ€Đ°Ņ†ŅŒ Đ˛Ņ‹ŅĐ˛Ņƒ Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ Đ´ĐģŅ Ņ„Đ°Ņ€ĐŧĐ°Ņ‚Đ°Ņž, ŅˆŅ‚Đž ĐŊĐĩ ĐŋŅ€Ņ‹Đ´Đ°Ņ‚ĐŊŅ‹Ņ Đ´ĐģŅ Đ˛ŅĐą. КаĐģŅ– ŅžĐēĐģŅŽŅ‡Đ°ĐŊа ĐžĐŋŅ†Ņ‹Ņ \"ĐĐ´Đ´Đ°Đ˛Đ°Ņ†ŅŒ ĐŋĐĩŅ€Đ°Đ˛Đ°ĐŗŅƒ ŅžĐąŅƒĐ´Đ°Đ˛Đ°ĐŊаК ĐŋŅ€Đ°ŅĐ˛Đĩ\", ĐŋŅ€Đ°ĐŗĐģŅĐ´Ņ‹ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ŅŽŅ†Ņ†Đ° ĐŊĐĩĐŋĐ°ŅŅ€ŅĐ´ĐŊа ĐąĐĩС ĐēаĐŊвĐĩŅ€Ņ‚Đ°Ņ†Ņ‹Ņ–. НĐĩ ŅžĐŋĐģŅ‹Đ˛Đ°Đĩ ĐŊа Đ˛ŅĐą-ĐŋŅ€Ņ‹Đ´Đ°Ņ‚ĐŊŅ‹Ņ Ņ„Đ°Ņ€ĐŧĐ°Ņ‚Ņ‹, Ņ‚Đ°ĐēŅ–Ņ ŅĐē JPEG.", + "image_fullsize_quality_description": "Đ¯ĐēĐ°ŅŅ†ŅŒ Đ˛Ņ‹ŅĐ˛Ņ‹ Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ ад 1 да 100. БоĐģҌ҈ Đ˛Ņ‹ŅĐžĐēаĐĩ СĐŊĐ°Ņ‡ŅĐŊĐŊĐĩ ĐģĐĩĐŋŅˆĐ°Đĩ, аĐģĐĩ ĐŋŅ€Ņ‹Đ˛ĐžĐ´ĐˇŅ–Ņ†ŅŒ да ĐŋавĐĩĐģŅ–Ņ‡ŅĐŊĐŊŅ ĐŋаĐŧĐĩŅ€Ņƒ Ņ„Đ°ĐšĐģа.", + "image_fullsize_title": "НаĐģĐ°Đ´Ņ‹ Đ˛Ņ‹ŅĐ˛Ņ‹ Ņž ĐŋĐžŅžĐŊŅ‹Đŧ ĐŋаĐŧĐĩҀҋ", "image_preview_title": "НаĐģĐ°Đ´Ņ‹ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐŗĐ° ĐŋŅ€Đ°ĐŗĐģŅĐ´Ņƒ", "image_quality": "Đ¯ĐēĐ°ŅŅ†ŅŒ", "image_resolution": "Đ Đ°ĐˇĐ´ĐˇŅĐģŅĐģҌĐŊĐ°ŅŅ†ŅŒ", diff --git a/i18n/bg.json b/i18n/bg.json index e9e72743e3..7f0fca55d6 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -183,20 +183,13 @@ "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ", "oauth_auto_register_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŊОви ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ҁĐģĐĩĐ´ вĐģиСаĐŊĐĩ ҁ OAuth", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐŊа ĐąŅƒŅ‚ĐžĐŊа", - "oauth_client_id": "КĐģиĐĩĐŊ҂ҁĐēи ID", - "oauth_client_secret": "КĐģиĐĩĐŊ҂ҁĐēа Ņ‚Đ°ĐšĐŊа", "oauth_enable_description": "ВĐģиСаĐŊĐĩ ҁ OAuth", - "oauth_issuer_url": "URL ĐŊа Đ¸ĐˇĐ´Đ°Ņ‚ĐĩĐģŅ", "oauth_mobile_redirect_uri": "URI Са ĐŧОйиĐģĐŊĐž ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ", "oauth_mobile_redirect_uri_override": "URI ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ Са ĐŧОйиĐģĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "oauth_mobile_redirect_uri_override_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đēа Са OAuth ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊĐĩ ĐŋОСвОĐģŅĐ˛Đ° Са ĐŧОйиĐģĐŊи URI идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸, ĐēĐ°Ņ‚Đž '{callback}'", - "oauth_profile_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚ŅŠĐŧ Са ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžŅ„Đ¸Đģи", - "oauth_profile_signing_algorithm_description": "АĐģĐŗĐžŅ€Đ¸Ņ‚ŅŠĐŧ иСĐŋĐžĐģСваĐŊ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēи ĐŋŅ€ĐžŅ„Đ¸Đģ.", - "oauth_scope": "ОбĐģĐ°ŅŅ‚/ĐžĐąŅ…Đ˛Đ°Ņ‚ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ", "oauth_settings": "OAuth", "oauth_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ˛Ņ…ĐžĐ´ ҁ OAuth", "oauth_settings_more_details": "За ĐŋОвĐĩ҇Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģĐŊĐžŅŅ‚Ņ‚Đ°, ҁĐĩ ĐŋĐžŅ‚ŅŠŅ€ŅĐĩŅ‚Đĩ в docs.", - "oauth_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚ŅŠĐŧ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ", "oauth_storage_label_claim": "Đ—Đ°ŅĐ˛Đēа Са ĐĩŅ‚Đ¸ĐēĐĩŅ‚ Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ", "oauth_storage_label_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐˇĐ°Đ´Đ°ĐšŅ‚Đĩ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ҁҊҁ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Ņ‚Đ° ĐžŅ‚ Ņ‚Đ°ĐˇĐ¸ ĐˇĐ°ŅĐ˛Đēа.", "oauth_storage_quota_claim": "Đ—Đ°ŅĐ˛Đēа Са ĐēĐ˛ĐžŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ", @@ -553,7 +546,6 @@ "direction": "ĐŸĐžŅĐžĐēа", "disabled": "ИСĐēĐģŅŽŅ‡ĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąŅ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩдаĐēŅ†Đ¸Đ¸Ņ‚Đĩ", - "discord": "Discord", "discover": "ĐžŅ‚ĐēŅ€Đ¸Đš", "dismiss_all_errors": "ĐžŅ‚Ņ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ĐŗŅ€Đĩ҈Đēи", "dismiss_error": "ĐžŅ‚Ņ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа ĐŗŅ€Đĩ҈Đēа", @@ -734,7 +726,6 @@ "unable_to_update_user": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ", "unable_to_upload_file": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐšĐģ" }, - "exif": "Exif", "exit_slideshow": "Đ˜ĐˇŅ…ĐžĐ´ ĐžŅ‚ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒŅ‚Đž", "expand_all": "Đ Đ°ĐˇŅˆĐ¸Ņ€Đ¸ Đ˛ŅĐ¸Ņ‡Đēи", "expire_after": "Đ˜ĐˇŅ‚Đ¸Ņ‡Đ° ҁĐģĐĩĐ´", @@ -926,7 +917,6 @@ "notification_toggle_setting_description": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа иĐŧĐĩĐšĐģ иСвĐĩŅŅ‚Đ¸Ņ", "notifications": "ИСвĐĩŅŅ‚Đ¸Ņ", "notifications_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа иСвĐĩŅŅ‚Đ¸ŅŅ‚Đ°", - "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģĐŊа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Immich", "offline": "ĐžŅ„ĐģаКĐŊ", "offline_paths": "ĐžŅ„ĐģаКĐŊ ĐŋŅŠŅ‚Đ¸Ņ‰Đ°", @@ -1013,7 +1003,7 @@ "public_share": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊĐž ҁĐŋОдĐĩĐģŅĐŊĐĩ", "purchase_account_info": "ĐŸĐžĐ´Đ´Ņ€ŅŠĐļĐŊиĐē", "purchase_activated_subtitle": "БĐģĐ°ĐŗĐžĐ´Đ°Ņ€Đ¸Đŧ ви, ҇Đĩ ĐŋОдĐēŅ€ĐĩĐŋŅŅ‚Đĩ Immich и ŅĐžŅ„Ņ‚ŅƒĐĩŅ€Đ° ҁ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊ ĐēОд", - "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ ĐŊа {date, date}", + "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ ĐŊа {date}", "purchase_activated_title": "Đ’Đ°ŅˆĐ¸ŅŅ‚ ĐēĐģŅŽŅ‡ ĐąĐĩ҈Đĩ ҃ҁĐŋĐĩ҈ĐŊĐž аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ", "purchase_button_activate": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Đš", "purchase_button_buy": "ĐšŅƒĐŋи", @@ -1323,7 +1313,6 @@ "upload_status_errors": "Đ“Ņ€Đĩ҈Đēи", "upload_status_uploaded": "ĐšĐ°Ņ‡ĐĩĐŊĐž", "upload_success": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ҃ҁĐŋĐĩ҈ĐŊĐž, ĐžĐŋŅ€ĐĩҁĐŊĐĩŅ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ°, Са да Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Đ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ.", - "url": "URL", "usage": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊиĐĩ", "use_custom_date_range": "ИСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊ диаĐŋаСОĐŊ ĐžŅ‚ Đ´Đ°Ņ‚Đ¸ вĐŧĐĩŅŅ‚Đž Ņ‚ĐžĐ˛Đ°", "user": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", diff --git a/i18n/bi.json b/i18n/bi.json index a70ce05dfb..fff8196e75 100644 --- a/i18n/bi.json +++ b/i18n/bi.json @@ -3,8 +3,6 @@ "account": "Akaont", "account_settings": "Seting blo Akaont", "acknowledge": "Akcept", - "action": "", - "actions": "", "active": "Stap Mekem", "activity": "Wanem hemi Mekem", "activity_changed": "WAnem hemi Mekem hemi", @@ -16,850 +14,5 @@ "add_exclusion_pattern": "Putem wan paten wae hemi karem aot", "add_import_path": "Putem wan pat blo import", "add_location": "Putem wan place blo hem", - "add_more_users": "Putem mor man", - "add_partner": "", - "add_path": "", - "add_photos": "", - "add_to": "", - "add_to_album": "", - "add_to_shared_album": "", - "admin": { - "add_exclusion_pattern_description": "", - "authentication_settings": "", - "authentication_settings_description": "", - "background_task_job": "", - "check_all": "", - "config_set_by_file": "", - "confirm_delete_library": "", - "confirm_delete_library_assets": "", - "confirm_email_below": "", - "confirm_reprocess_all_faces": "", - "confirm_user_password_reset": "", - "disable_login": "", - "duplicate_detection_job_description": "", - "exclusion_pattern_description": "", - "external_library_created_at": "", - "external_library_management": "", - "face_detection": "", - "face_detection_description": "", - "facial_recognition_job_description": "", - "force_delete_user_warning": "", - "forcing_refresh_library_files": "", - "image_format_description": "", - "image_prefer_embedded_preview": "", - "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", - "image_prefer_wide_gamut_setting_description": "", - "image_quality": "", - "image_settings": "", - "image_settings_description": "", - "job_concurrency": "", - "job_not_concurrency_safe": "", - "job_settings": "", - "job_settings_description": "", - "job_status": "", - "jobs_delayed": "", - "jobs_failed": "", - "library_created": "", - "library_deleted": "", - "library_import_path_description": "", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", - "library_settings_description": "", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", - "machine_learning_clip_model": "", - "machine_learning_duplicate_detection": "", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled_description": "", - "machine_learning_facial_recognition": "", - "machine_learning_facial_recognition_description": "", - "machine_learning_facial_recognition_model": "", - "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", - "machine_learning_settings": "", - "machine_learning_settings_description": "", - "machine_learning_smart_search": "", - "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_concurrency": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", - "map_settings": "", - "map_settings_description": "", - "map_style_description": "", - "metadata_extraction_job": "", - "metadata_extraction_job_description": "", - "migration_job": "", - "migration_job_description": "", - "no_paths_added": "", - "no_pattern_added": "", - "note_apply_storage_label_previous_assets": "", - "note_cannot_be_changed_later": "", - "notification_email_from_address": "", - "notification_email_from_address_description": "", - "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", - "notification_email_ignore_certificate_errors_description": "", - "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", - "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", - "oauth_mobile_redirect_uri_override": "", - "oauth_mobile_redirect_uri_override_description": "", - "oauth_scope": "", - "oauth_settings": "", - "oauth_settings_description": "", - "oauth_signing_algorithm": "", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", - "oauth_storage_quota_default": "", - "oauth_storage_quota_default_description": "", - "offline_paths": "", - "offline_paths_description": "", - "password_enable_description": "", - "password_settings": "", - "password_settings_description": "", - "paths_validated_successfully": "", - "quota_size_gib": "", - "refreshing_all_libraries": "", - "repair_all": "", - "repair_matched_items": "", - "repaired_items": "", - "require_password_change_on_login": "", - "reset_settings_to_default": "", - "reset_settings_to_recent_saved": "", - "send_welcome_email": "", - "server_external_domain_settings": "", - "server_external_domain_settings_description": "", - "server_settings": "", - "server_settings_description": "", - "server_welcome_message": "", - "server_welcome_message_description": "", - "sidecar_job": "", - "sidecar_job_description": "", - "slideshow_duration_description": "", - "smart_search_job_description": "", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", - "storage_template_migration": "", - "storage_template_migration_job": "", - "storage_template_settings": "", - "storage_template_settings_description": "", - "system_settings": "", - "theme_custom_css_settings": "", - "theme_custom_css_settings_description": "", - "theme_settings": "", - "theme_settings_description": "", - "these_files_matched_by_checksum": "", - "thumbnail_generation_job": "", - "thumbnail_generation_job_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", - "transcoding_acceleration_qsv": "", - "transcoding_acceleration_rkmpp": "", - "transcoding_acceleration_vaapi": "", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", - "transcoding_audio_codec": "", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", - "transcoding_constant_quality_mode": "", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", - "transcoding_hardware_acceleration_description": "", - "transcoding_hardware_decoding": "", - "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_transcode_policy_description": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", - "transcoding_video_codec": "", - "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", - "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", - "untracked_files": "", - "untracked_files_description": "", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", - "user_management": "", - "user_password_has_been_reset": "", - "user_password_reset_description": "", - "user_settings": "", - "user_settings_description": "", - "user_successfully_removed": "", - "version_check_enabled_description": "", - "version_check_settings": "", - "version_check_settings_description": "", - "video_conversion_job": "", - "video_conversion_job_description": "" - }, - "admin_email": "", - "admin_password": "", - "administration": "", - "advanced": "", - "album_added": "", - "album_added_notification_setting_description": "", - "album_cover_updated": "", - "album_info_updated": "", - "album_name": "", - "album_options": "", - "album_updated": "", - "album_updated_setting_description": "", - "albums": "", - "albums_count": "", - "all": "", - "all_people": "", - "allow_dark_mode": "", - "allow_edits": "", - "api_key": "", - "api_keys": "", - "app_settings": "", - "appears_in": "", - "archive": "", - "archive_or_unarchive_photo": "", - "asset_offline": "", - "assets": "", - "authorized_devices": "", - "back": "", - "backward": "", - "blurred_background": "", - "camera": "", - "camera_brand": "", - "camera_model": "", - "cancel": "", - "cancel_search": "", - "cannot_merge_people": "", - "cannot_update_the_description": "", - "change_date": "", - "change_expiration_time": "", - "change_location": "", - "change_name": "", - "change_name_successfully": "", - "change_password": "", - "change_your_password": "", - "changed_visibility_successfully": "", - "check_all": "", - "check_logs": "", - "choose_matching_people_to_merge": "", - "city": "", - "clear": "", - "clear_all": "", - "clear_message": "", - "clear_value": "", - "close": "", - "collapse_all": "", - "color_theme": "", - "comment_options": "", - "comments_are_disabled": "", - "confirm": "", - "confirm_admin_password": "", - "confirm_delete_shared_link": "", - "confirm_password": "", - "contain": "", - "context": "", - "continue": "", - "copied_image_to_clipboard": "", - "copied_to_clipboard": "", - "copy_error": "", - "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", - "country": "", - "cover": "", - "covers": "", - "create": "", - "create_album": "", - "create_library": "", - "create_link": "", - "create_link_to_share": "", - "create_new_person": "", - "create_new_user": "", - "create_user": "", - "created": "", - "current_device": "", - "custom_locale": "", - "custom_locale_description": "", - "dark": "", - "date_after": "", - "date_and_time": "", - "date_before": "", - "date_range": "", - "day": "", - "default_locale": "", - "default_locale_description": "", - "delete": "", - "delete_album": "", - "delete_api_key_prompt": "", - "delete_key": "", - "delete_library": "", - "delete_link": "", - "delete_shared_link": "", - "delete_user": "", - "deleted_shared_link": "", - "description": "", - "details": "", - "direction": "", - "disabled": "", - "disallow_edits": "", - "discover": "", - "dismiss_all_errors": "", - "dismiss_error": "", - "display_options": "", - "display_order": "", - "display_original_photos": "", - "display_original_photos_setting_description": "", - "done": "", - "download": "", - "downloading": "", - "duration": "", - "edit_album": "", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", - "edit_key": "", - "edit_link": "", - "edit_location": "", - "edit_name": "", - "edit_people": "", - "edit_title": "", - "edit_user": "", - "edited": "", - "editor": "", - "email": "", - "empty_trash": "", - "enable": "", - "enabled": "", - "end_date": "", - "error": "", - "error_loading_image": "", - "errors": { - "cleared_jobs": "", - "exclusion_pattern_already_exists": "", - "failed_job_command": "", - "import_path_already_exists": "", - "paths_validation_failed": "", - "quota_higher_than_disk_size": "", - "repair_unable_to_check_items": "", - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_exclusion_pattern": "", - "unable_to_add_import_path": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_change_password": "", - "unable_to_copy_to_clipboard": "", - "unable_to_create_api_key": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", - "unable_to_delete_exclusion_pattern": "", - "unable_to_delete_import_path": "", - "unable_to_delete_shared_link": "", - "unable_to_delete_user": "", - "unable_to_edit_exclusion_pattern": "", - "unable_to_edit_import_path": "", - "unable_to_empty_trash": "", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", - "unable_to_hide_person": "", - "unable_to_link_oauth_account": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_api_key": "", - "unable_to_remove_deleted_assets": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_api_key": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_timeline_display_status": "", - "unable_to_update_user": "" - }, - "exit_slideshow": "", - "expand_all": "", - "expire_after": "", - "expired": "", - "explore": "", - "export": "", - "export_as_json": "", - "extension": "", - "external": "", - "external_libraries": "", - "favorite": "", - "favorite_or_unfavorite_photo": "", - "favorites": "", - "feature_photo_updated": "", - "file_name": "", - "file_name_or_extension": "", - "filename": "", - "filetype": "", - "filter_people": "", - "find_them_fast": "", - "fix_incorrect_match": "", - "forward": "", - "general": "", - "get_help": "", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "group_albums_by": "", - "has_quota": "", - "hide_gallery": "", - "hide_password": "", - "hide_person": "", - "host": "", - "hour": "", - "image": "", - "immich_logo": "", - "import_from_json": "", - "import_path": "", - "in_archive": "", - "include_archived": "", - "include_shared_albums": "", - "include_shared_partner_assets": "", - "individual_share": "", - "info": "", - "interval": { - "day_at_onepm": "", - "hours": "", - "night_at_midnight": "", - "night_at_twoam": "" - }, - "invite_people": "", - "invite_to_album": "", - "jobs": "", - "keep": "", - "keyboard_shortcuts": "", - "language": "", - "language_setting_description": "", - "last_seen": "", - "leave": "", - "let_others_respond": "", - "level": "", - "library": "", - "library_options": "", - "light": "", - "link_options": "", - "link_to_oauth": "", - "linked_oauth_account": "", - "list": "", - "loading": "", - "loading_search_results_failed": "", - "log_out": "", - "log_out_all_devices": "", - "login_has_been_disabled": "", - "look": "", - "loop_videos": "", - "loop_videos_description": "", - "make": "", - "manage_shared_links": "", - "manage_sharing_with_partners": "", - "manage_the_app_settings": "", - "manage_your_account": "", - "manage_your_api_keys": "", - "manage_your_devices": "", - "manage_your_oauth_connection": "", - "map": "", - "map_marker_with_image": "", - "map_settings": "", - "matches": "", - "media_type": "", - "memories": "", - "memories_setting_description": "", - "menu": "", - "merge": "", - "merge_people": "", - "merge_people_successfully": "", - "minimize": "", - "minute": "", - "missing": "", - "model": "", - "month": "", - "more": "", - "moved_to_trash": "", - "my_albums": "", - "name": "", - "name_or_nickname": "", - "never": "", - "new_api_key": "", - "new_password": "", - "new_person": "", - "new_user_created": "", - "newest_first": "", - "next": "", - "next_memory": "", - "no": "", - "no_albums_message": "", - "no_archived_assets_message": "", - "no_assets_message": "", - "no_duplicates_found": "", - "no_exif_info_available": "", - "no_explore_results_message": "", - "no_favorites_message": "", - "no_libraries_message": "", - "no_name": "", - "no_places": "", - "no_results": "", - "no_shared_albums_message": "", - "not_in_any_album": "", - "note_apply_storage_label_to_previously_uploaded assets": "", - "notes": "", - "notification_toggle_setting_description": "", - "notifications": "", - "notifications_setting_description": "", - "oauth": "", - "offline": "", - "offline_paths": "", - "offline_paths_description": "", - "ok": "", - "oldest_first": "", - "online": "", - "only_favorites": "", - "open_the_search_filters": "", - "options": "", - "organize_your_library": "", - "other": "", - "other_devices": "", - "other_variables": "", - "owned": "", - "owner": "", - "partner_can_access": "", - "partner_can_access_assets": "", - "partner_can_access_location": "", - "partner_sharing": "", - "partners": "", - "password": "", - "password_does_not_match": "", - "password_required": "", - "password_reset_success": "", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, - "path": "", - "pattern": "", - "pause": "", - "pause_memories": "", - "paused": "", - "pending": "", - "people": "", - "people_sidebar_description": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", - "permanently_delete": "", - "permanently_deleted_asset": "", - "photos": "", - "photos_count": "", - "photos_from_previous_years": "", - "pick_a_location": "", - "place": "", - "places": "", - "play": "", - "play_memories": "", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", - "profile_picture_set": "", - "public_share": "", - "reaction_options": "", - "read_changelog": "", - "recent": "", - "recent_searches": "", - "refresh": "", - "refreshed": "", - "refreshes_every_file": "", - "remove": "", - "remove_deleted_assets": "", - "remove_from_album": "", - "remove_from_favorites": "", - "remove_from_shared_link": "", - "removed_api_key": "", - "rename": "", - "repair": "", - "repair_no_results_message": "", - "replace_with_upload": "", - "require_password": "", - "require_user_to_change_password_on_first_login": "", - "reset": "", - "reset_password": "", - "reset_people_visibility": "", - "restore": "", - "restore_all": "", - "restore_user": "", - "resume": "", - "retry_upload": "", - "review_duplicates": "", - "role": "", - "save": "", - "saved_api_key": "", - "saved_profile": "", - "saved_settings": "", - "say_something": "", - "scan_all_libraries": "", - "scan_settings": "", - "search": "", - "search_albums": "", - "search_by_context": "", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", - "search_for_existing_person": "", - "search_people": "", - "search_places": "", - "search_state": "", - "search_timezone": "", - "search_type": "", - "search_your_photos": "", - "searching_locales": "", - "second": "", - "select_album_cover": "", - "select_all": "", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_keep_all": "", - "select_library_owner": "", - "select_new_face": "", - "select_photos": "", - "select_trash_all": "", - "selected": "", - "send_message": "", - "send_welcome_email": "", - "server_stats": "", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", - "settings": "", - "settings_saved": "", - "share": "", - "shared": "", - "shared_by": "", - "shared_by_you": "", - "shared_from_partner": "", - "shared_links": "", - "shared_with_partner": "", - "sharing": "", - "sharing_sidebar_description": "", - "show_album_options": "", - "show_and_hide_people": "", - "show_file_location": "", - "show_gallery": "", - "show_hidden_people": "", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", - "show_metadata": "", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", - "shuffle": "", - "sign_out": "", - "sign_up": "", - "size": "", - "skip_to_content": "", - "slideshow": "", - "slideshow_settings": "", - "sort_albums_by": "", - "stack": "", - "stack_selected_photos": "", - "stacktrace": "", - "start": "", - "start_date": "", - "state": "", - "status": "", - "stop_motion_photo": "", - "stop_photo_sharing": "", - "stop_photo_sharing_description": "", - "stop_sharing_photos_with_user": "", - "storage": "", - "storage_label": "", - "storage_usage": "", - "submit": "", - "suggestions": "", - "sunrise_on_the_beach": "", - "swap_merge_direction": "", - "sync": "", - "template": "", - "theme": "", - "theme_selection": "", - "theme_selection_description": "", - "time_based_memories": "", - "timezone": "", - "to_archive": "", - "to_favorite": "", - "toggle_settings": "", - "toggle_theme": "", - "total_usage": "", - "trash": "", - "trash_all": "", - "trash_no_results_message": "", - "trashed_items_will_be_permanently_deleted_after": "", - "type": "", - "unarchive": "", - "unfavorite": "", - "unhide_person": "", - "unknown": "", - "unknown_year": "", - "unlimited": "", - "unlink_oauth": "", - "unlinked_oauth_account": "", - "unselect_all": "", - "unstack": "", - "untracked_files": "", - "untracked_files_decription": "", - "up_next": "", - "updated_password": "", - "upload": "", - "upload_concurrency": "", - "url": "", - "usage": "", - "user": "", - "user_id": "", - "user_usage_detail": "", - "username": "", - "users": "", - "utilities": "", - "validate": "", - "variables": "", - "version": "", - "video": "", - "video_hover_setting": "", - "video_hover_setting_description": "", - "videos": "", - "videos_count": "", - "view_all": "", - "view_all_users": "", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", - "waiting": "", - "week": "", - "welcome_to_immich": "", - "year": "", - "yes": "", - "you_dont_have_any_shared_links": "", - "zoom_image": "" + "add_more_users": "Putem mor man" } diff --git a/i18n/bn.json b/i18n/bn.json index 0967ef424b..966d474111 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -1 +1,17 @@ -{} +{ + "about": "āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇", + "account": "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ", + "account_settings": "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āϏ⧇āϟāĻŋāĻ‚āϏ", + "acknowledge": "āĻ¸ā§āĻŦā§€āĻ•ā§ƒāϤāĻŋ", + "action": "āĻ•āĻžāĻ°ā§āϝ", + "action_common_update": "āφāĻĒāĻĄā§‡āϟ", + "actions": "āĻ•āĻ°ā§āĻŽ", + "active": "āϏāϚāϞ", + "activity": "āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ", + "add": "āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_a_description": "āĻāĻ•āϟāĻŋ āĻŦāĻŋāĻŦāϰāĻŖ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_a_location": "āĻāĻ•āϟāĻŋ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_a_name": "āĻāĻ•āϟāĻŋ āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_a_title": "āĻāĻ•āϟāĻŋ āĻļāĻŋāϰ⧋āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_endpoint": "āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āϝ⧋āĻ— āĻ•āϰ⧁āύ" +} diff --git a/i18n/ca.json b/i18n/ca.json index 52a47a83d5..cd9cc4e0b4 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Estàs segur que vols desactivar tots els mètodes d'inici de sessiÃŗ? L'inici de sessiÃŗ quedarà completament desactivat.", "authentication_settings_reenable": "Per a tornar a habilitar, empra una Comanda de Servidor.", "background_task_job": "Tasques en segon pla", - "backup_database": "CÃ˛pia de la base de dades", - "backup_database_enable_description": "Habilitar cÃ˛pies de la base de dades", - "backup_keep_last_amount": "Quantitat de cÃ˛pies de seguretat anteriors per conservar", - "backup_settings": "Ajustes de les cÃ˛pies de seguretat", - "backup_settings_description": "Gestionar la configuraciÃŗ de la cÃ˛pia de seguretat de la base de dades", + "backup_database": "Fer un bolcat de la base de dades", + "backup_database_enable_description": "Habilitar bolcat de la base de dades", + "backup_keep_last_amount": "Quantitat de bolcats anteriors per conservar", + "backup_settings": "ConfiguraciÃŗ dels bolcats", + "backup_settings_description": "Gestionar la configuraciÃŗ bolcats de la base de dades. Nota: els treballs no es monitoritzen ni es notifiquen les fallades.", "check_all": "Marca-ho tot", "cleanup": "Neteja", "cleared_jobs": "Tasques esborrades per a: {job}", @@ -53,6 +53,7 @@ "confirm_email_below": "Per a confirmar, escriviu \"{email}\" a sota", "confirm_reprocess_all_faces": "Esteu segur que voleu reprocessar totes les cares? AixÃ˛ tambÊ esborrarà la gent que heu anomenat.", "confirm_user_password_reset": "Esteu segur que voleu reinicialitzar la contrasenya de l'usuari {user}?", + "confirm_user_pin_code_reset": "Esteu segur que voleu restablir el codi PIN de {user}?", "create_job": "Crear tasca", "cron_expression": "ExpressiÃŗ Cron", "cron_expression_description": "Estableix l'interval d'escaneig amb el format cron. Per obtenir mÊs informaciÃŗ, consulteu, p.e Crontab Guru", @@ -192,26 +193,22 @@ "oauth_auto_register": "Registre automàtic", "oauth_auto_register_description": "Registra nous usuaris automàticament desprÊs d'iniciar sessiÃŗ amb OAuth", "oauth_button_text": "Text del botÃŗ", - "oauth_client_id": "ID Client", - "oauth_client_secret": "Secret de Client", + "oauth_client_secret_description": "Requerit si PKCE (Proof Key for Code Exchange) no està suportat pel proveïdor OAuth", "oauth_enable_description": "Iniciar sessiÃŗ amb OAuth", - "oauth_issuer_url": "URL de l'emissor", "oauth_mobile_redirect_uri": "URI de redirecciÃŗ mÃ˛bil", "oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecciÃŗ mÃ˛bil", "oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mÃ˛bil, com ara '{callback}'", - "oauth_profile_signing_algorithm": "Algoritme de signatura del perfil", - "oauth_profile_signing_algorithm_description": "Algoritme utilitzat per signar el perfil d’usuari.", - "oauth_scope": "Abast", "oauth_settings": "OAuth", "oauth_settings_description": "Gestiona la configuraciÃŗ de l'inici de sessiÃŗ OAuth", "oauth_settings_more_details": "Per a mÊs detalls sobre aquesta funcionalitat, consulteu la documentaciÃŗ.", - "oauth_signing_algorithm": "Algorisme de signatura", "oauth_storage_label_claim": "PeticiÃŗ d'etiquetatge d'emmagatzematge", "oauth_storage_label_claim_description": "Estableix automàticament l'etiquetatge d'emmagatzematge de l'usuari a aquest valor.", "oauth_storage_quota_claim": "Quota d'emmagatzematge reclamada", "oauth_storage_quota_claim_description": "Estableix automàticament la quota d'emmagatzematge de l'usuari al valor d'aquest paràmetre.", "oauth_storage_quota_default": "Quota d'emmagatzematge predeterminada (GiB)", "oauth_storage_quota_default_description": "Quota disponible en GB quan no s'estableixi cap valor (Entreu 0 per a quota il¡limitada).", + "oauth_timeout": "Solicitud caducada", + "oauth_timeout_description": "Timeout per a sol¡licituds en mil¡lisegons", "offline_paths": "Rutes sense connexiÃŗ", "offline_paths_description": "Aquests resultats poden ser deguts a l'eliminaciÃŗ manual de fitxers que no formen part d'una llibreria externa.", "password_enable_description": "Inicia sessiÃŗ amb correu electrÃ˛nic i contrasenya", @@ -352,6 +349,7 @@ "user_delete_delay_settings_description": "Nombre de dies desprÊs de la supressiÃŗ per eliminar permanentment el compte i els elements d'un usuari. El treball de supressiÃŗ d'usuaris s'executa a mitjanit per comprovar si hi ha usuaris preparats per eliminar. Els canvis en aquesta configuraciÃŗ s'avaluaran en la propera execuciÃŗ.", "user_delete_immediately": "El compte i els recursos de {user} es posaran a la cua per suprimir-los permanentment immediatament.", "user_delete_immediately_checkbox": "Posa en cua l'usuari i els recursos per suprimir-los immediatament", + "user_details": "Detalls d'usuari", "user_management": "GestiÃŗ d'usuaris", "user_password_has_been_reset": "La contrasenya de l'usuari ha estat restablida:", "user_password_reset_description": "Si us plau, proporcioneu la contrasenya temporal a l'usuari i informeu-los que haurà de canviar la contrasenya en el proper inici de sessiÃŗ.", @@ -371,13 +369,17 @@ "admin_password": "Contrasenya de l'administrador", "administration": "Administrador", "advanced": "Avançat", - "advanced_settings_log_level_title": "Nivell de registre: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opciÃŗ per filtrar els continguts multimèdia durant la sincronitzaciÃŗ segons criteris alternatius. NomÊs proveu-ho si teniu problemes amb l'aplicaciÃŗ per detectar tots els àlbums.", + "advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronitzaciÃŗ d'àlbums de dispositius alternatius", + "advanced_settings_log_level_title": "Nivell de registre: {level}", "advanced_settings_prefer_remote_subtitle": "Alguns dispositius sÃŗn molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", "advanced_settings_prefer_remote_title": "Prefereix imatges remotes", "advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol¡licitud de xarxa", "advanced_settings_proxy_headers_title": "Capçaleres de proxy", "advanced_settings_self_signed_ssl_subtitle": "Omet la verificaciÃŗ del certificat SSL del servidor. Requerit per a certificats autosignats.", "advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats", + "advanced_settings_sync_remote_deletions_subtitle": "Suprimeix o restaura automàticament un actiu en aquest dispositiu quan es realitzi aquesta acciÃŗ al web", + "advanced_settings_sync_remote_deletions_title": "Sincronitza les eliminacions remotes", "advanced_settings_tile_subtitle": "ConfiguraciÃŗ avançada de l'usuari", "advanced_settings_troubleshooting_subtitle": "Habilita funcions addicionals per a la resoluciÃŗ de problemes", "advanced_settings_troubleshooting_title": "ResoluciÃŗ de problemes", @@ -400,9 +402,9 @@ "album_remove_user_confirmation": "Esteu segurs que voleu eliminar {user}?", "album_share_no_users": "Sembla que has compartit aquest àlbum amb tots els usuaris o no tens cap usuari amb qui compartir-ho.", "album_thumbnail_card_item": "1 element", - "album_thumbnail_card_items": "{} elements", + "album_thumbnail_card_items": "{count} elements", "album_thumbnail_card_shared": " ¡ Compartit", - "album_thumbnail_shared_by": "Compartit per {}", + "album_thumbnail_shared_by": "Compartit per {user}", "album_updated": "Àlbum actualitzat", "album_updated_setting_description": "Rep una notificaciÃŗ per correu electrÃ˛nic quan un àlbum compartit tingui recursos nous", "album_user_left": "Surt de {album}", @@ -440,7 +442,7 @@ "archive": "Arxiu", "archive_or_unarchive_photo": "Arxivar o desarxivar fotografia", "archive_page_no_archived_assets": "No s'ha trobat res arxivat", - "archive_page_title": "Arxiu({})", + "archive_page_title": "Arxiu({count})", "archive_size": "Mida de l'arxiu", "archive_size_description": "Configureu la mida de l'arxiu de les descàrregues (en GiB)", "archived": "Arxivat", @@ -477,18 +479,18 @@ "assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum", "assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", - "assets_deleted_permanently": "{} element(s) esborrats permanentment", - "assets_deleted_permanently_from_server": "{} element(s) esborrats permanentment del servidor d'Immich", + "assets_deleted_permanently": "{count} element(s) esborrats permanentment", + "assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich", "assets_moved_to_trash_count": "{count, plural, one {# recurs mogut} other {# recursos moguts}} a la paperera", "assets_permanently_deleted_count": "{count, plural, one {# recurs esborrat} other {# recursos esborrats}} permanentment", "assets_removed_count": "{count, plural, one {# element eliminat} other {# elements eliminats}}", - "assets_removed_permanently_from_device": "{} element(s) esborrat permanentment del dispositiu", + "assets_removed_permanently_from_device": "{count} element(s) esborrat permanentment del dispositiu", "assets_restore_confirmation": "Esteu segurs que voleu restaurar tots els teus actius? Aquesta acciÃŗ no es pot desfer! Tingueu en compte que els recursos fora de línia no es poden restaurar d'aquesta manera.", "assets_restored_count": "{count, plural, one {# element restaurat} other {# elements restaurats}}", - "assets_restored_successfully": "{} element(s) recuperats correctament", - "assets_trashed": "{} element(s) enviat a la paperera", + "assets_restored_successfully": "{count} element(s) recuperats correctament", + "assets_trashed": "{count} element(s) enviat a la paperera", "assets_trashed_count": "{count, plural, one {# element enviat} other {# elements enviats}} a la paperera", - "assets_trashed_from_server": "{} element(s) enviat a la paperera del servidor d'Immich", + "assets_trashed_from_server": "{count} element(s) enviat a la paperera del servidor d'Immich", "assets_were_part_of_album_count": "{count, plural, one {L'element ja Ês} other {Els elements ja sÃŗn}} part de l'àlbum", "authorized_devices": "Dispositius autoritzats", "automatic_endpoint_switching_subtitle": "Connecteu-vos localment a travÊs de la Wi-Fi designada quan estigui disponible i utilitzeu connexions alternatives en altres llocs", @@ -497,7 +499,7 @@ "back_close_deselect": "Tornar, tancar o anul¡lar la selecciÃŗ", "background_location_permission": "Permís d'ubicaciÃŗ en segon pla", "background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accÊs a la ubicaciÃŗ precisa perquè l'aplicaciÃŗ pugui llegir el nom de la xarxa Wi-Fi", - "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({})", + "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})", "backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure", "backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procÊs de cÃ˛pia de seguretat.", "backup_album_selection_page_select_albums": "Selecciona àlbums", @@ -506,37 +508,36 @@ "backup_all": "Tots", "backup_background_service_backup_failed_message": "No s'ha pogut copiar els elements. Tornant a intentarâ€Ļ", "backup_background_service_connection_failed_message": "No s'ha pogut connectar al servidor. Tornant a intentarâ€Ļ", - "backup_background_service_current_upload_notification": "Pujant {}", - "backup_background_service_default_notification": "Cercant nous elements...", + "backup_background_service_current_upload_notification": "Pujant {filename}", + "backup_background_service_default_notification": "Cercant nous elementsâ€Ļ", "backup_background_service_error_title": "Error copiant", - "backup_background_service_in_progress_notification": "Copiant els teus elements", - "backup_background_service_upload_failure_notification": "Error al pujar {}", + "backup_background_service_in_progress_notification": "Copiant els teus elementsâ€Ļ", + "backup_background_service_upload_failure_notification": "Error en pujar {filename}", "backup_controller_page_albums": "Copia els àlbums", "backup_controller_page_background_app_refresh_disabled_content": "Activa l'actualitzaciÃŗ en segon pla de l'aplicaciÃŗ a ConfiguraciÃŗ > General > ActualitzaciÃŗ en segon pla per utilitzar la copia de seguretat en segon pla.", "backup_controller_page_background_app_refresh_disabled_title": "ActualitzaciÃŗ en segon pla desactivada", "backup_controller_page_background_app_refresh_enable_button_text": "VÊs a configuraciÃŗ", "backup_controller_page_background_battery_info_link": "Mostra'm com", - "backup_controller_page_background_battery_info_message": "Per obtenir la millor experiència de copia de seguretat en segon pla, desactiveu qualsevol optimitzaciÃŗ de bateria que restringeixi l'activitat en segon pla per a Immich.\n\nAtès que aixÃ˛ Ês específic del dispositiu, busqueu la informaciÃŗ necessària per al fabricant del vostre dispositiu", + "backup_controller_page_background_battery_info_message": "Per obtenir la millor experiència de cÃ˛pia de seguretat en segon pla, desactiveu qualsevol optimitzaciÃŗ de bateria que restringeixi l'activitat en segon pla per a Immich.\n\nAtès que aixÃ˛ Ês específic del dispositiu, busqueu la informaciÃŗ necessària per al fabricant del vostre dispositiu.", "backup_controller_page_background_battery_info_ok": "D'acord", "backup_controller_page_background_battery_info_title": "Optimitzacions de bateria", "backup_controller_page_background_charging": "NomÊs mentre es carrega", "backup_controller_page_background_configure_error": "No s'ha pogut configurar el servei en segon pla", - "backup_controller_page_background_delay": "Retard en la copia de seguretat de nous elements: {}", - "backup_controller_page_background_description": "Activeu el servei en segon pla per copiar automàticament tots els nous elements sense haver d'obrir l'aplicaciÃŗ.", + "backup_controller_page_background_delay": "Retard en la cÃ˛pia de seguretat de nous elements: {duration}", + "backup_controller_page_background_description": "Activeu el servei en segon pla per copiar automàticament tots els nous elements sense haver d'obrir l'aplicaciÃŗ", "backup_controller_page_background_is_off": "La cÃ˛pia automàtica en segon pla està desactivada", "backup_controller_page_background_is_on": "La cÃ˛pia automàtica en segon pla està activada", "backup_controller_page_background_turn_off": "Desactiva el servei en segon pla", "backup_controller_page_background_turn_on": "Activa el servei en segon pla", - "backup_controller_page_background_wifi": "NomÊs amb WiFi", + "backup_controller_page_background_wifi": "NomÊs amb Wi-Fi", "backup_controller_page_backup": "CÃ˛pia", "backup_controller_page_backup_selected": "Seleccionat: ", "backup_controller_page_backup_sub": "Fotografies i vídeos copiats", - "backup_controller_page_created": "Creat el: {}", + "backup_controller_page_created": "Creat el: {date}", "backup_controller_page_desc_backup": "Activeu la cÃ˛pia de seguretat per pujar automàticament els nous elements al servidor en obrir l'aplicaciÃŗ.", - "backup_controller_page_excluded": "Exclosos:", - "backup_controller_page_failed": "Fallats ({})", - "backup_controller_page_filename": "Nom de l'arxiu: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Exclosos: ", + "backup_controller_page_failed": "Fallats ({count})", + "backup_controller_page_filename": "Nom de l'arxiu: {filename} [{size}]", "backup_controller_page_info": "InformaciÃŗ de la cÃ˛pia", "backup_controller_page_none_selected": "Cap seleccionat", "backup_controller_page_remainder": "Restant", @@ -545,7 +546,7 @@ "backup_controller_page_start_backup": "Inicia la cÃ˛pia", "backup_controller_page_status_off": "La copia de seguretat està desactivada", "backup_controller_page_status_on": "La copia de seguretat està activada", - "backup_controller_page_storage_format": "{} de {} utilitzats", + "backup_controller_page_storage_format": "{used} de {total} utilitzats", "backup_controller_page_to_backup": "Àlbums a copiar", "backup_controller_page_total_sub": "Totes les fotografies i vídeos dels àlbums seleccionats", "backup_controller_page_turn_off": "Desactiva la cÃ˛pia de seguretat", @@ -570,21 +571,21 @@ "bulk_keep_duplicates_confirmation": "Esteu segur que voleu mantenir {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? AixÃ˛ resoldrà tots els grups duplicats sense eliminar res.", "bulk_trash_duplicates_confirmation": "Esteu segur que voleu enviar a les escombraries {count, plural, one {# recurs duplicat} other {# recursos duplicats}}? AixÃ˛ mantindrà el recurs mÊs gran de cada grup i eliminarà la resta de duplicats.", "buy": "Comprar Immich", - "cache_settings_album_thumbnails": "Miniatures de la pàgina de la biblioteca ({} elements)", + "cache_settings_album_thumbnails": "Miniatures de la pàgina de la biblioteca ({count} elements)", "cache_settings_clear_cache_button": "Neteja la memÃ˛ria cau", "cache_settings_clear_cache_button_title": "Neteja la memÃ˛ria cau de l'aplicaciÃŗ. AixÃ˛ impactarà significativament el rendiment fins que la memÃ˛ria cau es torni a reconstruir.", "cache_settings_duplicated_assets_clear_button": "NETEJA", - "cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicaciÃŗ.", - "cache_settings_duplicated_assets_title": "Elements duplicats ({})", - "cache_settings_image_cache_size": "Mida de la memÃ˛ria cau de imatges ({} elements)", + "cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicaciÃŗ", + "cache_settings_duplicated_assets_title": "Elements duplicats ({count})", + "cache_settings_image_cache_size": "Mida de la memÃ˛ria cau d'imatges ({count} elements)", "cache_settings_statistics_album": "Miniatures de la biblioteca", - "cache_settings_statistics_assets": "{} elements ({})", + "cache_settings_statistics_assets": "{count} elements ({size})", "cache_settings_statistics_full": "Imatges completes", "cache_settings_statistics_shared": "Miniatures d'àlbums compartits", "cache_settings_statistics_thumbnail": "Miniatures", "cache_settings_statistics_title": "Ús de memÃ˛ria cau", "cache_settings_subtitle": "Controla el comportament de la memÃ˛ria cau de l'aplicaciÃŗ mÃ˛bil Immich", - "cache_settings_thumbnail_size": "Mida de la memÃ˛ria cau de les miniatures ({} elements)", + "cache_settings_thumbnail_size": "Mida de la memÃ˛ria cau de les miniatures ({count} elements)", "cache_settings_tile_subtitle": "Controla el comportament de l'emmagatzematge local", "cache_settings_tile_title": "Emmagatzematge local", "cache_settings_title": "ConfiguraciÃŗ de la memÃ˛ria cau", @@ -610,6 +611,7 @@ "change_password_form_new_password": "Nova contrasenya", "change_password_form_password_mismatch": "Les contrasenyes no coincideixen", "change_password_form_reenter_new_password": "Torna a introduir la nova contrasenya", + "change_pin_code": "Canviar el codi PIN", "change_your_password": "Canvia la teva contrasenya", "changed_visibility_successfully": "Visibilitat canviada amb èxit", "check_all": "Marqueu-ho tot", @@ -624,7 +626,6 @@ "clear_all_recent_searches": "Esborra totes les cerques recents", "clear_message": "Neteja el missatge", "clear_value": "Neteja el valor", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Introdueix la contrasenya", "client_cert_import": "Importar", "client_cert_import_success_msg": "S'ha importat el certificat del client", @@ -636,7 +637,6 @@ "close": "Tanca", "collapse": "Tanca", "collapse_all": "Redueix-ho tot", - "color": "Color", "color_theme": "Tema de color", "comment_deleted": "Comentari esborrat", "comment_options": "Opcions de comentari", @@ -650,11 +650,11 @@ "confirm_delete_face": "Estàs segur que vols eliminar la cara de {name} de les cares reconegudes?", "confirm_delete_shared_link": "Esteu segurs que voleu eliminar aquest enllaç compartit?", "confirm_keep_this_delete_others": "Excepte aquest element, tots els altres de la pila se suprimiran. Esteu segur que voleu continuar?", + "confirm_new_pin_code": "Confirma el nou codi PIN", "confirm_password": "ConfirmaciÃŗ de contrasenya", "contain": "Contingut", - "context": "Context", "continue": "Continuar", - "control_bottom_app_bar_album_info_shared": "{} elements - Compartits", + "control_bottom_app_bar_album_info_shared": "{count} elements - Compartits", "control_bottom_app_bar_create_new_album": "Crea un àlbum nou", "control_bottom_app_bar_delete_from_immich": "Suprimeix del Immich", "control_bottom_app_bar_delete_from_local": "Suprimeix del dispositiu", @@ -692,9 +692,11 @@ "create_tag_description": "Crear una nova etiqueta. Per les etiquetes aniuades, escriu la ruta comperta de l'etiqueta, incloses les barres diagonals.", "create_user": "Crea un usuari", "created": "Creat", + "created_at": "Creat", "crop": "Retalla", "curated_object_page_title": "Coses", "current_device": "Dispositiu actual", + "current_pin_code": "Codi PIN actual", "current_server_address": "Adreça actual del servidor", "custom_locale": "LocalitzaciÃŗ personalitzada", "custom_locale_description": "Format de dates i nÃēmeros segons la llengua i regiÃŗ", @@ -718,7 +720,7 @@ "delete": "Esborra", "delete_album": "Esborra l'àlbum", "delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?", - "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu.", + "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu", "delete_dialog_alert_local": "Aquests elements s'eliminaran permanentment del vostre dispositiu, perÃ˛ encara estaran disponibles al servidor Immich", "delete_dialog_alert_local_non_backed_up": "Alguns dels elements no tenen cÃ˛pia de seguretat a Immich i s'eliminaran permanentment del dispositiu", "delete_dialog_alert_remote": "Aquests elements s'eliminaran permanentment del servidor Immich", @@ -746,7 +748,6 @@ "direction": "DirecciÃŗ", "disabled": "Desactivat", "disallow_edits": "No permetre les edicions", - "discord": "Discord", "discover": "Descobreix", "dismiss_all_errors": "Descarta tots els errors", "dismiss_error": "Descarta l'error", @@ -763,7 +764,7 @@ "download_enqueue": "Descàrrega en cua", "download_error": "Error de descàrrega", "download_failed": "Descàrrega ha fallat", - "download_filename": "arxiu: {}", + "download_filename": "arxiu: {filename}", "download_finished": "Descàrrega acabada", "download_include_embedded_motion_videos": "Vídeos incrustats", "download_include_embedded_motion_videos_description": "Incloure vídeos incrustats en fotografies en moviment com un arxiu separat", @@ -801,12 +802,12 @@ "edit_title": "Edita títol", "edit_user": "Edita l'usuari", "edited": "Editat", - "editor": "Editor", "editor_close_without_save_prompt": "No es desaran els canvis", "editor_close_without_save_title": "Tancar l'editor?", "editor_crop_tool_h2_aspect_ratios": "RelaciÃŗ d'aspecte", "editor_crop_tool_h2_rotation": "RotaciÃŗ", "email": "Correu electrÃ˛nic", + "email_notifications": "Correu electrÃ˛nic de notificacions", "empty_folder": "Aquesta carpeta Ês buida", "empty_trash": "Buidar la paperera", "empty_trash_confirmation": "Esteu segur que voleu buidar la paperera? AixÃ˛ eliminarà tots els recursos a la paperera permanentment d'Immich.\nNo podeu desfer aquesta acciÃŗ!", @@ -814,12 +815,10 @@ "enabled": "Activat", "end_date": "Data final", "enqueued": "En cua", - "enter_wifi_name": "Introdueix el nom de WiFi", - "error": "Error", + "enter_wifi_name": "Introdueix el nom de Wi-Fi", "error_change_sort_album": "No s'ha pogut canviar l'ordre d'ordenaciÃŗ dels àlbums", "error_delete_face": "Error esborrant cara de les cares reconegudes", "error_loading_image": "Error carregant la imatge", - "error_saving_image": "Error: {}", "error_title": "Error - Quelcom ha anat malament", "errors": { "cannot_navigate_next_asset": "No es pot navegar a l'element segÃŧent", @@ -849,10 +848,12 @@ "failed_to_keep_this_delete_others": "No s'ha pogut conservar aquest element i suprimir els altres", "failed_to_load_asset": "No s'ha pogut carregar l'element", "failed_to_load_assets": "No s'han pogut carregar els elements", + "failed_to_load_notifications": "Error en carregar les notificacions", "failed_to_load_people": "No s'han pogut carregar les persones", "failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte", "failed_to_stack_assets": "No s'han pogut apilar els elements", "failed_to_unstack_assets": "No s'han pogut desapilar els elements", + "failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions", "import_path_already_exists": "Aquesta ruta d'importaciÃŗ ja existeix.", "incorrect_email_or_password": "Correu electrÃ˛nic o contrasenya incorrectes", "paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar", @@ -920,6 +921,7 @@ "unable_to_remove_reaction": "No es pot eliminar la reacciÃŗ", "unable_to_repair_items": "No es poden reparar els elements", "unable_to_reset_password": "No es pot restablir la contrasenya", + "unable_to_reset_pin_code": "No es pot restablir el codi PIN", "unable_to_resolve_duplicate": "No es pot resoldre el duplicat", "unable_to_restore_assets": "No es poden restaurar els recursos", "unable_to_restore_trash": "No es pot restaurar la paperera", @@ -947,22 +949,20 @@ "unable_to_update_user": "No es pot actualitzar l'usuari", "unable_to_upload_file": "No es pot carregar el fitxer" }, - "exif": "Exif", - "exif_bottom_sheet_description": "Afegeix descripciÃŗ", + "exif_bottom_sheet_description": "Afegeix descripciÃŗ...", "exif_bottom_sheet_details": "DETALLS", "exif_bottom_sheet_location": "UBICACIÓ", "exif_bottom_sheet_people": "PERSONES", "exif_bottom_sheet_person_add_person": "Afegir nom", - "exif_bottom_sheet_person_age": "Edat {}", - "exif_bottom_sheet_person_age_months": "Edat {} mesos", - "exif_bottom_sheet_person_age_year_months": "Edat 1 any, {} mesos", - "exif_bottom_sheet_person_age_years": "Edat {}", + "exif_bottom_sheet_person_age": "Edat {age}", + "exif_bottom_sheet_person_age_months": "Edat {months} mesos", + "exif_bottom_sheet_person_age_year_months": "Edat 1 any, {months} mesos", + "exif_bottom_sheet_person_age_years": "Edat {years}", "exit_slideshow": "Surt de la presentaciÃŗ de diapositives", "expand_all": "Ampliar-ho tot", "experimental_settings_new_asset_list_subtitle": "Treball en curs", "experimental_settings_new_asset_list_title": "Habilita la graella de fotos experimental", "experimental_settings_subtitle": "Utilitzeu-ho sota la vostra responsabilitat!", - "experimental_settings_title": "Experimental", "expire_after": "Caduca desprÊs de", "expired": "Caducat", "expires_date": "Caduca el {date}", @@ -974,7 +974,7 @@ "external": "Extern", "external_libraries": "Llibreries externes", "external_network": "Xarxa externa", - "external_network_sheet_info": "Quan no estigui a la xarxa WiFi preferida, l'aplicaciÃŗ es connectarà al servidor mitjançant el primer dels URL segÃŧents a què pot arribar, començant de dalt a baix.", + "external_network_sheet_info": "Quan no estigui a la xarxa Wi-Fi preferida, l'aplicaciÃŗ es connectarà al servidor mitjançant el primer dels URL segÃŧents a què pot arribar, començant de dalt a baix", "face_unassigned": "Sense assignar", "failed": "Fallat", "failed_to_load_assets": "Error carregant recursos", @@ -992,6 +992,7 @@ "filetype": "Tipus d'arxiu", "filter": "Filtrar", "filter_people": "Filtra persones", + "filter_places": "Filtrar per llocs", "find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca", "fix_incorrect_match": "Corregiu la coincidència incorrecta", "folder": "Carpeta", @@ -999,7 +1000,6 @@ "folders": "Carpetes", "folders_feature_description": "Explorar la vista de carpetes per les fotos i vídeos del sistema d'arxius", "forward": "Endavant", - "general": "General", "get_help": "Aconseguir ajuda", "get_wifiname_error": "No s'ha pogut obtenir el nom de la Wi-Fi. Assegureu-vos que heu concedit els permisos necessaris i que esteu connectat a una xarxa Wi-Fi", "getting_started": "Començant", @@ -1040,7 +1040,7 @@ "home_page_delete_remote_err_local": "Elements locals a la selecciÃŗ d'eliminaciÃŗ remota, ometent", "home_page_favorite_err_local": "Encara no es pot afegir a preferits elements locals, ometent", "home_page_favorite_err_partner": "Encara no es pot afegir a preferits elements de companys, ometent", - "home_page_first_time_notice": "Si Ês la primera vegada que utilitzes l'app, si us plau, assegura't d'escollir un àlbum de cÃ˛pia de seguretat perquè la línia de temps pugui carregar fotos i vídeos als àlbums.", + "home_page_first_time_notice": "Si Ês la primera vegada que utilitzes l'app, si us plau, assegura't d'escollir un àlbum de cÃ˛pia de seguretat perquè la línia de temps pugui carregar fotos i vídeos als àlbums", "home_page_share_err_local": "No es poden compartir els elements locals a travÊs d'un enllaç, ometent", "home_page_upload_err_limit": "NomÊs es poden pujar un màxim de 30 elements alhora, ometent", "host": "AmfitriÃŗ", @@ -1120,7 +1120,7 @@ "local_network": "Xarxa local", "local_network_sheet_info": "L'aplicaciÃŗ es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada", "location_permission": "Permís d'ubicaciÃŗ", - "location_permission_content": "Per utilitzar la funciÃŗ de canvi automàtic, Immich necessita un permís de ubicaciÃŗ precisa perquè pugui llegir el nom de la xarxa WiFi actual", + "location_permission_content": "Per utilitzar la funciÃŗ de canvi automàtic, Immich necessita un permís d'ubicaciÃŗ precisa perquè pugui llegir el nom de la xarxa Wi-Fi actual", "location_picker_choose_on_map": "Escollir en el mapa", "location_picker_latitude_error": "Introdueix una latitud vàlida", "location_picker_latitude_hint": "Introdueix aquí la latitud", @@ -1144,7 +1144,7 @@ "login_form_err_trailing_whitespace": "Espai en blanc al final", "login_form_failed_get_oauth_server_config": "Error en iniciar sessiÃŗ amb OAuth, comprova l'URL del servidor", "login_form_failed_get_oauth_server_disable": "La funcionalitat OAuth no està disponible en aquest servidor", - "login_form_failed_login": "Error en iniciar sessiÃŗ, comprova l'URL del servidor, el correu electrÃ˛nic i la contrasenya.", + "login_form_failed_login": "Error en iniciar sessiÃŗ, comprova l'URL del servidor, el correu electrÃ˛nic i la contrasenya", "login_form_handshake_exception": "S'ha produït una excepciÃŗ de handshake amb el servidor. Activa el suport per certificats autofirmats a la configuraciÃŗ si estàs fent servir un certificat autofirmat.", "login_form_password_hint": "contrasenya", "login_form_save_login": "Mantingues identificat", @@ -1170,8 +1170,8 @@ "manage_your_devices": "Gestioneu els vostres dispositius connectats", "manage_your_oauth_connection": "Gestioneu la vostra connexiÃŗ OAuth", "map": "Mapa", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} fotos", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "No es pot obtenir la ubicaciÃŗ de l'usuari", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Utilitzar aquesta ubicaciÃŗ", @@ -1185,15 +1185,18 @@ "map_settings": "Paràmetres de mapa", "map_settings_dark_mode": "Mode fosc", "map_settings_date_range_option_day": "Últimes 24 hores", - "map_settings_date_range_option_days": "Darrers {} dies", + "map_settings_date_range_option_days": "Darrers {days} dies", "map_settings_date_range_option_year": "Any passat", - "map_settings_date_range_option_years": "Darrers {} anys", + "map_settings_date_range_option_years": "Darrers {years} anys", "map_settings_dialog_title": "ConfiguraciÃŗ del mapa", "map_settings_include_show_archived": "Incloure arxivats", "map_settings_include_show_partners": "Incloure companys", "map_settings_only_show_favorites": "Mostra nomÊs preferits", "map_settings_theme_settings": "Tema del Mapa", "map_zoom_to_see_photos": "Allunya per veure fotos", + "mark_all_as_read": "Marcar-ho tot com a llegit", + "mark_as_read": "Marcar com ha llegit", + "marked_all_as_read": "Marcat tot com a llegit", "matches": "Coincidències", "media_type": "Tipus de mitjà", "memories": "Records", @@ -1202,8 +1205,6 @@ "memories_setting_description": "Gestiona el que veus als teus records", "memories_start_over": "Torna a començar", "memories_swipe_to_close": "Llisca per tancar", - "memories_year_ago": "Fa un any", - "memories_years_ago": "Fa {} anys", "memory": "Record", "memory_lane_title": "Línia de records {title}", "menu": "MenÃē", @@ -1216,13 +1217,13 @@ "minimize": "Minimitza", "minute": "Minut", "missing": "Restants", - "model": "Model", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", "more": "MÊs", + "moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu", + "moved_to_library": "S'ha mogut {count, plural, one {# asset} other {# assets}} a la llibreria", "moved_to_trash": "S'ha mogut a la paperera", "multiselect_grid_edit_date_time_err_read_only": "No es pot canviar la data del fitxer(s) de nomÊs lectura, ometent", - "multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localitzaciÃŗ de fitxers de nomÊs lectura. Saltant.", + "multiselect_grid_edit_gps_err_read_only": "No es pot canviar la localitzaciÃŗ de fitxers de nomÊs lectura, saltant", "mute_memories": "Silenciar records", "my_albums": "Els meus àlbums", "name": "Nom", @@ -1234,12 +1235,12 @@ "new_api_key": "Nova clau de l'API", "new_password": "Nova contrasenya", "new_person": "Persona nova", + "new_pin_code": "Nou codi PIN", "new_user_created": "Nou usuari creat", "new_version_available": "NOVA VERSIÓ DISPONIBLE", "newest_first": "El mÊs nou primer", "next": "SegÃŧent", "next_memory": "SegÃŧent record", - "no": "No", "no_albums_message": "Creeu un àlbum per organitzar les vostres fotos i vídeos", "no_albums_with_name_yet": "Sembla que encara no tens cap àlbum amb aquest nom.", "no_albums_yet": "Sembla que encara no tens cap àlbum.", @@ -1252,6 +1253,8 @@ "no_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant", "no_libraries_message": "Creeu una llibreria externa per veure les vostres fotos i vídeos", "no_name": "Sense nom", + "no_notifications": "No hi ha notificacions", + "no_people_found": "No s'han trobat coincidències de persones", "no_places": "No hi ha llocs", "no_results": "Sense resultats", "no_results_description": "Proveu un sinÃ˛nim o una paraula clau mÊs general", @@ -1259,7 +1262,6 @@ "not_in_any_album": "En cap àlbum", "not_selected": "No seleccionat", "note_apply_storage_label_to_previously_uploaded assets": "Nota: per aplicar l'etiqueta d'emmagatzematge als actius penjats anteriorment, executeu el", - "notes": "Notes", "notification_permission_dialog_content": "Per activar les notificacions, aneu a ConfiguraciÃŗ i seleccioneu permet.", "notification_permission_list_tile_content": "Atorga permís per a activar les notificacions.", "notification_permission_list_tile_enable_button": "Activa les notificacions", @@ -1267,7 +1269,6 @@ "notification_toggle_setting_description": "Activa les notificacions per correu electrÃ˛nic", "notifications": "Notificacions", "notifications_setting_description": "Gestiona les notificacions", - "oauth": "OAuth", "official_immich_resources": "Recursos oficials d'Immich", "offline": "Fora de línia", "offline_paths": "Rutes fora de línia", @@ -1282,13 +1283,13 @@ "onboarding_welcome_user": "Benvingut, {user}", "online": "En línia", "only_favorites": "NomÊs preferits", + "open": "Obrir", "open_in_map_view": "Obrir a la vista del mapa", "open_in_openstreetmap": "Obre a OpenStreetMap", "open_the_search_filters": "Obriu els filtres de cerca", "options": "Opcions", "or": "o", "organize_your_library": "Organitzeu la llibreria", - "original": "original", "other": "Altres", "other_devices": "Altres dispositius", "other_variables": "Altres variables", @@ -1305,7 +1306,7 @@ "partner_page_partner_add_failed": "No s'ha pogut afegir el company", "partner_page_select_partner": "Escull company", "partner_page_shared_to_title": "Compartit amb", - "partner_page_stop_sharing_content": "{} ja no podrà accedir a les teves fotos.", + "partner_page_stop_sharing_content": "{partner} ja no podrà accedir a les teves fotos.", "partner_sharing": "ComparticiÃŗ amb companys", "partners": "Companys", "password": "Contrasenya", @@ -1351,6 +1352,9 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos d'anys anteriors", "pick_a_location": "Triar una ubicaciÃŗ", + "pin_code_changed_successfully": "Codi PIN canviat correctament", + "pin_code_reset_successfully": "S'ha restablert correctament el codi PIN", + "pin_code_setup_successfully": "S'ha configurat correctament un codi PIN", "place": "Lloc", "places": "Llocs", "places_count": "{count, plural, one {{count, number} Lloc} other {{count, number} Llocs}}", @@ -1358,7 +1362,6 @@ "play_memories": "Reproduir records", "play_motion_photo": "Reproduir Fotos en Moviment", "play_or_pause_video": "Reproduir o posar en pausa el vídeo", - "port": "Port", "preferences_settings_subtitle": "Gestiona les preferències de l'aplicaciÃŗ", "preferences_settings_title": "Preferències", "preset": "Preestablert", @@ -1368,11 +1371,11 @@ "previous_or_next_photo": "Foto anterior o segÃŧent", "primary": "Primària", "privacy": "Privacitat", + "profile": "Perfil", "profile_drawer_app_logs": "Registres", "profile_drawer_client_out_of_date_major": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ major.", "profile_drawer_client_out_of_date_minor": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ menor.", "profile_drawer_client_server_up_to_date": "El Client i el Servidor estan actualitzats", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ major.", "profile_drawer_server_out_of_date_minor": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ menor.", "profile_image_of_user": "Imatge de perfil de {user}", @@ -1381,7 +1384,7 @@ "public_share": "Compartit pÃēblicament", "purchase_account_info": "Contribuent", "purchase_activated_subtitle": "Gràcies per donar suport a Immich i al programari de codi obert", - "purchase_activated_time": "Activat el {date, date}", + "purchase_activated_time": "Activat el {date}", "purchase_activated_title": "La teva clau s'ha activat correctament", "purchase_button_activate": "Activar", "purchase_button_buy": "Comprar", @@ -1393,7 +1396,6 @@ "purchase_failed_activation": "No s'ha pogut activar! Si us plau, comproveu el vostre correu electrÃ˛nic per trobar la clau de producte correcta!", "purchase_individual_description_1": "Per a un particular", "purchase_individual_description_2": "Estat de la contribuciÃŗ", - "purchase_individual_title": "Individual", "purchase_input_suggestion": "Tens una clau de producte? Introduïu la clau a continuaciÃŗ", "purchase_license_subtitle": "Compra Immich per donar suport al desenvolupament continuat del servei", "purchase_lifetime_description": "Compra de per vida", @@ -1421,11 +1423,12 @@ "reassigned_assets_to_existing_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a {name, select, null {una persona existent} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a una persona nova", "reassing_hint": "Assignar els elements seleccionats a una persona existent", - "recent": "Recent", "recent-albums": "Àlbums recents", "recent_searches": "Cerques recents", "recently_added": "Afegit recentment", "recently_added_page_title": "Afegit recentment", + "recently_taken": "Fet recentment", + "recently_taken_page_title": "Fet recentment", "refresh": "Actualitzar", "refresh_encoded_videos": "Actualitza vídeos codificats", "refresh_faces": "Actualitzar cares", @@ -1468,6 +1471,7 @@ "reset": "Restablir", "reset_password": "Restablir contrasenya", "reset_people_visibility": "Restablir la visibilitat de les persones", + "reset_pin_code": "Restablir el codi PIN", "reset_to_default": "Restableix els valors predeterminats", "resolve_duplicates": "Resoldre duplicats", "resolved_all_duplicates": "Tots els duplicats resolts", @@ -1479,7 +1483,6 @@ "retry_upload": "Torna a provar de pujar", "review_duplicates": "Revisar duplicats", "role": "Rol", - "role_editor": "Editor", "role_viewer": "Visor", "save": "Desa", "save_to_gallery": "Desa a galeria", @@ -1523,7 +1526,6 @@ "search_no_people_named": "Cap persona anomenada \"{name}\"", "search_no_result": "No s'han trobat resultats, proveu un terme de cerca o una combinaciÃŗ diferents", "search_options": "Opcions de cerca", - "search_page_categories": "Categories", "search_page_motion_photos": "Fotografies animades", "search_page_no_objects": "No hi ha informaciÃŗ d'objectes disponibles", "search_page_no_places": "No hi ha informaciÃŗ de llocs disponibles", @@ -1560,6 +1562,7 @@ "select_keep_all": "MantÊn tota la selecciÃŗ", "select_library_owner": "Selecciona el propietari de la bilbioteca", "select_new_face": "Selecciona nova cara", + "select_person_to_tag": "Selecciona una persona per etiquetar", "select_photos": "Tria fotografies", "select_trash_all": "Envia la selecciÃŗ a la paperera", "select_user_for_sharing_page_err_album": "Error al crear l'àlbum", @@ -1590,12 +1593,12 @@ "setting_languages_apply": "Aplicar", "setting_languages_subtitle": "Canvia el llenguatge de l'aplicaciÃŗ", "setting_languages_title": "Idiomes", - "setting_notifications_notify_failures_grace_period": "Notifica les fallades de la cÃ˛pia de seguretat en segon pla: {}", - "setting_notifications_notify_hours": "{} hores", + "setting_notifications_notify_failures_grace_period": "Notifica les fallades de la cÃ˛pia de seguretat en segon pla: {duration}", + "setting_notifications_notify_hours": "{count} hores", "setting_notifications_notify_immediately": "immediatament", - "setting_notifications_notify_minutes": "{} minuts", + "setting_notifications_notify_minutes": "{count} minuts", "setting_notifications_notify_never": "mai", - "setting_notifications_notify_seconds": "{} segons", + "setting_notifications_notify_seconds": "{count} segons", "setting_notifications_single_progress_subtitle": "InformaciÃŗ detallada del progrÊs de la pujada de cada fitxer", "setting_notifications_single_progress_title": "Mostra el progrÊs detallat de la cÃ˛pia de seguretat en segon pla", "setting_notifications_subtitle": "Ajusta les preferències de notificaciÃŗ", @@ -1607,9 +1610,10 @@ "settings": "ConfiguraciÃŗ", "settings_require_restart": "Si us plau, reinicieu Immich per a aplicar aquest canvi", "settings_saved": "ConfiguraciÃŗ desada", + "setup_pin_code": "Configurar un codi PIN", "share": "Comparteix", "share_add_photos": "Afegeix fotografies", - "share_assets_selected": "{} seleccionats", + "share_assets_selected": "{count} seleccionats", "share_dialog_preparing": "S'està preparant...", "shared": "Compartit", "shared_album_activities_input_disable": "Els comentaris estan desactivats", @@ -1623,34 +1627,33 @@ "shared_by_user": "Compartit per {user}", "shared_by_you": "Compartit per tu", "shared_from_partner": "Fotos de {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Pujat", + "shared_intent_upload_button_progress_text": "{current} / {total} Pujat", "shared_link_app_bar_title": "Enllaços compartits", "shared_link_clipboard_copied_massage": "S'ha copiat al porta-retalls", - "shared_link_clipboard_text": "Enllaç: {}\nContrasenya: {}", + "shared_link_clipboard_text": "Enllaç: {link}\nContrasenya: {password}", "shared_link_create_error": "S'ha produït un error en crear l'enllaç compartit", "shared_link_edit_description_hint": "Introduïu la descripciÃŗ de comparticiÃŗ", "shared_link_edit_expire_after_option_day": "1 dia", - "shared_link_edit_expire_after_option_days": "{} dies", + "shared_link_edit_expire_after_option_days": "{count} dies", "shared_link_edit_expire_after_option_hour": "1 hora", - "shared_link_edit_expire_after_option_hours": "{} hores", + "shared_link_edit_expire_after_option_hours": "{count} hores", "shared_link_edit_expire_after_option_minute": "1 minut", - "shared_link_edit_expire_after_option_minutes": "{} minuts", - "shared_link_edit_expire_after_option_months": "{} mesos", - "shared_link_edit_expire_after_option_year": "any {}", + "shared_link_edit_expire_after_option_minutes": "{count} minuts", + "shared_link_edit_expire_after_option_months": "{count} mesos", + "shared_link_edit_expire_after_option_year": "any {count}", "shared_link_edit_password_hint": "Introduïu la contrasenya de comparticiÃŗ", "shared_link_edit_submit_button": "Actualitza l'enllaç", "shared_link_error_server_url_fetch": "No s'ha pogut obtenir l'URL del servidor", - "shared_link_expires_day": "Caduca d'aquí a {} dia", - "shared_link_expires_days": "Caduca d'aquí a {} dies", - "shared_link_expires_hour": "Caduca d'aquí a {} hora", - "shared_link_expires_hours": "Caduca d'aquí a {} hores", - "shared_link_expires_minute": "Caduca d'aquí a {} minut", - "shared_link_expires_minutes": "Caduca d'aquí a {} minuts", + "shared_link_expires_day": "Caduca d'aquí a {count} dia", + "shared_link_expires_days": "Caduca d'aquí a {count} dies", + "shared_link_expires_hour": "Caduca d'aquí a {count} hora", + "shared_link_expires_hours": "Caduca d'aquí a {count} hores", + "shared_link_expires_minute": "Caduca d'aquí a {count} minut", + "shared_link_expires_minutes": "Caduca d'aquí a {count} minuts", "shared_link_expires_never": "Caduca ∞", - "shared_link_expires_second": "Caduca d'aquí a {} segon", - "shared_link_expires_seconds": "Caduca d'aquí a {} segons", + "shared_link_expires_second": "Caduca d'aquí a {count} segon", + "shared_link_expires_seconds": "Caduca d'aquí a {count} segons", "shared_link_individual_shared": "Individual compartit", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestiona els enllaços compartits", "shared_link_options": "Opcions d'enllaços compartits", "shared_links": "Enllaços compartits", @@ -1723,6 +1726,7 @@ "stop_sharing_photos_with_user": "Deixa de compartir les fotos amb aquest usuari", "storage": "Emmagatzematge", "storage_label": "Etiquetatge d'emmagatzematge", + "storage_quota": "Quota d'emmagatzematge", "storage_usage": "{used} de {available} en Ãēs", "submit": "Envia", "suggestions": "Suggeriments", @@ -1749,7 +1753,7 @@ "theme_selection": "SelecciÃŗ de tema", "theme_selection_description": "Activa automàticament el tema fosc o clar en funciÃŗ de les preferències del sistema del navegador", "theme_setting_asset_list_storage_indicator_title": "Mostra l'indicador d'emmagatzematge als títols dels elements", - "theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({})", + "theme_setting_asset_list_tiles_per_row_title": "Nombre d'elements per fila ({count})", "theme_setting_colorful_interface_subtitle": "Apliqueu color primari a les superfícies de fons.", "theme_setting_colorful_interface_title": "Interfície colorida", "theme_setting_image_viewer_quality_subtitle": "Ajusta la qualitat del visor de detalls d'imatges", @@ -1774,7 +1778,6 @@ "to_trash": "Paperera", "toggle_settings": "Canvia configuraciÃŗ", "toggle_theme": "Alternar tema", - "total": "Total", "total_usage": "Ús total", "trash": "Paperera", "trash_all": "Envia-ho tot a la paperera", @@ -1784,13 +1787,15 @@ "trash_no_results_message": "Les imatges i vídeos que s'enviïn a la paperera es mostraran aquí.", "trash_page_delete_all": "Eliminar-ho tot", "trash_page_empty_trash_dialog_content": "Segur que voleu eliminar els elements? Aquests elements seran eliminats permanentment de Immich", - "trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment desprÊs de {} dies", + "trash_page_info": "Els elements que s'enviïn a la paperera s'eliminaran permanentment desprÊs de {days} dies", "trash_page_no_assets": "No hi ha elements a la paperera", "trash_page_restore_all": "Restaura-ho tot", "trash_page_select_assets_btn": "Selecciona elements", - "trash_page_title": "Paperera ({})", + "trash_page_title": "Paperera ({count})", "trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment desprÊs de {days, plural, one {# dia} other {# dies}}.", "type": "Tipus", + "unable_to_change_pin_code": "No es pot canviar el codi PIN", + "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", "unarchived_count": "{count, plural, other {# elements desarxivats}}", "unfavorite": "Reverteix preferit", @@ -1814,6 +1819,7 @@ "untracked_files": "Fitxers no monitoritzats", "untracked_files_decription": "Aquests fitxers no estan monitoritzats per l'aplicaciÃŗ. Poden ser el resultat de moviments errats, descàrregues interrompudes o deixats enrere per error", "up_next": "PrÃ˛xim", + "updated_at": "Actualitzat", "updated_password": "Contrasenya actualitzada", "upload": "Pujar", "upload_concurrency": "Concurrència de pujades", @@ -1823,18 +1829,19 @@ "upload_progress": "Restant {remaining, number} - Processat {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {S'ha omès # recurs duplicat} other {S'han omès # recursos duplicats}}", "upload_status_duplicates": "Duplicats", - "upload_status_errors": "Errors", "upload_status_uploaded": "Carregat", "upload_success": "Pujada correcta, actualitza la pàgina per veure nous recursos de pujada.", - "upload_to_immich": "Puja a Immich ({})", + "upload_to_immich": "Puja a Immich ({count})", "uploading": "Pujant", - "url": "URL", "usage": "Ús", "use_current_connection": "utilitzar la connexiÃŗ actual", "use_custom_date_range": "Fes servir un rang de dates personalitzat", "user": "Usuari", + "user_has_been_deleted": "Aquest usuari ha sigut eliminat.", "user_id": "ID d'usuari", "user_liked": "A {user} li ha agradat {type, select, photo {aquesta foto} video {aquest vídeo} asset {aquest recurs} other {}}", + "user_pin_code_settings": "Codi PIN", + "user_pin_code_settings_description": "Gestiona el teu codi PIN", "user_purchase_settings": "Compra", "user_purchase_settings_description": "Gestiona la teva compra", "user_role_set": "Establir {user} com a {role}", @@ -1846,7 +1853,6 @@ "utilities": "Utilitats", "validate": "Valida", "validate_endpoint_error": "Per favor introdueix un URL vàlid", - "variables": "Variables", "version": "VersiÃŗ", "version_announcement_closing": "El teu amic Alex", "version_announcement_message": "Hola! Hi ha una nova versiÃŗ d'Immich, si us plau, preneu-vos una estona per llegir les notes de llançament per assegurar que la teva configuraciÃŗ estigui actualitzada per evitar qualsevol error de configuraciÃŗ, especialment si utilitzeu WatchTower o qualsevol mecanisme que gestioni l'actualitzaciÃŗ automàtica de la vostra instància Immich.", @@ -1883,11 +1889,11 @@ "week": "Setmana", "welcome": "Benvingut", "welcome_to_immich": "Benvingut a immich", - "wifi_name": "Nom WiFi", + "wifi_name": "Nom Wi-Fi", "year": "Any", "years_ago": "Fa {years, plural, one {# any} other {# anys}}", "yes": "Sí", "you_dont_have_any_shared_links": "No tens cap enllaç compartit", - "your_wifi_name": "El teu nom WiFi", + "your_wifi_name": "Nom del teu Wi-Fi", "zoom_image": "Ampliar Imatge" } diff --git a/i18n/cs.json b/i18n/cs.json index f3373f06a1..7bfca25c01 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -26,6 +26,7 @@ "add_to_album": "Přidat do alba", "add_to_album_bottom_sheet_added": "PřidÃĄno do {album}", "add_to_album_bottom_sheet_already_exists": "Je jiÅž v {album}", + "add_to_locked_folder": "Přidat do uzamčenÊ sloÅžky", "add_to_shared_album": "Přidat do sdílenÊho alba", "add_url": "Přidat URL", "added_to_archive": "PřidÃĄno do archivu", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Opravdu chcete zakÃĄzat vÅĄechny metody přihlÃĄÅĄení? PřihlaÅĄovÃĄní bude Ãēplně zakÃĄzÃĄno.", "authentication_settings_reenable": "Pro opětovnÊ povolení pouÅžijte příkaz Příkaz serveru.", "background_task_job": "Úkoly na pozadí", - "backup_database": "ZÃĄlohovÃĄní databÃĄze", - "backup_database_enable_description": "Povolit zÃĄlohovÃĄní databÃĄze", - "backup_keep_last_amount": "Počet předchozích zÃĄloh k uchovÃĄní", - "backup_settings": "Nastavení zÃĄlohovÃĄní", - "backup_settings_description": "SprÃĄva nastavení zÃĄlohovÃĄní databÃĄze", + "backup_database": "Vytvořit vÃŊpis databÃĄze", + "backup_database_enable_description": "Povolit vÃŊpisy z databÃĄze", + "backup_keep_last_amount": "Počet předchozích vÃŊpisů, kterÊ se mají ponechat", + "backup_settings": "Nastavení vÃŊpisu databÃĄze", + "backup_settings_description": "SprÃĄva nastavení vÃŊpisu databÃĄze. PoznÃĄmka: Tyto Ãēlohy nejsou monitorovÃĄny a nebudete upozorněni na jejich selhÃĄní.", "check_all": "VÅĄe zkontrolovat", "cleanup": "VyčiÅĄtění", "cleared_jobs": "HotovÊ Ãēlohy pro: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Pro potvrzení zadejte níŞe \"{email}\"", "confirm_reprocess_all_faces": "Opravdu chcete znovu zpracovat vÅĄechny obličeje? Tím se vymaÅžou i pojmenovanÊ osoby.", "confirm_user_password_reset": "Opravdu chcete obnovit heslo uÅživatele {user}?", + "confirm_user_pin_code_reset": "Opravdu chcete resetovat PIN kÃŗd uÅživatele {user}?", "create_job": "Vytvořit Ãēlohu", "cron_expression": "VÃŊraz cron", "cron_expression_description": "Nastavte interval prohledÃĄvÃĄní pomocí cron formÃĄtu. DalÅĄÃ­ informace naleznete např. v Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "AutomatickÃĄ registrace", "oauth_auto_register_description": "Automaticky registrovat novÊ uÅživatele po přihlÃĄÅĄení pomocí OAuth", "oauth_button_text": "Text tlačítka", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Client Secret", + "oauth_client_secret_description": "VyÅžaduje se, pokud poskytovatel OAuth nepodporuje PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "PřihlÃĄsit pomocí OAuth", - "oauth_issuer_url": "URL vydavatele", "oauth_mobile_redirect_uri": "Mobilní přesměrovÃĄní URI", "oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrovÃĄní URI", "oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmus podepisovÃĄní profilu", - "oauth_profile_signing_algorithm_description": "Algoritmus pouÅžitÃŊ k podepsÃĄní profilu uÅživatele.", - "oauth_scope": "Rozsah", "oauth_settings": "OAuth", "oauth_settings_description": "SprÃĄva nastavení OAuth přihlÃĄÅĄení", "oauth_settings_more_details": "DalÅĄÃ­ podrobnosti o tÊto funkci naleznete v dokumentaci.", - "oauth_signing_algorithm": "Algoritmus podepisovÃĄní", "oauth_storage_label_claim": "Deklarace ÅĄtítku ÃēloÅžiÅĄtě", "oauth_storage_label_claim_description": "Automaticky nastavit ÅĄtítek ÃēloÅžiÅĄtě uÅživatele na hodnotu tÊto deklarace.", "oauth_storage_quota_claim": "Deklarace kvÃŗty ÃēloÅžiÅĄtě", "oauth_storage_quota_claim_description": "Automaticky nastavit kvÃŗtu ÃēloÅžiÅĄtě uÅživatele na hodnotu tÊto deklarace.", "oauth_storage_quota_default": "VÃŊchozí kvÃŗta ÃēloÅžiÅĄtě (GiB)", "oauth_storage_quota_default_description": "KvÃŗta v GiB, kterÃĄ se pouÅžije, pokud není poskytnuta ÅžÃĄdnÃĄ deklarace (pro neomezenou kvÃŗtu zadejte 0).", + "oauth_timeout": "ČasovÃŊ limit poÅžadavku", + "oauth_timeout_description": "ČasovÃŊ limit pro poÅžadavky v milisekundÃĄch", "offline_paths": "Cesty offline", "offline_paths_description": "Tyto vÃŊsledky mohou bÃŊt způsobeny ručním odstraněním souborů, kterÊ nejsou souÄÃĄstí externí knihovny.", "password_enable_description": "PřihlÃĄÅĄení pomocí e-mailu a hesla", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "Počet dní po odstranění, po kterÃŊch bude odstraněn Ãēčet a poloÅžky uÅživatele. Úloha odstraňovÃĄní uÅživatelů se spouÅĄtí o půlnoci a kontroluje uÅživatele, kteří jsou připraveni k odstranění. Změny tohoto nastavení se vyhodnotí při dalÅĄÃ­m spuÅĄtění.", "user_delete_immediately": "Účet a poloÅžky uÅživatele {user} budou zařazeny do fronty k trvalÊmu smazÃĄní okamÅžitě.", "user_delete_immediately_checkbox": "UÅživatele a poloÅžky zařadit do fronty k okamÅžitÊmu smazÃĄní", + "user_details": "Podrobnosti o uÅživateli", "user_management": "SprÃĄva uÅživatelů", "user_password_has_been_reset": "Heslo uÅživatele bylo obnoveno:", "user_password_reset_description": "Poskytněte uÅživateli dočasnÊ heslo a informujte ho, Åže si ho bude muset při pÅ™Ã­ÅĄtím přihlÃĄÅĄení změnit.", @@ -371,18 +370,22 @@ "admin_password": "Heslo sprÃĄvce", "administration": "Administrace", "advanced": "PokročilÊ", - "advanced_settings_log_level_title": "Úroveň protokolovÃĄní: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto moÅžnost pouÅžijte k filtrovÃĄní mÊdií během synchronizace na zÃĄkladě alternativních kritÊrií. Tuto moÅžnost vyzkouÅĄejte pouze v případě, Åže mÃĄte problÊmy s detekcí vÅĄech alb v aplikaci.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] PouŞít alternativní filtr pro synchronizaci alb zařízení", + "advanced_settings_log_level_title": "Úroveň protokolovÃĄní: {level}", "advanced_settings_prefer_remote_subtitle": "U některÃŊch zařízení je načítÃĄní miniatur z prostředků v zařízení velmi pomalÊ. Aktivujte toto nastavení, aby se místo toho načítaly vzdÃĄlenÊ obrÃĄzky.", "advanced_settings_prefer_remote_title": "Preferovat vzdÃĄlenÊ obrÃĄzky", "advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, kterÊ by měl Immich odesílat s kaÅždÃŊm síÅĨovÃŊm poÅžadavkem", "advanced_settings_proxy_headers_title": "Proxy hlavičky", "advanced_settings_self_signed_ssl_subtitle": "VynechÃĄ ověření SSL certifikÃĄtu serveru. VyÅžadovÃĄno pro self-signed certifikÃĄty.", "advanced_settings_self_signed_ssl_title": "Povolit self-signed SSL certifikÃĄty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky odstranit nebo obnovit poloÅžku v tomto zařízení, kdyÅž je tato akce provedena na webu", + "advanced_settings_sync_remote_deletions_title": "Synchronizace vzdÃĄlenÊho mazÃĄní [EXPERIMENTÁLNÍ]", "advanced_settings_tile_subtitle": "PokročilÊ uÅživatelskÊ nastavení", "advanced_settings_troubleshooting_subtitle": "Zobrazit dodatečnÊ vlastnosti pro řeÅĄení problÊmů", "advanced_settings_troubleshooting_title": "ŘeÅĄení problÊmů", - "age_months": "{months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}", - "age_year_months": "1 rok a {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}", + "age_months": "Věk {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}", + "age_year_months": "Věk 1 rok, {months, plural, one {# měsíc} other {# měsíce}}", "age_years": "{years, plural, one {# rok} few {# roky} other {# let}}", "album_added": "PřidÃĄno album", "album_added_notification_setting_description": "DostÃĄvat e-mailovÊ oznÃĄmení, kdyÅž jste přidÃĄni do sdílenÊho alba", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Opravdu chcete odebrat uÅživatele {user}?", "album_share_no_users": "Zřejmě jste toto album sdíleli se vÅĄemi uÅživateli, nebo nemÃĄte ÅžÃĄdnÊho uÅživatele, se kterÃŊm byste ho mohli sdílet.", "album_thumbnail_card_item": "1 poloÅžka", - "album_thumbnail_card_items": "{} poloÅžek", + "album_thumbnail_card_items": "{count} poloÅžek", "album_thumbnail_card_shared": " ¡ Sdíleno", - "album_thumbnail_shared_by": "Sdílel(a) {}", + "album_thumbnail_shared_by": "Sdílel(a) {user}", "album_updated": "Album aktualizovÃĄno", "album_updated_setting_description": "DostÃĄvat e-mailovÃĄ oznÃĄmení o novÃŊch poloÅžkÃĄch sdílenÊho alba", "album_user_left": "Opustil {album}", @@ -410,7 +413,7 @@ "album_viewer_appbar_delete_confirm": "Opravdu chcete toto album odstranit ze svÊho Ãēčtu?", "album_viewer_appbar_share_err_delete": "Nepodařilo se smazat album", "album_viewer_appbar_share_err_leave": "Nepodařilo se opustit album", - "album_viewer_appbar_share_err_remove": "Při odstraňovÃĄní poloÅžek z alba se vyskytly problÊmy.", + "album_viewer_appbar_share_err_remove": "Při odstraňovÃĄní poloÅžek z alba se vyskytly problÊmy", "album_viewer_appbar_share_err_title": "Nepodařilo se změnit nÃĄzev alba", "album_viewer_appbar_share_leave": "Opustit album", "album_viewer_appbar_share_to": "Sdílet na", @@ -440,7 +443,7 @@ "archive": "Archiv", "archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku", "archive_page_no_archived_assets": "Nebyla nalezena ÅžÃĄdnÃĄ archivovanÃĄ mÊdia", - "archive_page_title": "Archiv ({})", + "archive_page_title": "Archiv ({count})", "archive_size": "Velikost archivu", "archive_size_description": "Nastavte velikost archivu pro stahovÃĄní (v GiB)", "archived": "Archiv", @@ -477,18 +480,18 @@ "assets_added_to_album_count": "Do alba {count, plural, one {byla přidÃĄna # poloÅžka} few {byly přidÃĄny # poloÅžky} other {bylo přidÃĄno # poloÅžek}}", "assets_added_to_name_count": "{count, plural, one {PřidÃĄna # poloÅžka} few {PřidÃĄny # poloÅžky} other {PřidÃĄno # poloÅžek}} do {hasName, select, true {alba {name}} other {novÊho alba}}", "assets_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžek}}", - "assets_deleted_permanently": "{} poloÅžek trvale odstraněno", - "assets_deleted_permanently_from_server": "{} poloÅžek trvale odstraněno z Immich serveru", + "assets_deleted_permanently": "{count} poloÅžek trvale odstraněno", + "assets_deleted_permanently_from_server": "{count} poloÅžek trvale odstraněno z Immich serveru", "assets_moved_to_trash_count": "Do koÅĄe {count, plural, one {přesunuta # poloÅžka} few {přesunuty # poloÅžky} other {přesunuto # poloÅžek}}", "assets_permanently_deleted_count": "Trvale {count, plural, one {smazÃĄna # poloÅžka} few {smazÃĄny # poloÅžky} other {smazÃĄno # poloÅžek}}", "assets_removed_count": "{count, plural, one {Odstraněna # poloÅžka} few {Odstraněny # poloÅžky} other {Odstraněno # poloÅžek}}", - "assets_removed_permanently_from_device": "{} poloÅžek trvale odstraněno z vaÅĄeho zařízení", + "assets_removed_permanently_from_device": "{count} poloÅžek trvale odstraněno z vaÅĄeho zařízení", "assets_restore_confirmation": "Opravdu chcete obnovit vÅĄechny vyhozenÊ poloÅžky? Tuto akci nelze vrÃĄtit zpět! Upozorňujeme, Åže tímto způsobem nelze obnovit ÅžÃĄdnÊ offline poloÅžky.", "assets_restored_count": "{count, plural, one {Obnovena # poloÅžka} few {Obnoveny # poloÅžky} other {Obnoveno # poloÅžek}}", - "assets_restored_successfully": "{} poloÅžek ÃēspÄ›ÅĄně obnoveno", - "assets_trashed": "{} poloÅžek vyhozeno do koÅĄe", + "assets_restored_successfully": "{count} poloÅžek ÃēspÄ›ÅĄně obnoveno", + "assets_trashed": "{count} poloÅžek vyhozeno do koÅĄe", "assets_trashed_count": "{count, plural, one {Vyhozena # poloÅžka} few {Vyhozeny # poloÅžky} other {Vyhozeno # poloÅžek}}", - "assets_trashed_from_server": "{} poloÅžek vyhozeno do koÅĄe na Immich serveru", + "assets_trashed_from_server": "{count} poloÅžek vyhozeno do koÅĄe na Immich serveru", "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka byla} other {PoloÅžky byly}} souÄÃĄstí alba", "authorized_devices": "AutorizovanÃĄ zařízení", "automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a pouŞívat alternativní připojení jinde", @@ -497,31 +500,30 @@ "back_close_deselect": "Zpět, zavřít nebo zruÅĄit vÃŊběr", "background_location_permission": "Povolení polohy na pozadí", "background_location_permission_content": "Aby bylo moÅžnÊ přepínat sítě při běhu na pozadí, musí mít Immich *vÅždy* přístup k přesnÊ poloze, aby mohl zjistit nÃĄzev Wi-Fi sítě", - "backup_album_selection_page_albums_device": "Alba v zařízení ({})", + "backup_album_selection_page_albums_device": "Alba v zařízení ({count})", "backup_album_selection_page_albums_tap": "Klepnutím na poloÅžku ji zahrnete, opětovnÃŊm klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "PoloÅžky mohou bÃŊt roztrouÅĄeny ve více albech. To umoŞňuje zahrnout nebo vyloučit alba během procesu zÃĄlohovÃĄní.", "backup_album_selection_page_select_albums": "VybranÃĄ alba", "backup_album_selection_page_selection_info": "Informace o vÃŊběru", "backup_album_selection_page_total_assets": "CelkovÃŊ počet jedinečnÃŊch poloÅžek", "backup_all": "VÅĄe", - "backup_background_service_backup_failed_message": "ZÃĄlohovÃĄní mÊdií selhalo. ZkouÅĄÃ­m to znovu...", - "backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. ZkouÅĄÃ­m to znovu...", - "backup_background_service_current_upload_notification": "NahrÃĄvÃĄní {}", + "backup_background_service_backup_failed_message": "ZÃĄlohovÃĄní mÊdií selhalo. ZkouÅĄÃ­m to znovuâ€Ļ", + "backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. ZkouÅĄÃ­m to znovuâ€Ļ", + "backup_background_service_current_upload_notification": "NahrÃĄvÃĄní {filename}", "backup_background_service_default_notification": "Kontrola novÃŊch mÊdiíâ€Ļ", "backup_background_service_error_title": "Chyba zÃĄlohovÃĄní", - "backup_background_service_in_progress_notification": "ZÃĄlohovÃĄní vaÅĄich mÊdií...", - "backup_background_service_upload_failure_notification": "Nepodařilo se nahrÃĄt {}", + "backup_background_service_in_progress_notification": "ZÃĄlohovÃĄní vaÅĄich mÊdiíâ€Ļ", + "backup_background_service_upload_failure_notification": "Nepodařilo se nahrÃĄt {filename}", "backup_controller_page_albums": "ZÃĄlohovanÃĄ alba", "backup_controller_page_background_app_refresh_disabled_content": "Povolte obnovení aplikace na pozadí v Nastavení > ObecnÊ > Obnovení aplikace na pozadí, abyste mohli pouŞívat zÃĄlohovÃĄní na pozadí.", - "backup_controller_page_background_app_refresh_disabled_title": " ObnovovÃĄní aplikací na pozadí je vypnutÊ", + "backup_controller_page_background_app_refresh_disabled_title": "ObnovovÃĄní aplikací na pozadí je vypnutÊ", "backup_controller_page_background_app_refresh_enable_button_text": "Přejít do nastavení", "backup_controller_page_background_battery_info_link": "UkaÅž mi jak", "backup_controller_page_background_battery_info_message": "Chcete-li dosÃĄhnout nejlepÅĄÃ­ch vÃŊsledků při zÃĄlohovÃĄní na pozadí, vypněte vÅĄechny optimalizace baterie, kterÊ omezují aktivitu na pozadí pro Immich ve vaÅĄem zařízení. \n\nJelikoÅž je to zÃĄvislÊ na typu zařízení, vyhledejte poÅžadovanÊ informace pro vÃŊrobce vaÅĄeho zařízení.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimalizace baterie", "backup_controller_page_background_charging": "Pouze během nabíjení", "backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat sluÅžbu na pozadí", - "backup_controller_page_background_delay": "ZpoÅždění zÃĄlohovÃĄní novÃŊch mÊdií: {}", + "backup_controller_page_background_delay": "ZpoÅždění zÃĄlohovÃĄní novÃŊch mÊdií: {duration}", "backup_controller_page_background_description": "Povolte sluÅžbu na pozadí pro automatickÊ zÃĄlohovÃĄní vÅĄech novÃŊch poloÅžek bez nutnosti otevření aplikace", "backup_controller_page_background_is_off": "AutomatickÊ zÃĄlohovÃĄní na pozadí je vypnuto", "backup_controller_page_background_is_on": "AutomatickÊ zÃĄlohovÃĄní na pozadí je zapnuto", @@ -531,12 +533,11 @@ "backup_controller_page_backup": "ZÃĄlohovÃĄní", "backup_controller_page_backup_selected": "VybranÊ: ", "backup_controller_page_backup_sub": "ZÃĄlohovanÊ fotografie a videa", - "backup_controller_page_created": "Vytvořeno: {}", + "backup_controller_page_created": "Vytvořeno: {date}", "backup_controller_page_desc_backup": "Zapněte zÃĄlohovÃĄní na popředí, aby se novÊ poloÅžky automaticky nahrÃĄvaly na server při otevření aplikace.", "backup_controller_page_excluded": "Vyloučeno: ", - "backup_controller_page_failed": "Nepodařilo se ({})", - "backup_controller_page_filename": "NÃĄzev souboru: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Nepodařilo se ({count})", + "backup_controller_page_filename": "NÃĄzev souboru: {filename} [{size}]", "backup_controller_page_info": "Informace o zÃĄlohovÃĄní", "backup_controller_page_none_selected": "ÅŊÃĄdnÊ vybranÊ", "backup_controller_page_remainder": "ZbÃŊvÃĄ", @@ -545,7 +546,7 @@ "backup_controller_page_start_backup": "Spustit zÃĄlohovÃĄní", "backup_controller_page_status_off": "AutomatickÊ zÃĄlohovÃĄní na popředí je vypnuto", "backup_controller_page_status_on": "AutomatickÊ zÃĄlohovÃĄní na popředí je zapnuto", - "backup_controller_page_storage_format": "{} z {} pouÅžitÃŊch", + "backup_controller_page_storage_format": "{used} z {total} pouÅžitÃŊch", "backup_controller_page_to_backup": "Alba, kterÃĄ mají bÃŊt zÃĄlohovÃĄna", "backup_controller_page_total_sub": "VÅĄechny jedinečnÊ fotografie a videa z vybranÃŊch alb", "backup_controller_page_turn_off": "Vypnout zÃĄlohovÃĄní na popředí", @@ -560,6 +561,10 @@ "backup_options_page_title": "Nastavení zÃĄloh", "backup_setting_subtitle": "SprÃĄva nastavení zÃĄlohovÃĄní na pozadí a na popředí", "backward": "PozpÃĄtku", + "biometric_auth_enabled": "BiometrickÊ ověřovÃĄní je povoleno", + "biometric_locked_out": "Jste vyloučeni z biometrickÊho ověřovÃĄní", + "biometric_no_options": "BiometrickÊ moÅžnosti nejsou k dispozici", + "biometric_not_available": "BiometrickÊ ověřovÃĄní není na tomto zařízení k dispozici", "birthdate_saved": "Datum narození ÃēspÄ›ÅĄně uloÅženo", "birthdate_set_description": "Datum narození se pouŞívÃĄ k vÃŊpočtu věku osoby v době pořízení fotografie.", "blurred_background": "RozmazanÊ pozadí", @@ -570,21 +575,21 @@ "bulk_keep_duplicates_confirmation": "Opravdu si chcete ponechat {count, plural, one {# duplicitní poloÅžku} few {# duplicitní poloÅžky} other {# duplicitních poloÅžek}}? Tím se vyřeÅĄÃ­ vÅĄechny duplicitní skupiny, aniÅž by se cokoli odstranilo.", "bulk_trash_duplicates_confirmation": "Opravdu chcete hromadně vyhodit {count, plural, one {# duplicitní poloÅžku} few {# duplicitní poloÅžky} other {# duplicitních poloÅžek}}? Tím se zachovÃĄ největÅĄÃ­ poloÅžka z kaÅždÊ skupiny a vÅĄechny ostatní duplikÃĄty se vyhodí.", "buy": "Zakoupit Immich", - "cache_settings_album_thumbnails": "NÃĄhledy strÃĄnek knihovny (poloÅžek {})", + "cache_settings_album_thumbnails": "NÃĄhledy strÃĄnek knihovny ({count} poloÅžek)", "cache_settings_clear_cache_button": "Vymazat vyrovnÃĄvací paměÅĨ", "cache_settings_clear_cache_button_title": "VymaÅže vyrovnÃĄvací paměÅĨ aplikace. To vÃŊrazně ovlivní vÃŊkon aplikace, dokud se vyrovnÃĄvací paměÅĨ neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, kterÊ aplikace zařadila na černou listinu", - "cache_settings_duplicated_assets_title": "Duplicitní poloÅžky ({})", - "cache_settings_image_cache_size": "Velikost vyrovnÃĄvací paměti (poloÅžek {})", + "cache_settings_duplicated_assets_title": "Duplicitní poloÅžky ({count})", + "cache_settings_image_cache_size": "Velikost vyrovnÃĄvací paměti ({count} poloÅžek)", "cache_settings_statistics_album": "Knihovna nÃĄhledů", - "cache_settings_statistics_assets": "{} poloÅžky ({})", + "cache_settings_statistics_assets": "{count} poloÅžek ({size})", "cache_settings_statistics_full": "Kompletní fotografie", "cache_settings_statistics_shared": "SdílenÊ nÃĄhledy alb", "cache_settings_statistics_thumbnail": "NÃĄhledy", "cache_settings_statistics_title": "PouÅžití vyrovnÃĄvací paměti", "cache_settings_subtitle": "OvlÃĄdÃĄní chovÃĄní mobilní aplikace Immich v mezipaměti", - "cache_settings_thumbnail_size": "Velikost vyrovnÃĄvací paměti nÃĄhledů (poloÅžek {})", + "cache_settings_thumbnail_size": "Velikost vyrovnÃĄvací paměti nÃĄhledů ({count} poloÅžek)", "cache_settings_tile_subtitle": "OvlÃĄdÃĄní chovÃĄní místního ÃēloÅžiÅĄtě", "cache_settings_tile_title": "Místní ÃēloÅžiÅĄtě", "cache_settings_title": "Nastavení vyrovnÃĄvací paměti", @@ -597,7 +602,9 @@ "cannot_merge_people": "Nelze sloučit osoby", "cannot_undo_this_action": "Tuto akci nelze vrÃĄtit zpět!", "cannot_update_the_description": "Nelze aktualizovat popis", + "cast": "PřenÃĄÅĄet", "change_date": "Změnit datum", + "change_description": "Změnit popis", "change_display_order": "Změnit pořadí zobrazení", "change_expiration_time": "Změna konce platnosti", "change_location": "Změna polohy", @@ -610,6 +617,7 @@ "change_password_form_new_password": "NovÊ heslo", "change_password_form_password_mismatch": "Hesla se neshodují", "change_password_form_reenter_new_password": "Znovu zadejte novÊ heslo", + "change_pin_code": "Změnit PIN kÃŗd", "change_your_password": "Změna vaÅĄeho hesla", "changed_visibility_successfully": "Změna viditelnosti proběhla ÃēspÄ›ÅĄně", "check_all": "Zkontrolovat vÅĄe", @@ -624,7 +632,6 @@ "clear_all_recent_searches": "Vymazat vÅĄechna nedÃĄvnÃĄ vyhledÃĄvÃĄní", "clear_message": "Vymazat zprÃĄvu", "clear_value": "Vymazat hodnotu", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Zadejte heslo", "client_cert_import": "Importovat", "client_cert_import_success_msg": "KlientskÃŊ certifikÃĄt je importovÃĄn", @@ -650,11 +657,13 @@ "confirm_delete_face": "Opravdu chcete z poloÅžky odstranit obličej osoby {name}?", "confirm_delete_shared_link": "Opravdu chcete odstranit tento sdílenÃŊ odkaz?", "confirm_keep_this_delete_others": "VÅĄechny ostatní poloÅžky v tomto uskupení mimo tÊto budou odstraněny. Opravdu chcete pokračovat?", + "confirm_new_pin_code": "Potvrzení novÊho PIN kÃŗdu", "confirm_password": "Potvrzení hesla", + "connected_to": "Připojeno k", "contain": "Obsah", "context": "Kontext", "continue": "Pokračovat", - "control_bottom_app_bar_album_info_shared": "{} poloÅžky – sdílenÊ", + "control_bottom_app_bar_album_info_shared": "{count} poloÅžek ¡ Sdíleno", "control_bottom_app_bar_create_new_album": "Vytvořit novÊ album", "control_bottom_app_bar_delete_from_immich": "Smazat ze serveru Immich", "control_bottom_app_bar_delete_from_local": "Smazat ze zařízení", @@ -692,9 +701,11 @@ "create_tag_description": "Vytvoření novÊ značky. U vnořenÃŊch značek zadejte celou cestu ke značce včetně dopřednÃŊch lomítek.", "create_user": "Vytvořit uÅživatele", "created": "Vytvořeno", + "created_at": "Vytvořeno", "crop": "Oříznout", "curated_object_page_title": "Věci", "current_device": "SoučasnÊ zařízení", + "current_pin_code": "AktuÃĄlní PIN kÃŗd", "current_server_address": "AktuÃĄlní adresa serveru", "custom_locale": "Vlastní lokalizace", "custom_locale_description": "FormÃĄtovat datumy a čísla podle jazyka a oblasti", @@ -721,7 +732,7 @@ "delete_dialog_alert": "Tyto poloÅžky budou trvale smazÃĄny z aplikace Immich i z vaÅĄeho zařízení", "delete_dialog_alert_local": "Tyto poloÅžky budou z vaÅĄeho zařízení trvale smazÃĄny, ale budou stÃĄle k dispozici na Immich serveru", "delete_dialog_alert_local_non_backed_up": "NěkterÊ poloÅžky nejsou zÃĄlohovÃĄny na Immich server a budou ze zařízení trvale smazÃĄny", - "delete_dialog_alert_remote": "Tyto poloÅžky budou trvale smazÃĄny z Immich serveru ", + "delete_dialog_alert_remote": "Tyto poloÅžky budou trvale smazÃĄny z Immich serveru", "delete_dialog_ok_force": "Přesto smazat", "delete_dialog_title": "Smazat trvale", "delete_duplicates_confirmation": "Opravdu chcete tyto duplicity trvale odstranit?", @@ -746,7 +757,6 @@ "direction": "Směr", "disabled": "ZakÃĄzÃĄno", "disallow_edits": "ZakÃĄzat Ãēpravy", - "discord": "Discord", "discover": "Objevit", "dismiss_all_errors": "ZruÅĄit vÅĄechny chyby", "dismiss_error": "ZruÅĄit chybu", @@ -763,7 +773,7 @@ "download_enqueue": "StahovÃĄní ve frontě", "download_error": "Chyba při stahovÃĄní", "download_failed": "StahovÃĄní selhalo", - "download_filename": "soubor: {}", + "download_filename": "soubor: {filename}", "download_finished": "StahovÃĄní dokončeno", "download_include_embedded_motion_videos": "VloÅženÃĄ videa", "download_include_embedded_motion_videos_description": "Zahrnout videa vloÅženÃĄ do pohyblivÃŊch fotografií jako samostatnÃŊ soubor", @@ -787,6 +797,8 @@ "edit_avatar": "Upravit avatar", "edit_date": "Upravit datum", "edit_date_and_time": "Upravit datum a čas", + "edit_description": "Upravit popis", + "edit_description_prompt": "Vyberte novÃŊ popis:", "edit_exclusion_pattern": "Upravit vzor vyloučení", "edit_faces": "Upravit obličeje", "edit_import_path": "Upravit cestu importu", @@ -801,25 +813,28 @@ "edit_title": "Upravit nÃĄzev", "edit_user": "Upravit uÅživatele", "edited": "Upraveno", - "editor": "Editor", "editor_close_without_save_prompt": "Změny nebudou uloÅženy", "editor_close_without_save_title": "Zavřít editor?", "editor_crop_tool_h2_aspect_ratios": "Poměr stran", "editor_crop_tool_h2_rotation": "Otočení", "email": "E-mail", + "email_notifications": "E-mailovÃĄ oznÃĄmení", "empty_folder": "Tato sloÅžka je prÃĄzdnÃĄ", "empty_trash": "VyprÃĄzdnit koÅĄ", "empty_trash_confirmation": "Opravdu chcete vysypat koÅĄ? Tím se z Immiche trvale odstraní vÅĄechny poloÅžky v koÅĄi.\nTuto akci nelze vrÃĄtit zpět!", "enable": "Povolit", + "enable_biometric_auth_description": "Zadejte vÃĄÅĄ PIN kÃŗd pro povolení biometrickÊho ověřovÃĄní", "enabled": "Povoleno", "end_date": "KonečnÊ datum", "enqueued": "Ve frontě", - "enter_wifi_name": "Zadejte nÃĄzev WiFi", + "enter_wifi_name": "Zadejte nÃĄzev Wi-Fi", + "enter_your_pin_code": "Zadejte PIN kÃŗd", + "enter_your_pin_code_subtitle": "Zadejte PIN kÃŗd pro přístup k uzamčenÊ sloÅžce", "error": "Chyba", "error_change_sort_album": "Nepodařilo se změnit pořadí alba", "error_delete_face": "Chyba při odstraňovÃĄní obličeje z poloÅžky", "error_loading_image": "Chyba při načítÃĄní obrÃĄzku", - "error_saving_image": "Chyba: {}", + "error_saving_image": "Chyba: {error}", "error_title": "Chyba - Něco se pokazilo", "errors": { "cannot_navigate_next_asset": "Nelze přejít na dalÅĄÃ­ poloÅžku", @@ -849,10 +864,12 @@ "failed_to_keep_this_delete_others": "Nepodařilo se zachovat tuto poloÅžku a odstranit ostatní poloÅžky", "failed_to_load_asset": "Nepodařilo se načíst poloÅžku", "failed_to_load_assets": "Nepodařilo se načíst poloÅžky", + "failed_to_load_notifications": "Nepodařilo se načíst oznÃĄmení", "failed_to_load_people": "Chyba načítÃĄní osob", "failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu", "failed_to_stack_assets": "Nepodařilo se seskupit poloÅžky", "failed_to_unstack_assets": "Nepodařilo se rozloÅžit poloÅžky", + "failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznÃĄmení", "import_path_already_exists": "Tato cesta importu jiÅž existuje.", "incorrect_email_or_password": "NesprÃĄvnÃŊ e-mail nebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta neproÅĄla} few {# cesty neproÅĄly} other {# cest neproÅĄlo}} kontrolou", @@ -870,6 +887,7 @@ "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}", "unable_to_change_album_user_role": "Nelze změnit roli uÅživatele alba", "unable_to_change_date": "Nelze změnit datum", + "unable_to_change_description": "Nelze změnit popis", "unable_to_change_favorite": "Nelze změnit oblíbení poloÅžky", "unable_to_change_location": "Nelze změnit polohu", "unable_to_change_password": "Nelze změnit heslo", @@ -907,6 +925,7 @@ "unable_to_log_out_all_devices": "Nelze odhlÃĄsit vÅĄechna zařízení", "unable_to_log_out_device": "Nelze odhlÃĄsit zařízení", "unable_to_login_with_oauth": "Nelze se přihlÃĄsit pomocí OAuth", + "unable_to_move_to_locked_folder": "Nelze přesunout do uzamčenÊ sloÅžky", "unable_to_play_video": "Nelze přehrÃĄt video", "unable_to_reassign_assets_existing_person": "Nelze přeřadit poloÅžky na {name, select, null {existující osobu} other {{name}}}", "unable_to_reassign_assets_new_person": "Nelze přeřadit poloÅžku na novou osobu", @@ -920,6 +939,7 @@ "unable_to_remove_reaction": "Nelze odstranit reakci", "unable_to_repair_items": "Nelze opravit poloÅžky", "unable_to_reset_password": "Nelze obnovit heslo", + "unable_to_reset_pin_code": "Nelze resetovat PIN kÃŗd", "unable_to_resolve_duplicate": "Nelze vyřeÅĄit duplicitu", "unable_to_restore_assets": "Nelze obnovit poloÅžky", "unable_to_restore_trash": "Nelze obnovit koÅĄ", @@ -947,16 +967,15 @@ "unable_to_update_user": "Nelze aktualizovat uÅživatele", "unable_to_upload_file": "Nepodařilo se nahrÃĄt soubor" }, - "exif": "Exif", "exif_bottom_sheet_description": "Přidat popis...", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "LIDÉ", "exif_bottom_sheet_person_add_person": "Přidat jmÊno", - "exif_bottom_sheet_person_age": "{} let", - "exif_bottom_sheet_person_age_months": "{} měsíců", - "exif_bottom_sheet_person_age_year_months": "1 rok a {} měsíců", - "exif_bottom_sheet_person_age_years": "{} let", + "exif_bottom_sheet_person_age": "Věk {age}", + "exif_bottom_sheet_person_age_months": "{months} měsíců", + "exif_bottom_sheet_person_age_year_months": "1 rok a {months} měsíců", + "exif_bottom_sheet_person_age_years": "{years} let", "exit_slideshow": "Ukončit prezentaci", "expand_all": "Rozbalit vÅĄe", "experimental_settings_new_asset_list_subtitle": "ZpracovÃĄvÃĄm", @@ -968,15 +987,15 @@ "expires_date": "Platnost končí {date}", "explore": "Prozkoumat", "explorer": "Průzkumník", - "export": "Export", "export_as_json": "Exportovat jako JSON", "extension": "Přípona", "external": "Externí", "external_libraries": "Externí knihovny", "external_network": "Externí síÅĨ", - "external_network_sheet_info": "Pokud nejste v preferovanÊ síti WiFi, aplikace se připojí k serveru prostřednictvím první z níŞe uvedenÃŊch adres URL, kterÊ můŞe dosÃĄhnout, počínaje shora dolů", + "external_network_sheet_info": "Pokud nejste v preferovanÊ síti Wi-Fi, aplikace se připojí k serveru prostřednictvím první z níŞe uvedenÃŊch adres URL, kterÊ můŞe dosÃĄhnout, počínaje shora dolů", "face_unassigned": "Nepřiřazena", "failed": "Selhalo", + "failed_to_authenticate": "Ověření se nezdařilo", "failed_to_load_assets": "Nepodařilo se načíst poloÅžky", "failed_to_load_folder": "Nepodařilo se načíst sloÅžku", "favorite": "Oblíbit", @@ -992,6 +1011,7 @@ "filetype": "Typ souboru", "filter": "Filtr", "filter_people": "Filtrovat lidi", + "filter_places": "Filtrovat místa", "find_them_fast": "Najděte je rychle vyhledÃĄním jejich jmÊna", "fix_incorrect_match": "Opravit nesprÃĄvnou shodu", "folder": "SloÅžka", @@ -1040,7 +1060,9 @@ "home_page_delete_remote_err_local": "Místní poloÅžky ve vzdÃĄlenÊm vÃŊběru pro smazÃĄní, přeskakuji", "home_page_favorite_err_local": "Zatím není moÅžnÊ zařadit lokÃĄlní mÊdia mezi oblíbenÃĄ, přeskakuji", "home_page_favorite_err_partner": "PoloÅžky partnera nelze označit jako oblíbenÊ, přeskakuji", - "home_page_first_time_notice": "Pokud aplikaci pouŞívÃĄte poprvÊ, nezapomeňte si vybrat zÃĄlohovanÃĄ alba, aby se na časovÊ ose mohly nachÃĄzet fotografie a videa z vybranÃŊch alb.", + "home_page_first_time_notice": "Pokud aplikaci pouŞívÃĄte poprvÊ, nezapomeňte si vybrat zÃĄlohovanÃĄ alba, aby se na časovÊ ose mohly nachÃĄzet fotografie a videa z vybranÃŊch alb", + "home_page_locked_error_local": "Místní poloÅžky nelze přesunout do uzamčenÊ sloÅžky, přeskočí se", + "home_page_locked_error_partner": "PoloÅžky partnera nelze přesunout do uzamčenÊ sloÅžky, přeskočí se", "home_page_share_err_local": "Nelze sdílet místní poloÅžky prostřednictvím odkazu, přeskakuji", "home_page_upload_err_limit": "Lze nahrÃĄt nejvÃŊÅĄe 30 poloÅžek najednou, přeskakuji", "host": "Hostitel", @@ -1062,7 +1084,6 @@ "image_viewer_page_state_provider_download_started": "StahovÃĄní zahÃĄjeno", "image_viewer_page_state_provider_download_success": "StahovÃĄní bylo ÃēspÄ›ÅĄnÊ", "image_viewer_page_state_provider_share_error": "Chyba sdílení", - "immich_logo": "Immich Logo", "immich_web_interface": "WebovÊ rozhraní Immich", "import_from_json": "Import z JSONu", "import_path": "Cesta importu", @@ -1120,12 +1141,14 @@ "local_network": "Místní síÅĨ", "local_network_sheet_info": "Aplikace se při pouÅžití zadanÊ sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "OprÃĄvnění polohy", - "location_permission_content": "Aby bylo moÅžnÊ pouŞívat funkci automatickÊho přepínÃĄní, potřebuje Immich oprÃĄvnění k přesnÊ poloze, aby mohl přečíst nÃĄzev aktuÃĄlní WiFi sítě", + "location_permission_content": "Aby bylo moÅžnÊ pouŞívat funkci automatickÊho přepínÃĄní, potřebuje Immich oprÃĄvnění k přesnÊ poloze, aby mohl přečíst nÃĄzev aktuÃĄlní sítě Wi-Fi", "location_picker_choose_on_map": "Vyberte na mapě", "location_picker_latitude_error": "Zadejte platnou zeměpisnou ÅĄÃ­Å™ku", "location_picker_latitude_hint": "Zadejte vlastní zeměpisnou ÅĄÃ­Å™ku", "location_picker_longitude_error": "Zadejte platnou zeměpisnou dÊlku", "location_picker_longitude_hint": "Zadejte vlastní zeměpisnou dÊlku", + "lock": "Zamknout", + "locked_folder": "UzamčenÃĄ sloÅžka", "log_out": "OdhlÃĄsit", "log_out_all_devices": "OdhlÃĄsit vÅĄechna zařízení", "logged_out_all_devices": "VÅĄechna zařízení odhlÃĄÅĄena", @@ -1144,7 +1167,7 @@ "login_form_err_trailing_whitespace": "KoncovÃĄ mezera", "login_form_failed_get_oauth_server_config": "Chyba přihlÃĄÅĄení pomocí OAuth, zkontrolujte adresu URL serveru", "login_form_failed_get_oauth_server_disable": "Funkce OAuth není na tomto serveru dostupnÃĄ", - "login_form_failed_login": "Chyba přihlÃĄÅĄení, zkontrolujte URL adresu serveru, e-mail a heslo.", + "login_form_failed_login": "Chyba přihlÃĄÅĄení, zkontrolujte URL adresu serveru, e-mail a heslo", "login_form_handshake_exception": "DoÅĄlo k vÃŊjimce Handshake se serverem. Pokud pouŞívÃĄte self-signed certifikÃĄt, povolte v nastavení podporu self-signed certifikÃĄtu.", "login_form_password_hint": "heslo", "login_form_save_login": "Zůstat přihlÃĄÅĄen", @@ -1170,8 +1193,8 @@ "manage_your_devices": "SprÃĄva přihlÃĄÅĄenÃŊch zařízení", "manage_your_oauth_connection": "SprÃĄva OAuth propojení", "map": "Mapa", - "map_assets_in_bound": "{} fotka", - "map_assets_in_bounds": "{} fotek", + "map_assets_in_bound": "{count} fotka", + "map_assets_in_bounds": "{count} fotek", "map_cannot_get_user_location": "Nelze zjistit polohu uÅživatele", "map_location_dialog_yes": "Ano", "map_location_picker_page_use_location": "PouŞít tuto polohu", @@ -1185,15 +1208,18 @@ "map_settings": "Nastavení mapy", "map_settings_dark_mode": "TmavÃŊ reÅžim", "map_settings_date_range_option_day": "Posledních 24 hodin", - "map_settings_date_range_option_days": "Posledních {} dní", + "map_settings_date_range_option_days": "Posledních {days} dní", "map_settings_date_range_option_year": "Poslední rok", - "map_settings_date_range_option_years": "Poslední {} roky", + "map_settings_date_range_option_years": "Poslední {years} roky", "map_settings_dialog_title": "Nastavení map", "map_settings_include_show_archived": "Zahrnout archivovanÊ", "map_settings_include_show_partners": "Včetně partnerů", "map_settings_only_show_favorites": "Zobrazit pouze oblíbenÊ", "map_settings_theme_settings": "Motiv mapy", "map_zoom_to_see_photos": "OddÃĄlit pro zobrazení fotografií", + "mark_all_as_read": "Označit vÅĄe jako přečtenÊ", + "mark_as_read": "Označit jako přečtenÊ", + "marked_all_as_read": "VÅĄe označeno jako přečtenÊ", "matches": "Shody", "media_type": "Typ mÊdia", "memories": "Vzpomínky", @@ -1202,8 +1228,6 @@ "memories_setting_description": "SprÃĄva toho, co vidíte ve svÃŊch vzpomínkÃĄch", "memories_start_over": "Začít znovu", "memories_swipe_to_close": "Přejetím nahoru zavřete", - "memories_year_ago": "Před rokem", - "memories_years_ago": "Před {} lety", "memory": "Vzpomínka", "memory_lane_title": "Řada vzpomínek {title}", "menu": "Nabídka", @@ -1216,10 +1240,15 @@ "minimize": "Minimalizovat", "minute": "Minuta", "missing": "Chybějící", - "model": "Model", "month": "Měsíc", "monthly_title_text_date_format": "LLLL y", "more": "Více", + "move": "Přesunout", + "move_off_locked_folder": "Přesunout z uzamčenÊ sloÅžky", + "move_to_locked_folder": "Přesunout do uzamčenÊ sloÅžky", + "move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze vÅĄech alb a bude je moÅžnÊ zobrazit pouze v uzamčenÊ sloÅžce", + "moved_to_archive": "{count, plural, one {Přesunuta # poloÅžka} few {Přesunuty # poloÅžky} other {Přesunuto # poloÅžek}} do archivu", + "moved_to_library": "{count, plural, one {Přesunuta # poloÅžka} few {Přesunuty # poloÅžky} other {Přesunuto # poloÅžek}} do knihovny", "moved_to_trash": "Přesunuto do koÅĄe", "multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum poloÅžek pouze pro čtení, přeskakuji", "multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu poloÅžek pouze pro čtení, přeskakuji", @@ -1234,6 +1263,8 @@ "new_api_key": "NovÃŊ API klíč", "new_password": "NovÊ heslo", "new_person": "NovÃĄ osoba", + "new_pin_code": "NovÃŊ PIN kÃŗd", + "new_pin_code_subtitle": "PoprvÊ přistupujete k uzamčenÊ sloÅžce. Vytvořte si kÃŗd PIN pro bezpečnÃŊ přístup na tuto strÃĄnku", "new_user_created": "Vytvořen novÃŊ uÅživatel", "new_version_available": "NOVÁ VERZE K DISPOZICI", "newest_first": "NejnovějÅĄÃ­ první", @@ -1251,7 +1282,10 @@ "no_explore_results_message": "Nahrajte dalÅĄÃ­ fotografie a prozkoumejte svou sbírku.", "no_favorites_message": "Přidejte si oblíbenÊ poloÅžky a rychle najděte svÊ nejlepÅĄÃ­ obrÃĄzky a videa", "no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí", + "no_locked_photos_message": "Fotky a videa v uzamčenÊ sloÅžce jsou skrytÊ a při prochÃĄzení knihovny se nezobrazují.", "no_name": "Bez jmÊna", + "no_notifications": "ÅŊÃĄdnÃĄ oznÃĄmení", + "no_people_found": "Nebyli nalezeni ÅžÃĄdní odpovídající lidÊ", "no_places": "ÅŊÃĄdnÃĄ místa", "no_results": "ÅŊÃĄdnÊ vÃŊsledky", "no_results_description": "Zkuste pouŞít synonymum nebo obecnějÅĄÃ­ klíčovÊ slovo", @@ -1260,6 +1294,7 @@ "not_selected": "Není vybrÃĄno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li pouŞít ÅĄtítek ÃēloÅžiÅĄtě na dříve nahranÊ poloÅžky, spusÅĨte příkaz", "notes": "PoznÃĄmky", + "nothing_here_yet": "Zatím zde nic není", "notification_permission_dialog_content": "Chcete-li povolit oznÃĄmení, přejděte do nastavení a vyberte moÅžnost povolit.", "notification_permission_list_tile_content": "Udělte oprÃĄvnění k aktivaci oznÃĄmení.", "notification_permission_list_tile_enable_button": "Povolit oznÃĄmení", @@ -1267,12 +1302,9 @@ "notification_toggle_setting_description": "Povolení e-mailovÃŊch oznÃĄmení", "notifications": "OznÃĄmení", "notifications_setting_description": "SprÃĄva oznÃĄmení", - "oauth": "OAuth", "official_immich_resources": "OficiÃĄlní zdroje Immich", - "offline": "Offline", "offline_paths": "Offline cesty", "offline_paths_description": "Tyto vÃŊsledky mohou bÃŊt způsobeny ručním odstraněním souborů, kterÊ nejsou souÄÃĄstí externí knihovny.", - "ok": "Ok", "oldest_first": "NejstarÅĄÃ­ první", "on_this_device": "V tomto zařízení", "onboarding": "ZahÃĄjení", @@ -1280,8 +1312,8 @@ "onboarding_theme_description": "Zvolte si barevnÃŊ motiv pro svou instanci. MůŞete to později změnit v nastavení.", "onboarding_welcome_description": "Nastavíme vaÅĄi instanci pomocí několika běŞnÃŊch nastavení.", "onboarding_welcome_user": "Vítej, {user}", - "online": "Online", "only_favorites": "Pouze oblíbenÊ", + "open": "Otevřít", "open_in_map_view": "Otevřít v zobrazení mapy", "open_in_openstreetmap": "Otevřít v OpenStreetMap", "open_the_search_filters": "Otevřít vyhledÃĄvací filtry", @@ -1294,7 +1326,6 @@ "other_variables": "DalÅĄÃ­ proměnnÊ", "owned": "Vlastní", "owner": "Vlastník", - "partner": "Partner", "partner_can_access": "{partner} mÃĄ přístup", "partner_can_access_assets": "VÅĄechny vaÅĄe fotky a videa kromě těch, kterÊ jsou v sekcích ArchivovÃĄno a SmazÃĄno", "partner_can_access_location": "Místo, kde byly vaÅĄe fotografie pořízeny", @@ -1305,7 +1336,7 @@ "partner_page_partner_add_failed": "Nepodařilo se přidat partnera", "partner_page_select_partner": "Vyberte partnera", "partner_page_shared_to_title": "Sdíleno", - "partner_page_stop_sharing_content": "{} jiÅž nebude mít přístup k vaÅĄim fotografiím.", + "partner_page_stop_sharing_content": "{partner} jiÅž nebude mít přístup k vaÅĄim fotografiím.", "partner_sharing": "Sdílení mezi partnery", "partners": "Partneři", "password": "Heslo", @@ -1343,7 +1374,7 @@ "permission_onboarding_permission_limited": "Přístup omezen. Chcete-li pouŞívat Immich k zÃĄlohovÃĄní a sprÃĄvě celÊ vaÅĄÃ­ kolekce galerií, povolte v nastavení přístup k fotkÃĄm a videím.", "permission_onboarding_request": "Immich potřebuje přístup k zobrazení vaÅĄich fotek a videí.", "person": "Osoba", - "person_birthdate": "Narozen/a {date}", + "person_birthdate": "Narozen(a) {date}", "person_hidden": "{name}{hidden, select, true { (skryto)} other {}}", "photo_shared_all_users": "VypadÃĄ to, Åže jste fotky sdíleli se vÅĄemi uÅživateli, nebo nemÃĄte ÅžÃĄdnÊho uÅživatele, se kterÃŊm byste je mohli sdílet.", "photos": "Fotky", @@ -1351,6 +1382,10 @@ "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotek}}", "photos_from_previous_years": "Fotky z předchozích let", "pick_a_location": "Vyberte polohu", + "pin_code_changed_successfully": "PIN kÃŗd byl ÃēspÄ›ÅĄně změněn", + "pin_code_reset_successfully": "PIN kÃŗd ÃēspÄ›ÅĄně resetovÃĄn", + "pin_code_setup_successfully": "PIN kÃŗd ÃēspÄ›ÅĄně nastaven", + "pin_verification": "Ověření PIN kÃŗdu", "place": "Místo", "places": "Místa", "places_count": "{count, plural, one {{count, number} místo} few {{count, number} místa} other {{count, number} míst}}", @@ -1358,7 +1393,7 @@ "play_memories": "PřehrÃĄt vzpomníky", "play_motion_photo": "PřehrÃĄt pohybovou fotografii", "play_or_pause_video": "PřehrÃĄt nebo pozastavit video", - "port": "Port", + "please_auth_to_access": "Pro přístup se prosím ověřte", "preferences_settings_subtitle": "SprÃĄva předvoleb aplikace", "preferences_settings_title": "Předvolby", "preset": "Přednastavení", @@ -1368,11 +1403,11 @@ "previous_or_next_photo": "Předchozí nebo dalÅĄÃ­ fotka", "primary": "PrimÃĄrní", "privacy": "Soukromí", + "profile": "Profil", "profile_drawer_app_logs": "Logy", "profile_drawer_client_out_of_date_major": "Mobilní aplikace je zastaralÃĄ. Aktualizujte ji na nejnovějÅĄÃ­ hlavní verzi.", "profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralÃĄ. Aktualizujte ji na nejnovějÅĄÃ­ verzi.", "profile_drawer_client_server_up_to_date": "Klient a server jsou aktuÃĄlní", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server je zastaralÃŊ. Aktualizujte na nejnovějÅĄÃ­ hlavní verzi.", "profile_drawer_server_out_of_date_minor": "Server je zastaralÃŊ. Aktualizujte je na nejnovějÅĄÃ­ verzi.", "profile_image_of_user": "ProfilovÃŊ obrÃĄzek uÅživatele {user}", @@ -1381,7 +1416,7 @@ "public_share": "VeřejnÊ sdílení", "purchase_account_info": "Podporovatel", "purchase_activated_subtitle": "Děkujeme vÃĄm za podporu aplikace Immich a softwaru s otevřenÃŊm zdrojovÃŊm kÃŗdem", - "purchase_activated_time": "AktivovÃĄno dne {date, date}", + "purchase_activated_time": "AktivovÃĄno dne {date}", "purchase_activated_title": "VÃĄÅĄ klíč byl ÃēspÄ›ÅĄně aktivovÃĄn", "purchase_button_activate": "Aktivovat", "purchase_button_buy": "Koupit", @@ -1409,7 +1444,6 @@ "purchase_remove_server_product_key_prompt": "Opravdu chcete odebrat serverovÃŊ produktovÃŊ klíč?", "purchase_server_description_1": "Pro celÃŊ server", "purchase_server_description_2": "Stav podporovatele", - "purchase_server_title": "Server", "purchase_settings_server_activated": "ProduktovÃŊ klíč serveru spravuje sprÃĄvce", "rating": "Hodnocení hvězdičkami", "rating_clear": "Vyčistit hodnocení", @@ -1426,6 +1460,8 @@ "recent_searches": "NedÃĄvnÃĄ vyhledÃĄvÃĄní", "recently_added": "NedÃĄvno přidanÊ", "recently_added_page_title": "NedÃĄvno přidanÊ", + "recently_taken": "NedÃĄvno pořízenÊ", + "recently_taken_page_title": "NedÃĄvno pořízenÊ", "refresh": "Obnovit", "refresh_encoded_videos": "Obnovit kÃŗdovanÃĄ videa", "refresh_faces": "Obnovit obličeje", @@ -1445,6 +1481,8 @@ "remove_deleted_assets": "Odstranit offline soubory", "remove_from_album": "Odstranit z alba", "remove_from_favorites": "Odstranit z oblíbenÃŊch", + "remove_from_locked_folder": "Odstranit z uzamčenÊ sloÅžky", + "remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčenÊ sloÅžky? Budou viditelnÊ ve vaÅĄÃ­ knihovně.", "remove_from_shared_link": "Odstranit ze sdílenÊho odkazu", "remove_memory": "Odstranit vzpomínku", "remove_photo_from_memory": "Odstranit fotografii z tÊto vzpomínky", @@ -1468,6 +1506,7 @@ "reset": "VÃŊchozí", "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", + "reset_pin_code": "Resetovat PIN kÃŗd", "reset_to_default": "Obnovit vÃŊchozí nastavení", "resolve_duplicates": "VyřeÅĄit duplicity", "resolved_all_duplicates": "VyřeÅĄeny vÅĄechny duplicity", @@ -1478,8 +1517,6 @@ "resume": "Pokračovat", "retry_upload": "OpakovÃĄní nahrÃĄvÃĄní", "review_duplicates": "Kontrola duplicit", - "role": "Role", - "role_editor": "Editor", "role_viewer": "DivÃĄk", "save": "UloÅžit", "save_to_gallery": "UloÅžit do galerie", @@ -1560,6 +1597,7 @@ "select_keep_all": "Vybrat ponechat vÅĄe", "select_library_owner": "Vyberte vlastníka knihovny", "select_new_face": "VÃŊběr novÊho obličeje", + "select_person_to_tag": "Vyberte osobu, kterou chcete označit", "select_photos": "Vybrat fotky", "select_trash_all": "Vybrat vyhodit vÅĄe", "select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album", @@ -1590,12 +1628,12 @@ "setting_languages_apply": "PouŞít", "setting_languages_subtitle": "Změna jazyka aplikace", "setting_languages_title": "Jazyk", - "setting_notifications_notify_failures_grace_period": "OznÃĄmení o selhÃĄní zÃĄlohovÃĄní na pozadí: {}", - "setting_notifications_notify_hours": "{} hodin", + "setting_notifications_notify_failures_grace_period": "OznÃĄmení o selhÃĄní zÃĄlohovÃĄní na pozadí: {duration}", + "setting_notifications_notify_hours": "{count} hodin", "setting_notifications_notify_immediately": "okamÅžitě", - "setting_notifications_notify_minutes": "{} minut", + "setting_notifications_notify_minutes": "{count} minut", "setting_notifications_notify_never": "nikdy", - "setting_notifications_notify_seconds": "{} sekundy", + "setting_notifications_notify_seconds": "{count} sekund", "setting_notifications_single_progress_subtitle": "PodrobnÊ informace o průběhu nahrÃĄvÃĄní poloÅžky", "setting_notifications_single_progress_title": "Zobrazit průběh detailů zÃĄlohovÃĄní na pozadí", "setting_notifications_subtitle": "Přizpůsobení předvoleb oznÃĄmení", @@ -1607,10 +1645,12 @@ "settings": "Nastavení", "settings_require_restart": "Pro pouÅžití tohoto nastavení restartujte Immich", "settings_saved": "Nastavení uloÅženo", + "setup_pin_code": "Nastavení PIN kÃŗdu", "share": "Sdílet", "share_add_photos": "Přidat fotografie", - "share_assets_selected": "{} vybrÃĄno", + "share_assets_selected": "{count} vybrÃĄno", "share_dialog_preparing": "Připravuji...", + "share_link": "Sdílet odkaz", "shared": "SdílenÊ", "shared_album_activities_input_disable": "KomentÃĄÅ™ je vypnutÃŊ", "shared_album_activity_remove_content": "Chcete odstranit tuto aktivitu?", @@ -1623,34 +1663,33 @@ "shared_by_user": "Sdílel(a) {user}", "shared_by_you": "Sdíleli jste", "shared_from_partner": "Fotky od {partner}", - "shared_intent_upload_button_progress_text": "{} / {} nahrÃĄno", + "shared_intent_upload_button_progress_text": "{current} / {total} nahrÃĄno", "shared_link_app_bar_title": "SdílenÊ odkazy", "shared_link_clipboard_copied_massage": "ZkopírovÃĄno do schrÃĄnky", - "shared_link_clipboard_text": "Odkaz: {}\nHeslo: {}", + "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Chyba při vytvÃĄÅ™ení sdílenÊho odkazu", "shared_link_edit_description_hint": "Zadejte popis sdílení", "shared_link_edit_expire_after_option_day": "1 den", - "shared_link_edit_expire_after_option_days": "{} dní", + "shared_link_edit_expire_after_option_days": "{count} dní", "shared_link_edit_expire_after_option_hour": "1 hodina", - "shared_link_edit_expire_after_option_hours": "{} hodin", + "shared_link_edit_expire_after_option_hours": "{count} hodin", "shared_link_edit_expire_after_option_minute": "1 minuta", - "shared_link_edit_expire_after_option_minutes": "{} minut", - "shared_link_edit_expire_after_option_months": "{} měsíce", - "shared_link_edit_expire_after_option_year": "{} rok", + "shared_link_edit_expire_after_option_minutes": "{count} minut", + "shared_link_edit_expire_after_option_months": "{count} měsíce", + "shared_link_edit_expire_after_option_year": "{count} rok", "shared_link_edit_password_hint": "Zadejte heslo pro sdílení", "shared_link_edit_submit_button": "Aktualizovat odkaz", "shared_link_error_server_url_fetch": "Nelze načíst url serveru", - "shared_link_expires_day": "VyprÅĄÃ­ za {} den", - "shared_link_expires_days": "VyprÅĄÃ­ za {} dní", - "shared_link_expires_hour": "VyprÅĄÃ­ za {} hodinu", - "shared_link_expires_hours": "VyprÅĄÃ­ za {} hodin", - "shared_link_expires_minute": "VyprÅĄÃ­ za {} minutu", - "shared_link_expires_minutes": "VyprÅĄÃ­ za {} minut", + "shared_link_expires_day": "VyprÅĄÃ­ za {count} den", + "shared_link_expires_days": "VyprÅĄÃ­ za {count} dní", + "shared_link_expires_hour": "VyprÅĄÃ­ za {count} hodinu", + "shared_link_expires_hours": "VyprÅĄÃ­ za {count} hodin", + "shared_link_expires_minute": "VyprÅĄÃ­ za {count} minutu", + "shared_link_expires_minutes": "VyprÅĄÃ­ za {count} minut", "shared_link_expires_never": "Platnost ∞", - "shared_link_expires_second": "VyprÅĄÃ­ za {} sekundu", - "shared_link_expires_seconds": "VyprÅĄÃ­ za {} sekund", + "shared_link_expires_second": "VyprÅĄÃ­ za {count} sekundu", + "shared_link_expires_seconds": "VyprÅĄÃ­ za {count} sekund", "shared_link_individual_shared": "IndividuÃĄlní sdílení", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovat sdílenÊ odkazy", "shared_link_options": "MoÅžnosti sdílenÊho odkazu", "shared_links": "SdílenÊ odkazy", @@ -1713,7 +1752,6 @@ "stack_selected_photos": "Seskupení vybranÃŊch fotografií", "stacked_assets_count": "{count, plural, one {Seskupena # poloÅžka} few {Seskupeny # poloÅžky} other {Seskupeno # poloÅžek}}", "stacktrace": "VÃŊpis zÃĄsobníku", - "start": "Start", "start_date": "PoÄÃĄteční datum", "state": "StÃĄt", "status": "Stav", @@ -1723,6 +1761,7 @@ "stop_sharing_photos_with_user": "Přestat sdílet svÊ fotky s tímto uÅživatelem", "storage": "Velikost ÃēloÅžiÅĄtě", "storage_label": "Å títek ÃēloÅžiÅĄtě", + "storage_quota": "KvÃŗta ÃēloÅžiÅĄtě", "storage_usage": "VyuÅžito {used} z {available}", "submit": "Odeslat", "suggestions": "NÃĄvrhy", @@ -1749,7 +1788,7 @@ "theme_selection": "VÃŊběr motivu", "theme_selection_description": "AutomatickÊ nastavení světlÊho nebo tmavÊho motivu podle systÊmovÃŊch preferencí prohlíŞeče", "theme_setting_asset_list_storage_indicator_title": "Zobrazit indikÃĄtor ÃēloÅžiÅĄtě na dlaÅždicích poloÅžek", - "theme_setting_asset_list_tiles_per_row_title": "Počet poloÅžek na Å™ÃĄdek ({})", + "theme_setting_asset_list_tiles_per_row_title": "Počet poloÅžek na Å™ÃĄdek ({count})", "theme_setting_colorful_interface_subtitle": "PouŞít hlavní barvu na povrchy pozadí.", "theme_setting_colorful_interface_title": "BarevnÊ rozhraní", "theme_setting_image_viewer_quality_subtitle": "Přizpůsobení kvality detailů prohlíŞeče obrÃĄzků", @@ -1759,7 +1798,7 @@ "theme_setting_system_primary_color_title": "PouÅžití systÊmovÊ barvy", "theme_setting_system_theme_switch": "Automaticky (podle systemovÊho nastavení)", "theme_setting_theme_subtitle": "Vyberte nastavení tÊmatu aplikace", - "theme_setting_three_stage_loading_subtitle": "TřístupňovÊ načítÃĄní můŞe zvÃŊÅĄit vÃŊkonnost načítÃĄní, ale vede k vÃŊrazně vyÅĄÅĄÃ­mu zatíŞení sítě.", + "theme_setting_three_stage_loading_subtitle": "TřístupňovÊ načítÃĄní můŞe zvÃŊÅĄit vÃŊkonnost načítÃĄní, ale vede k vÃŊrazně vyÅĄÅĄÃ­mu zatíŞení sítě", "theme_setting_three_stage_loading_title": "Povolení třístupňovÊho načítÃĄní", "they_will_be_merged_together": "Budou sloučeny dohromady", "third_party_resources": "Zdroje třetích stran", @@ -1784,13 +1823,15 @@ "trash_no_results_message": "Zde se zobrazí odstraněnÊ fotky a videa.", "trash_page_delete_all": "Smazat vÅĄechny", "trash_page_empty_trash_dialog_content": "Chcete vyprÃĄzdnit svoje vyhozenÊ poloÅžky? Tyto poloÅžky budou trvale odstraněny z aplikace", - "trash_page_info": "VyhozenÊ poloÅžky budou trvale smazÃĄny po {} dnech", + "trash_page_info": "VyhozenÊ poloÅžky budou trvale smazÃĄny po {days} dnech", "trash_page_no_assets": "ÅŊÃĄdnÊ vyhozenÊ poloÅžky", "trash_page_restore_all": "Obnovit vÅĄechny", "trash_page_select_assets_btn": "Vybrat poloÅžky", - "trash_page_title": "KoÅĄ ({})", + "trash_page_title": "KoÅĄ ({count})", "trashed_items_will_be_permanently_deleted_after": "SmazanÊ poloÅžky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.", "type": "Typ", + "unable_to_change_pin_code": "Nelze změnit PIN kÃŗd", + "unable_to_setup_pin_code": "Nelze nastavit PIN kÃŗd", "unarchive": "Odarchivovat", "unarchived_count": "{count, plural, one {OdarchivovÃĄna #} few {OdarchivovÃĄny #} other {OdarchivovÃĄno #}}", "unfavorite": "ZruÅĄit oblíbení", @@ -1814,6 +1855,7 @@ "untracked_files": "NesledovanÊ soubory", "untracked_files_decription": "Tyto soubory nejsou aplikaci znÃĄmy. Mohou bÃŊt vÃŊsledkem neÃēspÄ›ÅĄnÃŊch přesunů, přeruÅĄenÊho nahrÃĄvÃĄní nebo mohou zůstat pozadu kvůli chybě", "up_next": "To je prozatím vÅĄe", + "updated_at": "AktualizovÃĄno", "updated_password": "Heslo aktualizovÃĄno", "upload": "NahrÃĄt", "upload_concurrency": "SouběŞnost nahrÃĄvÃĄní", @@ -1826,15 +1868,18 @@ "upload_status_errors": "Chyby", "upload_status_uploaded": "NahrÃĄno", "upload_success": "NahrÃĄní proběhlo ÃēspÄ›ÅĄně, obnovením strÃĄnky se zobrazí nově nahranÊ poloÅžky.", - "upload_to_immich": "NahrÃĄt do Immiche ({})", + "upload_to_immich": "NahrÃĄt do Immich ({count})", "uploading": "NahrÃĄvÃĄní", - "url": "URL", "usage": "VyuÅžití", + "use_biometric": "PouŞít biometrickÊ Ãēdaje", "use_current_connection": "pouŞít aktuÃĄlní připojení", "use_custom_date_range": "PouŞít vlastní rozsah dat", "user": "UÅživatel", + "user_has_been_deleted": "Tento uÅživatel byl smazÃĄn.", "user_id": "ID uÅživatele", "user_liked": "UÅživateli {user} se {type, select, photo {líbila tato fotka} video {líbilo toto video} asset {líbila tato poloÅžka} other {to líbilo}}", + "user_pin_code_settings": "PIN kÃŗd", + "user_pin_code_settings_description": "SprÃĄva vaÅĄeho PIN kÃŗdu", "user_purchase_settings": "NÃĄkup", "user_purchase_settings_description": "SprÃĄva vaÅĄeho nÃĄkupu", "user_role_set": "UÅživatel {user} nastaven jako {role}", @@ -1857,7 +1902,6 @@ "version_announcement_overlay_title": "K dispozici je novÃĄ verze serveru 🎉", "version_history": "Historie verzí", "version_history_item": "NainstalovÃĄno {version} dne {date}", - "video": "Video", "video_hover_setting": "PřehrÃĄvat miniaturu videa po najetí myÅĄÃ­", "video_hover_setting_description": "PřehrÃĄt miniaturu videa při najetí myÅĄÃ­ na poloÅžku. I kdyÅž je přehrÃĄvÃĄní vypnuto, lze jej spustit najetím na ikonu přehrÃĄvÃĄní.", "videos": "Videa", @@ -1883,11 +1927,12 @@ "week": "TÃŊden", "welcome": "Vítejte", "welcome_to_immich": "Vítejte v Immichi", - "wifi_name": "NÃĄzev WiFi", + "wifi_name": "NÃĄzev Wi-Fi", + "wrong_pin_code": "ChybnÃŊ PIN kÃŗd", "year": "Rok", "years_ago": "Před {years, plural, one {rokem} other {# lety}}", "yes": "Ano", "you_dont_have_any_shared_links": "NemÃĄte ÅžÃĄdnÊ sdílenÊ odkazy", - "your_wifi_name": "VÃĄÅĄ nÃĄzev WiFi", + "your_wifi_name": "NÃĄzev vaÅĄÃ­ Wi-Fi", "zoom_image": "ZvětÅĄit obrÃĄzek" } diff --git a/i18n/da.json b/i18n/da.json index 086b97f15a..eeec0ce036 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -26,6 +26,7 @@ "add_to_album": "Tilføj til album", "add_to_album_bottom_sheet_added": "Tilføjet til {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_locked_folder": "Tilføj til lÃĨst mappe", "add_to_shared_album": "Tilføj til delt album", "add_url": "Tilføj URL", "added_to_archive": "Tilføjet til arkiv", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Er du sikker pÃĨ at du vil deaktivere alle loginmuligheder? Login vil blive helt deaktiveret.", "authentication_settings_reenable": "Brug en server-kommando for at genaktivere.", "background_task_job": "Baggrundsopgaver", - "backup_database": "Backup Database", + "backup_database": "Lav Database Dump", "backup_database_enable_description": "SlÃĨ database-backup til", "backup_keep_last_amount": "MÃĻngde af tidligere backups, der skal gemmes", - "backup_settings": "Backup-indstillinger", - "backup_settings_description": "Administrer backupindstillinger for database", + "backup_settings": "Database Backup-indstillinger", + "backup_settings_description": "Administrer backupindstillinger for database. BemÃĻrk: Disse jobs er ikke overvÃĨget og du vil ikke blive notificeret ved fejl.", "check_all": "Tjek Alle", "cleanup": "Ryd op", "cleared_jobs": "Ryddet jobs til: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "For at bekrÃĻfte, skriv \"{email}\" herunder", "confirm_reprocess_all_faces": "Er du sikker pÃĨ, at du vil genbehandle alle ansigter? Dette vil ogsÃĨ rydde navngivne personer.", "confirm_user_password_reset": "Er du sikker pÃĨ, at du vil nulstille {user}s adgangskode?", + "confirm_user_pin_code_reset": "Er du sikker pÃĨ at du vil nulstille {user}'s PIN kode?", "create_job": "Opret job", "cron_expression": "Cron formel", "cron_expression_description": "Indstil skannings intervallet i cron format. For mere information se: Crontab Guru", @@ -70,8 +72,13 @@ "forcing_refresh_library_files": "Tvinger genopfriskning af alle biblioteksfiler", "image_format": "Format", "image_format_description": "WebP producerer mindre filer end JPEG, men er langsommere at komprimere.", + "image_fullsize_description": "Fuld størrelses billede uden metadata, brugt nÃĨr zoomet ind", + "image_fullsize_enabled": "Aktiver fuld størrelses billede generering", + "image_fullsize_enabled_description": "Generer fuld-størrelses billede for ikke-web-venlige formater. NÃĨr \"ForetrÃĻk indlejret forhÃĨndsvisning\" er slÃĨet til, bliver indlejrede forhÃĨndsvisninger brugt direkte uden konvertering. PÃĨvirker ikke web-venlige formater sÃĨsom JPEG.", + "image_fullsize_quality_description": "Fuld-størrelses billede kvalitet fra 1-100. Højere er bedre, men producerer større filer.", + "image_fullsize_title": "Full-størrelses billede indstillinger", "image_prefer_embedded_preview": "ForetrÃĻk indlejret forhÃĨndsvisning", - "image_prefer_embedded_preview_setting_description": "Brug indlejrede forhÃĨndsvisninger i RAW fotos som input til billedbehandling, nÃĨr det er tilgÃĻngeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhÃĨndsvisningen er kameraafhÃĻngig, og billedet kan have flere komprimeringsartefakter.", + "image_prefer_embedded_preview_setting_description": "Brug indlejrede forhÃĨndsvisninger i RAW fotos som input til billedbehandling og nÃĨr det er tilgÃĻngeligt. Dette kan give mere nøjagtige farver for nogle billeder, men kvaliteten af forhÃĨndsvisningen er kameraafhÃĻngig, og billedet kan have flere komprimeringsartefakter.", "image_prefer_wide_gamut": "ForetrÃĻkker bred farveskala", "image_prefer_wide_gamut_setting_description": "Brug Display P3 til miniaturebilleder. Dette bevarer billeder med brede farveskalaers dynamik bedre, men billeder kan komme til at se anderledes ud pÃĨ gamle enheder med en gammel browserversion. sRGB-billeder bliver beholdt som sRGB for at undgÃĨ farveskift.", "image_preview_description": "Mellemstørrelse billede med fjernet metadata, der bruges, nÃĨr du ser en enkelt mediefil og til machine learning", @@ -79,7 +86,7 @@ "image_preview_title": "Indstillinger for forhÃĨndsvisning", "image_quality": "Kvalitet", "image_resolution": "Opløsning", - "image_resolution_description": "højere opløsning indeholder flere detaljer, men tager lÃĻngere tid at processerer, giver større filer og sÃĻnker svartiderne i applikationen.", + "image_resolution_description": "Højere opløsning indeholder flere detaljer, men tager lÃĻngere tid at processerer, giver større filer og sÃĻnker svartiderne i applikationen.", "image_settings": "Billedindstillinger", "image_settings_description": "Administrer kvaliteten og opløsningen af genererede billeder", "image_thumbnail_description": "SmÃĨ miniaturer uden metadata, bruges nÃĨr der ses samlinger eller den primÃĻre tidslinie", @@ -187,26 +194,22 @@ "oauth_auto_register": "AutoregistrÊr", "oauth_auto_register_description": "RegistrÊr automatisk nye brugere efter at have logget ind med OAuth", "oauth_button_text": "Knaptekst", - "oauth_client_id": "Kunde-ID", - "oauth_client_secret": "Kundehemmelighed", + "oauth_client_secret_description": "PÃĨkrÃĻvet hvis PKCE (Proof Key for Code Exchange) ikke er supporteret af OAuth-udbyderen", "oauth_enable_description": "Log ind med OAuth", - "oauth_issuer_url": "Udsteder-URL", "oauth_mobile_redirect_uri": "Mobilomdiregerings-URL", "oauth_mobile_redirect_uri_override": "TilsidesÃĻttelse af mobil omdiregerings-URL", "oauth_mobile_redirect_uri_override_description": "Aktiver, nÃĨr OAuth-udbyderen ikke tillader en mobil URI, som '{callback}'", - "oauth_profile_signing_algorithm": "Log-ind-algoritme", - "oauth_profile_signing_algorithm_description": "Algoritme til signering af brugerprofilen.", - "oauth_scope": "Omfang", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer OAuth login-indstillinger", "oauth_settings_more_details": "LÃĻs flere detaljer om funktionen i dokumentationen.", - "oauth_signing_algorithm": "Signeringsalgoritme", "oauth_storage_label_claim": "LagringsmÃĻrkat fordring", "oauth_storage_label_claim_description": "SÃĻt automatisk brugerens lagringsmÃĻrkat til denne fordrings vÃĻrdi.", "oauth_storage_quota_claim": "Lagringskvotefordring", "oauth_storage_quota_claim_description": "SÃĻt automatisk brugeres lagringskvote til denne nye fordrings vÃĻrdi.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", "oauth_storage_quota_default_description": "Kvote i GiB som bruges, nÃĨr der ikke bliver oplyst en fordring (Indtast 0 for uendelig kvote).", + "oauth_timeout": "Forespørgslen udløb", + "oauth_timeout_description": "Udløbstid for forespørgsel i milisekunder", "offline_paths": "Offline-stier", "offline_paths_description": "Disse resultater kan vÃĻre pÃĨ grund af manuel sletning af filer, som ikke er en del af et eksternt bibliotek.", "password_enable_description": "Log ind med email og adgangskode", @@ -347,6 +350,7 @@ "user_delete_delay_settings_description": "Antal dage efter fjernelse for permanent at slette en brugers konto og mediefiler. Opgaven for sletning af brugere kører ved midnat for at tjekke efter brugere, der er klar til sletning. Ændringer i denne indstilling vil blive evalueret ved nÃĻste udførelse.", "user_delete_immediately": "{user}'s konto og aktiver vil blive sat i kø til permanent sletning med det samme.", "user_delete_immediately_checkbox": "SÃĻt bruger og aktiver i kø til øjeblikkelig sletning", + "user_details": "Brugeroplysninger", "user_management": "Brugeradministration", "user_password_has_been_reset": "Brugerens adgangskode er blevet nulstillet:", "user_password_reset_description": "Venligst oplys brugeren om den midlertidige adgangskode og informÊr dem, at de vil vÃĻre nødt til at ÃĻndre adgangskoden ved nÃĻste login.", @@ -364,15 +368,17 @@ }, "admin_email": "Administrator-email", "admin_password": "Administratoradgangskode", - "administration": "Administration", "advanced": "Avanceret", - "advanced_settings_log_level_title": "Logniveau: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Brug denne valgmulighed for at filtrere media under synkronisering baseret pÃĨ alternative kriterier. Prøv kun denne hvis du har problemer med at appen ikke opdager alle albums.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTEL] Brug alternativ enheds album synkroniserings filter", + "advanced_settings_log_level_title": "Logniveau: {level}", "advanced_settings_prefer_remote_subtitle": "Nogle enheder tager meget lang tid om at indlÃĻse miniaturebilleder af elementer pÃĨ enheden. Aktiver denne indstilling for i stedetat indlÃĻse elementer fra serveren.", "advanced_settings_prefer_remote_title": "ForetrÃĻk elementer pÃĨ serveren", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_proxy_headers_subtitle": "Definer proxy headers Immich skal sende med hver netvÃĻrks forespørgsel", "advanced_settings_self_signed_ssl_subtitle": "Spring verificering af SSL-certifikat over for serverens endelokation. KrÃĻves for selvsignerede certifikater.", "advanced_settings_self_signed_ssl_title": "Tillad selvsignerede certifikater", + "advanced_settings_sync_remote_deletions_subtitle": "Slet eller gendan automatisk en mediefil pÃĨ denne enhed, nÃĨr denne handling foretages pÃĨ Immich webinterface", + "advanced_settings_sync_remote_deletions_title": "Synkroniser fjernsletninger [EKSPERIMENTELT]", "advanced_settings_tile_subtitle": "Avancerede brugerindstillinger", "advanced_settings_troubleshooting_subtitle": "SlÃĨ ekstra funktioner for fejlsøgning til", "advanced_settings_troubleshooting_title": "Fejlsøgning", @@ -395,9 +401,9 @@ "album_remove_user_confirmation": "Er du sikker pÃĨ at du vil fjerne {user}?", "album_share_no_users": "Det ser ud til at du har delt denne album med alle brugere, eller du har ikke nogen brugere til at dele med.", "album_thumbnail_card_item": "1 genstand", - "album_thumbnail_card_items": "{} genstande", - "album_thumbnail_card_shared": ". Delt", - "album_thumbnail_shared_by": "Delt af {}", + "album_thumbnail_card_items": "{count} genstande", + "album_thumbnail_card_shared": " ¡ Delt", + "album_thumbnail_shared_by": "Delt af {user}", "album_updated": "Album opdateret", "album_updated_setting_description": "Modtag en emailnotifikation nÃĨr et delt album fÃĨr nye mediefiler", "album_user_left": "Forlod {album}", @@ -435,7 +441,7 @@ "archive": "Arkiv", "archive_or_unarchive_photo": "ArkivÊr eller dearkivÊr billede", "archive_page_no_archived_assets": "Ingen arkiverede elementer blev fundet", - "archive_page_title": "ArkivÊr ({})", + "archive_page_title": "ArkivÊr ({count})", "archive_size": "Arkiv størelse", "archive_size_description": "Konfigurer arkivstørrelsen for downloads (i GiB)", "archived": "Arkiveret", @@ -449,13 +455,11 @@ "asset_description_updated": "Mediefilsbeskrivelse er blevet opdateret", "asset_filename_is_offline": "Mediefil {filename} er offline", "asset_has_unassigned_faces": "Aktivet har ikke-tildelte ansigter", - "asset_hashing": "Hashingâ€Ļ", "asset_list_group_by_sub_title": "GruppÊr efter", "asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout", "asset_list_layout_settings_group_automatically": "Automatisk", - "asset_list_layout_settings_group_by": "GruppÊr elementer pr. ", + "asset_list_layout_settings_group_by": "GruppÊr elementer pr.", "asset_list_layout_settings_group_by_month_day": "MÃĨned + dag", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Indstillinger for billedgitterlayout", "asset_list_settings_title": "Billedgitter", "asset_offline": "Mediefil offline", @@ -472,18 +476,18 @@ "assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet", "assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {{name}} other {nyt album}}", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", - "assets_deleted_permanently": "{} element(er) blev fjernet permanent", - "assets_deleted_permanently_from_server": "{} element(er) blev fjernet permanent fra serveren", + "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", + "assets_deleted_permanently_from_server": "{count} element(er) blev fjernet permanent fra Immich serveren", "assets_moved_to_trash_count": "Flyttede {count, plural, one {# mediefil} other {# mediefiler}} til papirkurven", "assets_permanently_deleted_count": "{count, plural, one {# mediefil} other {# mediefiler}} slettet permanent", "assets_removed_count": "Fjernede {count, plural, one {# mediefil} other {# mediefiler}}", - "assets_removed_permanently_from_device": "{} element(er) blev fjernet permanent fra din enhed", + "assets_removed_permanently_from_device": "{count} element(er) blev fjernet permanent fra din enhed", "assets_restore_confirmation": "Er du sikker pÃĨ, at du vil gendanne alle dine mediafiler i papirkurven? Du kan ikke fortryde denne handling! BemÃĻrk, at offline mediefiler ikke kan gendannes pÃĨ denne mÃĨde.", "assets_restored_count": "{count, plural, one {# mediefil} other {# mediefiler}} gendannet", - "assets_restored_successfully": "{} element(er) blev gendannet succesfuldt", - "assets_trashed": "{} element(er) blev smidt i papirkurven", + "assets_restored_successfully": "{count} element(er) blev gendannet succesfuldt", + "assets_trashed": "{count} element(er) blev smidt i papirkurven", "assets_trashed_count": "{count, plural, one {# mediefil} other {# mediefiler}} smidt i papirkurven", - "assets_trashed_from_server": "{} element(er) blev smidt i serverens papirkurv", + "assets_trashed_from_server": "{count} element(er) blev smidt i Immich serverens papirkurv", "assets_were_part_of_album_count": "mediefil{count, plural, one {mediefil} other {mediefiler}} er allerede en del af albummet", "authorized_devices": "Tilladte enheder", "automatic_endpoint_switching_subtitle": "Forbind lokalt over det anviste WiFi, nÃĨr det er tilgÃĻngeligt og brug alternative forbindelser andre stÃĻder", @@ -492,46 +496,45 @@ "back_close_deselect": "Tilbage, luk eller fravÃĻlg", "background_location_permission": "Tilladelse til baggrundsplacering", "background_location_permission_content": "For at skifte netvÃĻrk, nÃĨr appen kører i baggrunden, skal Immich *altid* have prÃĻcis placeringsadgang, sÃĨ appen kan lÃĻse WiFi-netvÃĻrkets navn", - "backup_album_selection_page_albums_device": "Albummer pÃĨ enhed ({})", + "backup_album_selection_page_albums_device": "Albummer pÃĨ enheden ({count})", "backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere", "backup_album_selection_page_assets_scatter": "Elementer kan vÃĻre spredt pÃĨ tvÃĻrs af flere albummer. Albummer kan sÃĨledes inkluderes eller udelukkes under sikkerhedskopieringsprocessen.", "backup_album_selection_page_select_albums": "VÃĻlg albummer", "backup_album_selection_page_selection_info": "Oplysninger om valgte", "backup_album_selection_page_total_assets": "Samlede unikke elementer", "backup_all": "Alt", - "backup_background_service_backup_failed_message": "Sikkerhedskopiering af elementer fejlede. Forsøger igen...", - "backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igen...", - "backup_background_service_current_upload_notification": "Uploader {}", - "backup_background_service_default_notification": "Søger efter nye elementer...", + "backup_background_service_backup_failed_message": "Sikkerhedskopiering af elementer fejlede. Forsøger igenâ€Ļ", + "backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igenâ€Ļ", + "backup_background_service_current_upload_notification": "Uploader {filename}", + "backup_background_service_default_notification": "Søger efter nye elementerâ€Ļ", "backup_background_service_error_title": "Fejl med sikkerhedskopiering", - "backup_background_service_in_progress_notification": "Tager sikkerhedskopi af dine elementer...", - "backup_background_service_upload_failure_notification": "Fejlede med uploade af {}", + "backup_background_service_in_progress_notification": "Tager sikkerhedskopi af dine elementerâ€Ļ", + "backup_background_service_upload_failure_notification": "Fejlede med uploade af {filename}", "backup_controller_page_albums": "SikkerhedskopiÊr albummer", "backup_controller_page_background_app_refresh_disabled_content": "SlÃĨ baggrundsopdatering af applikationen til i Indstillinger > Generelt > Baggrundsopdatering af applikationer, for at bruge sikkerhedskopi i baggrunden.", "backup_controller_page_background_app_refresh_disabled_title": "Baggrundsopdatering af app er slÃĨet fra", "backup_controller_page_background_app_refresh_enable_button_text": "GÃĨ til indstillinger", "backup_controller_page_background_battery_info_link": "Vis mig hvordan", "backup_controller_page_background_battery_info_message": "For den bedste oplevelse med sikkerhedskopiering i baggrunden, bør du slÃĨ batterioptimering, der begrÃĻnder baggrundsaktivitet, fra.\n\nSiden dette er afhÃĻngigt af enheden, bør du undersøge denne information leveret af din enheds producent.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Batterioptimering", "backup_controller_page_background_charging": "Kun under opladning", "backup_controller_page_background_configure_error": "Fejlede konfigureringen af sikkerhedskopiering i baggrunden", - "backup_controller_page_background_delay": "Udskyd sikkerhedskopi af nye elementer: {}", + "backup_controller_page_background_delay": "Udskyd sikkerhedskopi af nye elementer: {duration}", "backup_controller_page_background_description": "SlÃĨ sikkerhedskopiering i baggrunden til, for automatisk at tage sikkerhedskopi af nye elementer, uden at skulle ÃĨbne appen", "backup_controller_page_background_is_off": "Automatisk sikkerhedskopiering i baggrunden er slÃĨet fra", "backup_controller_page_background_is_on": "Automatisk sikkerhedskopiering i baggrunden er slÃĨet til", "backup_controller_page_background_turn_off": "SlÃĨ sikkerhedskopiering i baggrunden fra", "backup_controller_page_background_turn_on": "SlÃĨ sikkerhedskopiering i baggrunden til", - "backup_controller_page_background_wifi": "Kun med WiFi", + "backup_controller_page_background_wifi": "Kun med Wi-Fi", "backup_controller_page_backup": "Sikkerhedskopier", "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Sikkerhedskopierede billeder og videoer", - "backup_controller_page_created": "Oprettet den: {}", + "backup_controller_page_created": "Oprettet den: {date}", "backup_controller_page_desc_backup": "SlÃĨ sikkerhedskopiering til automatisk at uploade nye elementer til serveren.", "backup_controller_page_excluded": "Ekskluderet: ", - "backup_controller_page_failed": "Felet ({})", - "backup_controller_page_filename": "Filnavn: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Fejlet ({count})", + "backup_controller_page_filename": "Filnavn: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Sikkerhedskopieringsinformation", "backup_controller_page_none_selected": "Ingen valgte", "backup_controller_page_remainder": "TilbagevÃĻrende", @@ -540,7 +543,7 @@ "backup_controller_page_start_backup": "Start sikkerhedskopiering", "backup_controller_page_status_off": "Sikkerhedskopiering er slÃĨet fra", "backup_controller_page_status_on": "Sikkerhedskopiering er slÃĨet til", - "backup_controller_page_storage_format": "{} af {} brugt", + "backup_controller_page_storage_format": "{used} af {total} brugt", "backup_controller_page_to_backup": "Albummer at sikkerhedskopiere", "backup_controller_page_total_sub": "Alle unikke billeder og videoer fra valgte albummer", "backup_controller_page_turn_off": "SlÃĨ sikkerhedskopiering fra", @@ -555,6 +558,10 @@ "backup_options_page_title": "Backupindstillinger", "backup_setting_subtitle": "Administrer indstillnger for upload i forgrund og baggrund", "backward": "BaglÃĻns", + "biometric_auth_enabled": "Biometrisk adgangskontrol slÃĨet til", + "biometric_locked_out": "Du er lÃĨst ude af biometrisk adgangskontrol", + "biometric_no_options": "Ingen biometrisk adgangskontrol tilgÃĻngelig", + "biometric_not_available": "Biometrisk adgangskontrol er ikke tilgÃĻngelig pÃĨ denne enhed", "birthdate_saved": "Fødselsdatoen blev gemt", "birthdate_set_description": "Fødselsdato bruges til at beregne alderen pÃĨ denne person pÃĨ tidspunktet for et billede.", "blurred_background": "Sløret baggrund", @@ -565,21 +572,21 @@ "bulk_keep_duplicates_confirmation": "Er du sikker pÃĨ, at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}}? Dette vil løse alle dubletgrupper uden at slette noget.", "bulk_trash_duplicates_confirmation": "Er du sikker pÃĨ, at du vil masseslette {count, plural, one {# duplikeret objekt} other {# duplikerede objekter}}? Dette vil beholde det største objekt i hver gruppe og slette alle andre dubletter.", "buy": "Køb Immich", - "cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({} elementer)", + "cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({count} mediefiler)", "cache_settings_clear_cache_button": "Fjern cache", "cache_settings_clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad pÃĨvirke appens ydeevne indtil cachen er genopbygget.", "cache_settings_duplicated_assets_clear_button": "RYD", "cache_settings_duplicated_assets_subtitle": "Billeder og videoer der er sortlistet af appen", - "cache_settings_duplicated_assets_title": "Dublikerede elementer ({})", - "cache_settings_image_cache_size": "Størrelse af billedecache ({} elementer)", + "cache_settings_duplicated_assets_title": "Dublikerede elementer ({count})", + "cache_settings_image_cache_size": "Størrelse af billedecache ({count} elementer)", "cache_settings_statistics_album": "Biblioteksminiaturer", - "cache_settings_statistics_assets": "{} elementer ({})", + "cache_settings_statistics_assets": "{count} elementer ({size})", "cache_settings_statistics_full": "Fulde billeder", "cache_settings_statistics_shared": "Miniaturebilleder til delte albummer", "cache_settings_statistics_thumbnail": "Miniaturebilleder", "cache_settings_statistics_title": "Cacheforbrug", - "cache_settings_subtitle": "HÃĨndter cache-adfÃĻrden for Immich-appen.", - "cache_settings_thumbnail_size": "Størrelse af miniaturebillede cache ({} elementer)", + "cache_settings_subtitle": "HÃĨndter cache-adfÃĻrden for Immich-appen", + "cache_settings_thumbnail_size": "Størrelse af miniaturebillede cache ({count} elementer)", "cache_settings_tile_subtitle": "Kontroller den lokale lagerplads", "cache_settings_tile_title": "Lokal lagerplads", "cache_settings_title": "Cache-indstillinger", @@ -588,11 +595,12 @@ "camera_model": "Kameramodel", "cancel": "AnnullÊr", "cancel_search": "AnnullÊr søgning", - "canceled": "Canceled", + "canceled": "Annulleret", "cannot_merge_people": "Kan ikke sammenflette personer", "cannot_undo_this_action": "Du kan ikke fortryde denne handling!", "cannot_update_the_description": "Kan ikke opdatere beskrivelsen", "change_date": "Ændr dato", + "change_description": "Beskrivelse af ÃĻndringer", "change_display_order": "Ændrer visningsrÃĻkkefølge", "change_expiration_time": "Ændr udløbstidspunkt", "change_location": "Ændr sted", @@ -605,6 +613,7 @@ "change_password_form_new_password": "Nyt kodeord", "change_password_form_password_mismatch": "Kodeord er ikke ens", "change_password_form_reenter_new_password": "Gentag nyt kodeord", + "change_pin_code": "Skift PIN kode", "change_your_password": "Skift dit kodeord", "changed_visibility_successfully": "Synlighed blev ÃĻndret", "check_all": "MarkÊr alle", @@ -619,14 +628,11 @@ "clear_all_recent_searches": "Ryd alle seneste søgninger", "clear_message": "Ryd bedsked", "clear_value": "Ryd vÃĻrdi", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "Klient certifikat er importeret", + "client_cert_invalid_msg": "Invalid certifikat fil eller forkert adgangskode", + "client_cert_remove_msg": "Klient certifikat er fjernet", + "client_cert_subtitle": "Supportere kin PKCS12 (.p12, .pfx) Certifikat importering/fjernelse er kun tilgÃĻngeligt før login", + "client_cert_title": "SSL Klient Certifikat", "clockwise": "Med uret", "close": "Luk", "collapse": "Klap sammen", @@ -639,23 +645,24 @@ "comments_are_disabled": "Kommentarer er slÃĨet fra", "common_create_new_album": "Opret et nyt album", "common_server_error": "Tjek din internetforbindelse, sørg for at serveren er tilgÃĻngelig og at app- og serversioner er kompatible.", - "completed": "Completed", + "completed": "Fuldført", "confirm": "BekrÃĻft", "confirm_admin_password": "BekrÃĻft administratoradgangskode", "confirm_delete_face": "Er du sikker pÃĨ, du vil slette {name}s ansigt fra denne mediefil?", "confirm_delete_shared_link": "Er du sikker pÃĨ, at du vil slette dette delte link?", "confirm_keep_this_delete_others": "Alle andre aktiver i stakken vil blive slettet undtagen dette aktiv. Er du sikker pÃĨ, at du vil fortsÃĻtte?", + "confirm_new_pin_code": "BekrÃĻft ny PIN kode", "confirm_password": "BekrÃĻft adgangskode", "contain": "InddÃĻm", "context": "Kontekst", "continue": "FortsÃĻt", - "control_bottom_app_bar_album_info_shared": "{} genstande â€ĸ Delt", + "control_bottom_app_bar_album_info_shared": "{count} genstande â€ĸ Delt", "control_bottom_app_bar_create_new_album": "Opret nyt album", "control_bottom_app_bar_delete_from_immich": "Slet fra Immich", "control_bottom_app_bar_delete_from_local": "Slet fra enhed", "control_bottom_app_bar_edit_location": "Rediger placering", "control_bottom_app_bar_edit_time": "Rediger tid og dato", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Del Link", "control_bottom_app_bar_share_to": "Del til", "control_bottom_app_bar_trash_from_immich": "Flyt til papirkurv", "copied_image_to_clipboard": "Kopierede billede til clipboard.", @@ -682,14 +689,16 @@ "create_new_person_hint": "Tildel valgte aktiver til en ny person", "create_new_user": "Opret ny bruger", "create_shared_album_page_share_add_assets": "TILFØJ ELEMENT", - "create_shared_album_page_share_select_photos": "VÃĻlg billeder", + "create_shared_album_page_share_select_photos": "VÃĻlg Billeder", "create_tag": "Opret tag", "create_tag_description": "Opret et nyt tag. For indlejrede tags skal du indtaste den fulde sti til tagget inklusive skrÃĨstreger.", "create_user": "Opret bruger", "created": "Oprettet", + "created_at": "Oprettet", "crop": "BeskÃĻr", "curated_object_page_title": "Ting", "current_device": "NuvÃĻrende enhed", + "current_pin_code": "NuvÃĻrende PIN kode", "current_server_address": "NuvÃĻrende serveraddresse", "custom_locale": "Brugerdefineret lokale", "custom_locale_description": "FormatÊr datoer og tal baseret pÃĨ sproget og regionen", @@ -741,7 +750,6 @@ "direction": "Retning", "disabled": "Deaktiveret", "disallow_edits": "DeaktivÊr redigeringer", - "discord": "Discord", "discover": "Opdag", "dismiss_all_errors": "Afvis alle fejl", "dismiss_error": "Afvis fejl", @@ -758,13 +766,12 @@ "download_enqueue": "Donload sat i kø", "download_error": "Fejl med download", "download_failed": "Download mislykkes", - "download_filename": "fil: {}", + "download_filename": "fil: {filename}", "download_finished": "Download afsluttet", "download_include_embedded_motion_videos": "Indlejrede videoer", "download_include_embedded_motion_videos_description": "Inkluder videoer indlejret i levende billeder som en separat fil", "download_notfound": "Download ikke fundet", "download_paused": "Download pauset", - "download_settings": "Download", "download_settings_description": "Administrer indstillinger relateret til mediefil-downloads", "download_started": "Download startet", "download_sucess": "Download fÃĻrdig", @@ -782,6 +789,8 @@ "edit_avatar": "RedigÊr avatar", "edit_date": "RedigÊr dato", "edit_date_and_time": "RedigÊr dato og tid", + "edit_description": "Rediger beskrivelse", + "edit_description_prompt": "VÃĻlg venligst en ny beskrivelse:", "edit_exclusion_pattern": "RedigÊr udelukkelsesmønster", "edit_faces": "RedigÊr ansigter", "edit_import_path": "RedigÊr import-sti", @@ -800,21 +809,24 @@ "editor_close_without_save_prompt": "Ændringerne vil ikke blive gemt", "editor_close_without_save_title": "Luk editor?", "editor_crop_tool_h2_aspect_ratios": "Størrelsesforhold", - "editor_crop_tool_h2_rotation": "Rotation", "email": "E-mail", - "empty_folder": "This folder is empty", + "email_notifications": "Email notifikationer", + "empty_folder": "Denne mappe er tom", "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker pÃĨ, at du vil tømme papirkurven? Dette vil fjerne alle objekter i papirkurven permanent fra Immich.\nDu kan ikke fortryde denne handling!", "enable": "AktivÊr", + "enable_biometric_auth_description": "Indtast din PIN kode for at slÃĨ biometrisk adgangskontrol til", "enabled": "Aktiveret", "end_date": "Slutdato", - "enqueued": "Enqueued", - "enter_wifi_name": "Indtast WiFi-navn", + "enqueued": "I kø", + "enter_wifi_name": "Indtast Wi-Fi-navn", + "enter_your_pin_code": "Indtast din PIN kode", + "enter_your_pin_code_subtitle": "Indtast din PIN kode for at tilgÃĨ den lÃĨste mappe", "error": "Fejl", "error_change_sort_album": "Ændring af sorteringsrÃĻkkefølgen mislykkedes", "error_delete_face": "Fejl ved sletning af ansigt fra mediefil", "error_loading_image": "Fejl ved indlÃĻsning af billede", - "error_saving_image": "Fejl: {}", + "error_saving_image": "Fejl: {error}", "error_title": "Fejl - Noget gik galt", "errors": { "cannot_navigate_next_asset": "Kan ikke navigere til nÃĻste mediefil", @@ -844,10 +856,12 @@ "failed_to_keep_this_delete_others": "Kunne ikke beholde denne mediefil og slette de andre mediefiler", "failed_to_load_asset": "IndlÃĻsning af mediefil mislykkedes", "failed_to_load_assets": "IndlÃĻsning af mediefiler mislykkedes", + "failed_to_load_notifications": "Kunne ikke indlÃĻse notifikationer", "failed_to_load_people": "IndlÃĻsning af personer mislykkedes", "failed_to_remove_product_key": "Fjernelse af produktnøgle mislykkedes", "failed_to_stack_assets": "Det lykkedes ikke at stable mediefiler", "failed_to_unstack_assets": "Det lykkedes ikke at fjerne gruperingen af mediefiler", + "failed_to_update_notification_status": "Kunne ikke uploade notifikations status", "import_path_already_exists": "Denne importsti findes allerede.", "incorrect_email_or_password": "Forkert email eller kodeord", "paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering", @@ -865,6 +879,7 @@ "unable_to_archive_unarchive": "Ude af stand til at {archived, select, true {arkivere} other {fjerne fra arkiv}}", "unable_to_change_album_user_role": "Ikke i stand til at ÃĻndre albumbrugerens rolle", "unable_to_change_date": "Ikke i stand til at ÃĻndre dato", + "unable_to_change_description": "Kunne ikke ÃĻndre beskrivelsen", "unable_to_change_favorite": "Kan ikke ÃĻndre favorit for mediefil", "unable_to_change_location": "Ikke i stand til at ÃĻndre sted", "unable_to_change_password": "Kunne ikke ÃĻndre adgangskode", @@ -902,6 +917,7 @@ "unable_to_log_out_all_devices": "Kan ikke logge af alle enheder", "unable_to_log_out_device": "Enheden kunne ikke logges af", "unable_to_login_with_oauth": "Kan ikke logge pÃĨ med OAuth", + "unable_to_move_to_locked_folder": "Kunne ikke flytte til lÃĨst mappe", "unable_to_play_video": "Ikke i stand til at afspille video", "unable_to_reassign_assets_existing_person": "Kunne ikke tildele mediafiler til {name, select, null {en eksisterende person} other {{name}}}", "unable_to_reassign_assets_new_person": "Kan ikke omfordele objekter til en ny person", @@ -915,6 +931,7 @@ "unable_to_remove_reaction": "Ikke i stand til at fjerne reaktion", "unable_to_repair_items": "Ikke i stand til at reparere ting", "unable_to_reset_password": "Ikke i stand til at nulstille adgangskode", + "unable_to_reset_pin_code": "Kunne ikke nulstille din PIN kode", "unable_to_resolve_duplicate": "Kunne ikke opklare duplikat", "unable_to_restore_assets": "Kunne ikke gendanne medierfil", "unable_to_restore_trash": "Ikke i stand til at gendanne fra skraldespanden", @@ -942,16 +959,15 @@ "unable_to_update_user": "Ikke i stand til at opdatere bruger", "unable_to_upload_file": "Filen kunne ikke uploades" }, - "exif": "Exif", "exif_bottom_sheet_description": "Tilføj beskrivelse...", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "LOKATION", "exif_bottom_sheet_people": "PERSONER", "exif_bottom_sheet_person_add_person": "Tilføj navn", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Alder {age}", + "exif_bottom_sheet_person_age_months": "Alder {months} mÃĨned(er)", + "exif_bottom_sheet_person_age_year_months": "Alder 1 ÃĨr, {months} mÃĨned(er)", + "exif_bottom_sheet_person_age_years": "Alder {years}", "exit_slideshow": "Afslut slideshow", "expand_all": "Udvid alle", "experimental_settings_new_asset_list_subtitle": "Under udarbejdelse", @@ -969,11 +985,12 @@ "external": "Ekstern", "external_libraries": "Eksterne biblioteker", "external_network": "Eksternt netvÃĻrk", - "external_network_sheet_info": "NÃĨ der er ikke er forbundet til det foretrukne WiFi-netvÃĻrk, vil appen forbinde til den første URL, den kan forbinde til, pÃĨ listen nedenfor. Startende med i toppen", + "external_network_sheet_info": "NÃĨ der er ikke er forbundet til det foretrukne Wi-Fi netvÃĻrk, vil appen forbinde til den første URL den kan forbinde til, pÃĨ listen nedenfor. Startende fra toppen", "face_unassigned": "Ikke tildelt", - "failed": "Failed", + "failed": "Fejlet", + "failed_to_authenticate": "Kunne ikke godkendes", "failed_to_load_assets": "Kunne ikke indlÃĻse mediefiler", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Kunne ikke indlÃĻse mappe", "favorite": "Favorit", "favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder", "favorites": "Favoritter", @@ -985,12 +1002,12 @@ "file_name_or_extension": "Filnavn eller filtype", "filename": "Filnavn", "filetype": "Filtype", - "filter": "Filter", "filter_people": "FiltrÊr personer", + "filter_places": "Filtrer steder", "find_them_fast": "Find dem hurtigt med søgning via navn", "fix_incorrect_match": "Fix forkert match", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Mappe", + "folder_not_found": "Mappe ikke fundet", "folders": "Mapper", "folders_feature_description": "Gennemse mappevisningen efter fotos og videoer pÃĨ filsystemet", "forward": "Fremad", @@ -1011,12 +1028,12 @@ "haptic_feedback_switch": "SlÃĨ haptisk feedback til", "haptic_feedback_title": "Haptisk feedback", "has_quota": "Har kvote", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Tilføj Header", + "header_settings_field_validator_msg": "VÃĻrdi kan ikke vÃĻre tom", + "header_settings_header_name_input": "Header navn", + "header_settings_header_value_input": "Header vÃĻrdi", + "headers_settings_tile_subtitle": "Definer proxy headers appen skal sende med hver netvÃĻrks forespørgsel", + "headers_settings_tile_title": "Brugerdefineret proxy headers", "hi_user": "Hej {name} ({email})", "hide_all_people": "Skjul alle personer", "hide_gallery": "Skjul galleri", @@ -1025,7 +1042,7 @@ "hide_person": "Skjul person", "hide_unnamed_people": "Skjul unavngivne personer", "home_page_add_to_album_conflicts": "Tilføjede {added} elementer til album {album}. {failed} elementer er allerede i albummet.", - "home_page_add_to_album_err_local": "Kan endnu ikke tilføje lokale elementer til album. Springer over..", + "home_page_add_to_album_err_local": "Kan endnu ikke tilføje lokale elementer til album. Springer over", "home_page_add_to_album_success": "Tilføjede {added} elementer til album {album}.", "home_page_album_err_partner": "Kan endnu ikke tilføje partners elementer til album. Springer over", "home_page_archive_err_local": "Kan ikke arkivere lokalt element endnu.. Springer over", @@ -1035,11 +1052,13 @@ "home_page_delete_remote_err_local": "Lokale elementer i fjernsletningssektion. Springer over", "home_page_favorite_err_local": "Kan endnu ikke gøre lokale elementer til favoritter. Springer over..", "home_page_favorite_err_partner": "Kan endnu ikke tilføje partners elementer som favoritter. Springer over", - "home_page_first_time_notice": "Hvis det er din første gang i appen, bedes du vÃĻlge en sikkerhedskopi af albummer sÃĨ tidlinjen kan blive fyldt med billeder og videoer fra albummerne.", + "home_page_first_time_notice": "Hvis det er din første gang i appen, bedes du vÃĻlge en sikkerhedskopi af albummer sÃĨ tidlinjen kan blive fyldt med billeder og videoer fra albummerne", + "home_page_locked_error_local": "Kan ikke flytte lokale mediefiler til lÃĨst mappe, springer over", + "home_page_locked_error_partner": "Kan ikke flytte partners mediefiler til lÃĨst mappe, springer over", "home_page_share_err_local": "Kan ikke dele lokale elementer via link, springer over", "home_page_upload_err_limit": "Det er kun muligt at lave sikkerhedskopi af 30 elementer ad gangen. Springer over", - "host": "Host", "hour": "Time", + "id": "ID", "ignore_icloud_photos": "Ignorer iCloud-billeder", "ignore_icloud_photos_description": "Billeder der er gemt pÃĨ iCloud vil ikke blive uploadet til Immich-serveren", "image": "Billede", @@ -1068,15 +1087,14 @@ "include_shared_partner_assets": "InkludÊr delte partnermedier", "individual_share": "Individuel andel", "individual_shares": "Individuelle delinger", - "info": "Info", "interval": { "day_at_onepm": "Hver dag kl. 13", "hours": "Hver {hours, plural, one {time} other {{hours, number} timer}}", "night_at_midnight": "Hver nat ved midnat", "night_at_twoam": "Hver nat kl. 2" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "Ugyldig dato", + "invalid_date_format": "Ugyldigt dato format", "invite_people": "Inviter personer", "invite_to_album": "Inviter til album", "items_count": "{count, plural, one {# element} other {# elementer}}", @@ -1099,7 +1117,7 @@ "library_options": "Biblioteksindstillinger", "library_page_device_albums": "Albummer pÃĨ enhed", "library_page_new_album": "Nyt album", - "library_page_sort_asset_count": "Antal af elementer\n", + "library_page_sort_asset_count": "Antal af elementer", "library_page_sort_created": "Senest oprettet", "library_page_sort_last_modified": "Sidst redigeret", "library_page_sort_title": "Albumtitel", @@ -1115,23 +1133,24 @@ "local_network": "Lokalt netvÃĻrk", "local_network_sheet_info": "Appen vil oprette forbindelse til serveren via denne URL, nÃĨr du bruger det angivne WiFi-netvÃĻrk", "location_permission": "Tilladelse til placering", - "location_permission_content": "For automatisk at skifte netvÃĻrk, skal Immich *altid* have prÃĻcis placeringsadgang, sÃĨ appen kan lÃĻse WiFi-netvÃĻrkets navn", + "location_permission_content": "For automatisk at skifte netvÃĻrk, skal Immich *altid* have prÃĻcis placeringsadgang, sÃĨ appen kan lÃĻse Wi-Fi netvÃĻrkets navn", "location_picker_choose_on_map": "VÃĻlg pÃĨ kort", "location_picker_latitude_error": "Indtast en gyldig breddegrad", "location_picker_latitude_hint": "Indtast din breddegrad her", "location_picker_longitude_error": "Indtast en gyldig lÃĻngdegrad", "location_picker_longitude_hint": "Indtast din lÃĻngdegrad her", + "lock": "LÃĨs", + "locked_folder": "LÃĨst mappe", "log_out": "Log ud", "log_out_all_devices": "Log ud af alle enheder", "logged_out_all_devices": "Logget ud af alle enheder", "logged_out_device": "Logget ud af enhed", "login": "Log ind", "login_disabled": "Login er blevet deaktiveret", - "login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen. ", + "login_form_api_exception": "API-undtagelse. Tjek serverens URL og prøv igen.", "login_form_back_button_text": "Tilbage", "login_form_email_hint": "din-e-mail@e-mail.com", "login_form_endpoint_hint": "http://din-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", "login_form_err_http": "Angiv venligst http:// eller https://", "login_form_err_invalid_email": "Ugyldig e-mail", "login_form_err_invalid_url": "Ugyldig webadresse", @@ -1155,6 +1174,7 @@ "loop_videos": "Gentag videoer", "loop_videos_description": "AktivÊr for at genafspille videoer automatisk i detaljeret visning.", "main_branch_warning": "Du bruger en udviklingsversion; vi anbefaler kraftigt at bruge en udgivelsesversion!", + "main_menu": "Hovedmenu", "make": "Producent", "manage_shared_links": "HÃĨndter delte links", "manage_sharing_with_partners": "AdministrÊr deling med partnere", @@ -1164,8 +1184,8 @@ "manage_your_devices": "AdministrÊr dine enheder der er logget ind", "manage_your_oauth_connection": "AdministrÊr din OAuth-tilslutning", "map": "Kort", - "map_assets_in_bound": "{} billede", - "map_assets_in_bounds": "{} billeder", + "map_assets_in_bound": "{count} billede", + "map_assets_in_bounds": "{count} billeder", "map_cannot_get_user_location": "Kan ikke finde brugerens placering", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Brug denne placering", @@ -1179,15 +1199,18 @@ "map_settings": "Kortindstillinger", "map_settings_dark_mode": "Mørk tilstand", "map_settings_date_range_option_day": "Sidste 24 timer", - "map_settings_date_range_option_days": "Sidste {} dage", + "map_settings_date_range_option_days": "Sidste {days} dage", "map_settings_date_range_option_year": "Sidste ÃĨr", - "map_settings_date_range_option_years": "Sidste {} ÃĨr", + "map_settings_date_range_option_years": "Sidste {years} ÃĨr", "map_settings_dialog_title": "Kortindstillinger", "map_settings_include_show_archived": "Inkluder arkiveret", "map_settings_include_show_partners": "Inkluder partnere", "map_settings_only_show_favorites": "Vis kun favoritter", "map_settings_theme_settings": "Korttema", "map_zoom_to_see_photos": "Zoom ud for at vise billeder", + "mark_all_as_read": "Marker alle som lÃĻst", + "mark_as_read": "Marker som lÃĻst", + "marked_all_as_read": "Markerede alle som lÃĻst", "matches": "Parringer", "media_type": "Medietype", "memories": "Minder", @@ -1196,11 +1219,8 @@ "memories_setting_description": "AdministrÊr hvad du ser i dine minder", "memories_start_over": "Start forfra", "memories_swipe_to_close": "Stryg op for at lukke", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Minde", "memory_lane_title": "Minder {title}", - "menu": "Menu", "merge": "Sammenflet", "merge_people": "Sammenflet personer", "merge_people_limit": "Du kan kun flette op til 5 ansigter ad gangen", @@ -1210,10 +1230,14 @@ "minimize": "MinimÊr", "minute": "Minut", "missing": "Mangler", - "model": "Model", "month": "MÃĨned", - "monthly_title_text_date_format": "MMMM y", "more": "Mere", + "move": "Flyt", + "move_off_locked_folder": "Flyt ud af lÃĨst mappe", + "move_to_locked_folder": "Flyt til lÃĨst mappe", + "move_to_locked_folder_confirmation": "Disse billeder og videoer vil blive fjernet fra alle albums, og vil kun vÃĻre synlig fra den lÃĨste mappe", + "moved_to_archive": "Flyttede {count, plural, one {# mediefil} other {# mediefiler}} til arkivet", + "moved_to_library": "Flyttede {count, plural, one {# mediefil} other {# mediefiler}} til biblioteket", "moved_to_trash": "Flyttet til skraldespand", "multiselect_grid_edit_date_time_err_read_only": "Kan ikke redigere datoen pÃĨ kun lÃĻselige elementer. Springer over", "multiselect_grid_edit_gps_err_read_only": "Kan ikke redigere lokation af kun lÃĻselige elementer. Springer over", @@ -1228,6 +1252,8 @@ "new_api_key": "Ny API-nøgle", "new_password": "Ny adgangskode", "new_person": "Ny person", + "new_pin_code": "Ny PIN kode", + "new_pin_code_subtitle": "Dette er første gang du tilgÃĨr den lÃĨste mappe. Lav en PIN kode for sikkert at tilgÃĨ denne side", "new_user_created": "Ny bruger oprettet", "new_version_available": "NY VERSION TILGÆNGELIG", "newest_first": "Nyeste først", @@ -1245,15 +1271,19 @@ "no_explore_results_message": "Upload flere billeder for at udforske din samling.", "no_favorites_message": "Tilføj favoritter for hurtigt at finde dine bedst billeder og videoer", "no_libraries_message": "Opret et eksternt bibliotek for at se dine billeder og videoer", + "no_locked_photos_message": "Billeder og videoer i den lÃĨste mappe er skjulte og vil ikke blive vist i dit bibliotek.", "no_name": "Intet navn", + "no_notifications": "Ingen notifikationer", + "no_people_found": "Ingen tilsvarende personer fundet", "no_places": "Ingen steder", "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller et mere generelt søgeord", "no_shared_albums_message": "Opret et album for at dele billeder og videoer med personer i dit netvÃĻrk", "not_in_any_album": "Ikke i noget album", - "not_selected": "Not selected", + "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "BemÃĻrk: For at anvende LagringsmÃĻrkat pÃĨ tidligere uploadede medier, kør", "notes": "Noter", + "nothing_here_yet": "Intet her endnu", "notification_permission_dialog_content": "GÃĨ til indstillinger for at slÃĨ notifikationer til.", "notification_permission_list_tile_content": "Tillad at bruge notifikationer.", "notification_permission_list_tile_enable_button": "SlÃĨ notifikationer til", @@ -1261,12 +1291,9 @@ "notification_toggle_setting_description": "AktivÊr emailnotifikationer", "notifications": "Notifikationer", "notifications_setting_description": "AdministrÊr notifikationer", - "oauth": "OAuth", "official_immich_resources": "Officielle Immich-ressourcer", - "offline": "Offline", "offline_paths": "Offline-stier", "offline_paths_description": "Disse resultater kan vÃĻre pÃĨ grund af manuel sletning af filer, som ikke er en del af et eksternt bibliotek.", - "ok": "Ok", "oldest_first": "Ældste først", "on_this_device": "PÃĨ denne enhed", "onboarding": "Introduktion", @@ -1274,21 +1301,19 @@ "onboarding_theme_description": "VÃĻlg et farvetema til din instans. Du kan ÃĻndre dette senere i dine indstillinger.", "onboarding_welcome_description": "Lad os fÃĨ din instans sat op med nogle almindelige indstillinger.", "onboarding_welcome_user": "Velkommen, {user}", - "online": "Online", "only_favorites": "Kun favoritter", + "open": "Åben", "open_in_map_view": "Åben i kortvisning", "open_in_openstreetmap": "Åben i OpenStreetMap", "open_the_search_filters": "Åbn søgefiltre", "options": "Handlinger", "or": "eller", "organize_your_library": "OrganisÊr dit bibliotek", - "original": "original", "other": "Andet", "other_devices": "Andre enheder", "other_variables": "Andre variable", "owned": "Egne", "owner": "Ejer", - "partner": "Partner", "partner_can_access": "{partner} kan tilgÃĨ", "partner_can_access_assets": "Alle dine billeder og videoer, bortset fra dem i Arkivet og Slettet", "partner_can_access_location": "Stedet, hvor dine billeder blev taget", @@ -1299,7 +1324,7 @@ "partner_page_partner_add_failed": "Kunne ikke tilføje en partner", "partner_page_select_partner": "VÃĻlg partner", "partner_page_shared_to_title": "Delt til", - "partner_page_stop_sharing_content": "{} vil ikke lÃĻngere have adgang til dine billeder.", + "partner_page_stop_sharing_content": "{partner} vil ikke lÃĻngere have adgang til dine billeder.", "partner_sharing": "Partnerdeling", "partners": "Partnere", "password": "Kodeord", @@ -1336,7 +1361,6 @@ "permission_onboarding_permission_granted": "Tilladelse givet! Du er nu klar.", "permission_onboarding_permission_limited": "Tilladelse begrÃĻnset. For at lade Immich lave sikkerhedskopi og styre hele dit galleri, skal der gives tilladelse til billeder og videoer i indstillinger.", "permission_onboarding_request": "Immich krÃĻver tilliadelse til at se dine billeder og videoer.", - "person": "Person", "person_birthdate": "Født den {date}", "person_hidden": "{name}{hidden, select, true { (skjult)} other {}}", "photo_shared_all_users": "Det ser ud til, at du har delt dine billeder med alle brugere, eller ogsÃĨ har du ikke nogen bruger at dele med.", @@ -1345,6 +1369,10 @@ "photos_count": "{count, plural, one {{count, number} Billede} other {{count, number} Billeder}}", "photos_from_previous_years": "Billeder fra tidligere ÃĨr", "pick_a_location": "VÃĻlg et sted", + "pin_code_changed_successfully": "Ændring af PIN kode vellykket", + "pin_code_reset_successfully": "Nulstilling af PIN kode vellykket", + "pin_code_setup_successfully": "OpsÃĻtning af PIN kode vellykket", + "pin_verification": "PIN kode verifikation", "place": "Sted", "places": "Steder", "places_count": "{count, plural, one {{count, number} Sted} other {{count, number} Steder}}", @@ -1352,7 +1380,7 @@ "play_memories": "Afspil minder", "play_motion_photo": "Afspil bevÃĻgelsesbillede", "play_or_pause_video": "Afspil eller pause video", - "port": "Port", + "please_auth_to_access": "Log venligst ind for at tilgÃĨ", "preferences_settings_subtitle": "Administrer app-prÃĻferencer", "preferences_settings_title": "PrÃĻferencer", "preset": "Forudindstilling", @@ -1362,20 +1390,19 @@ "previous_or_next_photo": "Forrige eller nÃĻste billede", "primary": "PrimÃĻre", "privacy": "Privatliv", + "profile": "Profil", "profile_drawer_app_logs": "Log", - "profile_drawer_client_out_of_date_major": "Mobilapp er forÃĻldet. Opdater venligst til den nyeste større version", - "profile_drawer_client_out_of_date_minor": "Mobilapp er forÃĻldet. Opdater venligst til den nyeste mindre version", + "profile_drawer_client_out_of_date_major": "Mobilapp er forÃĻldet. Opdater venligst til den nyeste større version.", + "profile_drawer_client_out_of_date_minor": "Mobilapp er forÃĻldet. Opdater venligst til den nyeste mindre version.", "profile_drawer_client_server_up_to_date": "Klient og server er ajour", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server er forÃĻldet. Opdater venligst til den nyeste større version", - "profile_drawer_server_out_of_date_minor": "Server er forÃĻldet. Opdater venligst til den nyeste mindre version", + "profile_drawer_server_out_of_date_major": "Server er forÃĻldet. Opdater venligst til den nyeste større version.", + "profile_drawer_server_out_of_date_minor": "Server er forÃĻldet. Opdater venligst til den nyeste mindre version.", "profile_image_of_user": "Profilbillede af {user}", "profile_picture_set": "Profilbillede indstillet.", "public_album": "Offentligt album", "public_share": "Offentlig deling", - "purchase_account_info": "Supporter", "purchase_activated_subtitle": "Tak fordi du støtter Immich og open source-software", - "purchase_activated_time": "Aktiveret den {date, date}", + "purchase_activated_time": "Aktiveret den {date}", "purchase_activated_title": "Din nøgle er blevet aktiveret", "purchase_button_activate": "Aktiver", "purchase_button_buy": "Køb", @@ -1402,8 +1429,6 @@ "purchase_remove_server_product_key": "Fjern serverens produktnøgle", "purchase_remove_server_product_key_prompt": "Er du sikker pÃĨ, at du vil fjerne serverproduktnøglen?", "purchase_server_description_1": "For hele serveren", - "purchase_server_description_2": "Supporter status", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Serverens produktnøgle administreres af administratoren", "rating": "Stjernebedømmelse", "rating_clear": "Nulstil vurdering", @@ -1420,6 +1445,8 @@ "recent_searches": "Seneste søgninger", "recently_added": "Senest tilføjet", "recently_added_page_title": "Nyligt tilføjet", + "recently_taken": "For nylig taget", + "recently_taken_page_title": "For nylig taget", "refresh": "OpdatÊr", "refresh_encoded_videos": "Opdater kodede videoer", "refresh_faces": "Opdater ansigter", @@ -1439,6 +1466,8 @@ "remove_deleted_assets": "Fjern slettede mediefiler", "remove_from_album": "Fjern fra album", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_locked_folder": "Fjern fra lÃĨst mappe", + "remove_from_locked_folder_confirmation": "Er du sikker pÃĨ at du vil flytte disse billeder og videoer ud af den lÃĨste mappe? De vil vÃĻre synlige i dit bibliotek", "remove_from_shared_link": "Fjern fra delt link", "remove_memory": "Fjern minde", "remove_photo_from_memory": "Fjern foto fra dette minde", @@ -1462,6 +1491,7 @@ "reset": "Nulstil", "reset_password": "Nulstil adgangskode", "reset_people_visibility": "Nulstil personsynlighed", + "reset_pin_code": "Nulstil PIN kode", "reset_to_default": "Nulstil til standard", "resolve_duplicates": "Løs dubletter", "resolved_all_duplicates": "Alle dubletter løst", @@ -1500,11 +1530,11 @@ "search_filter_apply": "Tilføj filter", "search_filter_camera_title": "VÃĻlg type af kamera", "search_filter_date": "Dato", - "search_filter_date_interval": "{start} til { slut}", + "search_filter_date_interval": "{start} til {end}", "search_filter_date_title": "VÃĻlg et datointerval", "search_filter_display_option_not_in_album": "Ikke i album", "search_filter_display_options": "Visningsindstillinger", - "search_filter_filename": "Search by file name", + "search_filter_filename": "Søg efter filnavn", "search_filter_location": "Lokation", "search_filter_location_title": "VÃĻlg lokation", "search_filter_media_type": "Medietype", @@ -1512,10 +1542,10 @@ "search_filter_people_title": "VÃĻlg personer", "search_for": "Søg efter", "search_for_existing_person": "Søg efter eksisterende person", - "search_no_more_result": "No more results", + "search_no_more_result": "Ikke flere resultater", "search_no_people": "Ingen personer", "search_no_people_named": "Ingen personer med navnet \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "Ingen resultater fundet, prøv en anden søgestreng eller kombination", "search_options": "Søgemuligheder", "search_page_categories": "Kategorier", "search_page_motion_photos": "BevÃĻgelsesbilleder", @@ -1534,7 +1564,7 @@ "search_result_page_new_search_hint": "Ny søgning", "search_settings": "søgeindstillinger", "search_state": "Søg efter lansdel...", - "search_suggestion_list_smart_search_hint_1": "Smart søgnining er slÃĨet til som standard. For at søge efter metadata brug syntaksen", + "search_suggestion_list_smart_search_hint_1": "Smart søgnining er slÃĨet til som standard. For at søge efter metadata brug syntaksen ", "search_suggestion_list_smart_search_hint_2": "m:dit-søgeord", "search_tags": "Søg tags...", "search_timezone": "Søg i tidszone...", @@ -1554,6 +1584,7 @@ "select_keep_all": "VÃĻlg gem alle", "select_library_owner": "VÃĻlg biblioteksejer", "select_new_face": "VÃĻlg nyt ansigt", + "select_person_to_tag": "VÃĻlg en person at tagge", "select_photos": "VÃĻlg billeder", "select_trash_all": "VÃĻlg smid alle ud", "select_user_for_sharing_page_err_album": "Fejlede i at oprette et nyt album", @@ -1563,11 +1594,7 @@ "send_welcome_email": "Send velkomstemail", "server_endpoint": "Server endepunkt", "server_info_box_app_version": "Applikationsversion", - "server_info_box_server_url": "Server URL", - "server_offline": "Server Offline", - "server_online": "Server Online", "server_stats": "Serverstatus", - "server_version": "Server Version", "set": "Indstil", "set_as_album_cover": "Indstil som albumcover", "set_as_featured_photo": "Indstil som fremhÃĻvet billede", @@ -1580,31 +1607,33 @@ "setting_image_viewer_original_title": "IndlÃĻs originalbillede", "setting_image_viewer_preview_subtitle": "SlÃĨ indlÃĻsning af et mediumstørrelse billede til. SlÃĨ fra for enten direkte at indlÃĻse originalen eller kun at bruge miniaturebilledet.", "setting_image_viewer_preview_title": "IndlÃĻs forhÃĨndsvisning af billedet", - "setting_image_viewer_title": "Images", + "setting_image_viewer_title": "Billeder", "setting_languages_apply": "Anvend", "setting_languages_subtitle": "Ændrer app-sprog", "setting_languages_title": "Sprog", - "setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {}", - "setting_notifications_notify_hours": "{} timer", + "setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {duration}", + "setting_notifications_notify_hours": "{count} timer", "setting_notifications_notify_immediately": "med det samme", - "setting_notifications_notify_minutes": "{} minutter", + "setting_notifications_notify_minutes": "{count} minutter", "setting_notifications_notify_never": "aldrig", - "setting_notifications_notify_seconds": "{} sekunder", + "setting_notifications_notify_seconds": "{count} sekunder", "setting_notifications_single_progress_subtitle": "Detaljeret uploadstatus pr. element", "setting_notifications_single_progress_title": "Vis detaljeret baggrundsuploadstatus", "setting_notifications_subtitle": "Tilpas dine notifikationsprÃĻferencer", "setting_notifications_total_progress_subtitle": "Samlet uploadstatus (fÃĻrdige/samlet antal elementer)", "setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus", "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_subtitle": "NÃĨr der streames video fra serveren, afspil da den originale selv nÃĨr en omkodet udgave er tilgÃĻngelig. Kan føre til buffering. Videoer, der er tilgÃĻngelige lokalt, afspilles i original kvalitet uanset denne indstilling.", + "setting_video_viewer_original_video_title": "Tving original video", "settings": "Indstillinger", "settings_require_restart": "Genstart venligst Immich for at anvende denne ÃĻndring", "settings_saved": "Indstillinger er gemt", + "setup_pin_code": "SÃĻt in PIN kode", "share": "Del", "share_add_photos": "Tilføj billeder", - "share_assets_selected": "{} valgt", + "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder...", + "share_link": "Del link", "shared": "Delt", "shared_album_activities_input_disable": "Kommentarer er deaktiveret", "shared_album_activity_remove_content": "Vil du slette denne aktivitet?", @@ -1617,34 +1646,33 @@ "shared_by_user": "Delt af {user}", "shared_by_you": "Delt af dig", "shared_from_partner": "Billeder fra {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Uploadet", "shared_link_app_bar_title": "Delte links", "shared_link_clipboard_copied_massage": "Kopieret til udklipsholderen", - "shared_link_clipboard_text": "Link: {}\nkodeord: {}", + "shared_link_clipboard_text": "Link: {link}\nAdgangskode: {password}", "shared_link_create_error": "Der opstod en fejl i oprettelsen af et delt link", "shared_link_edit_description_hint": "Indtast beskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", - "shared_link_edit_expire_after_option_days": "{} dage", + "shared_link_edit_expire_after_option_days": "{count} dage", "shared_link_edit_expire_after_option_hour": "1 time", - "shared_link_edit_expire_after_option_hours": "{} timer", + "shared_link_edit_expire_after_option_hours": "{count} timer", "shared_link_edit_expire_after_option_minute": "1 minut", - "shared_link_edit_expire_after_option_minutes": "{} minutter", - "shared_link_edit_expire_after_option_months": "{} mÃĨneder", - "shared_link_edit_expire_after_option_year": "{} ÃĨr", + "shared_link_edit_expire_after_option_minutes": "{count} minutter", + "shared_link_edit_expire_after_option_months": "{count} mÃĨneder", + "shared_link_edit_expire_after_option_year": "{count} ÃĨr", "shared_link_edit_password_hint": "Indtast kodeordet", "shared_link_edit_submit_button": "Opdater link", "shared_link_error_server_url_fetch": "Kan ikke finde server URL", - "shared_link_expires_day": "Udløber om {} dag", - "shared_link_expires_days": "Udløber om {} dage", - "shared_link_expires_hour": "Udløber om {} time", - "shared_link_expires_hours": "Udløber om {} timer", - "shared_link_expires_minute": "Udløber om {} minut", - "shared_link_expires_minutes": "Udløber om {} minutter", + "shared_link_expires_day": "Udløber om {count} dag", + "shared_link_expires_days": "Udløber om {count} dage", + "shared_link_expires_hour": "Udløber om {count} time", + "shared_link_expires_hours": "Udløber om {count} timer", + "shared_link_expires_minute": "Udløber om {count} minut", + "shared_link_expires_minutes": "Udløber om {count} minutter", "shared_link_expires_never": "Udløber aldrig", - "shared_link_expires_second": "Udløber om {} sekund", - "shared_link_expires_seconds": "Udløber om {} sekunder", + "shared_link_expires_second": "Udløber om {count} sekund", + "shared_link_expires_seconds": "Udløber om {count} sekunder", "shared_link_individual_shared": "Individuelt delt", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "HÃĨndter delte links", "shared_link_options": "Muligheder for delt link", "shared_links": "Delte links", @@ -1706,30 +1734,25 @@ "stack_select_one_photo": "VÃĻlg Êt hovedbillede til stakken", "stack_selected_photos": "Stak valgte billeder", "stacked_assets_count": "Stablet {count, plural, one {# aktiv} other {# aktiver}}", - "stacktrace": "Stacktrace", - "start": "Start", "start_date": "Startdato", "state": "Stat", - "status": "Status", "stop_motion_photo": "Stopmotionbillede", "stop_photo_sharing": "Stop med at dele dine billeder?", "stop_photo_sharing_description": "{partner} vil ikke lÃĻngere kunne tilgÃĨ dine billeder.", "stop_sharing_photos_with_user": "Afslut deling af dine fotos med denne bruger", "storage": "Lagringsplads", "storage_label": "LagringsmÃĻrkat", + "storage_quota": "Lagringskvota", "storage_usage": "{used} ud af {available} brugt", "submit": "Indsend", "suggestions": "Anbefalinger", "sunrise_on_the_beach": "Solopgang pÃĨ stranden", - "support": "Support", - "support_and_feedback": "Support & Feedback", "support_third_party_description": "Din Immich-installation blev sammensat af en tredjepart. Problemer, du oplever, kan vÃĻre forÃĨrsaget af denne udvikler, sÃĨ rejs venligst problemer med dem i første omgang ved at bruge nedenstÃĨende links.", "swap_merge_direction": "Byt retning for sammenfletning", "sync": "SynkronisÊr", "sync_albums": "Synkroniser albummer", "sync_albums_manual_subtitle": "Synkroniser alle uploadet billeder og videoer til de valgte backupalbummer", "sync_upload_album_setting_subtitle": "Opret og upload dine billeder og videoer til de valgte albummer i Immich", - "tag": "Tag", "tag_assets": "Tag mediefiler", "tag_created": "Oprettet tag: {tag}", "tag_feature_description": "Gennemse billeder og videoer grupperet efter logiske tag-emner", @@ -1737,13 +1760,12 @@ "tag_people": "Tag personer", "tag_updated": "Opdateret tag: {tag}", "tagged_assets": "Tagget {count, plural, one {# aktiv} other {# aktiver}}", - "tags": "Tags", "template": "Skabelon", "theme": "Tema", "theme_selection": "Temavalg", "theme_selection_description": "Indstil automatisk temaet til lyst eller mørkt baseret pÃĨ din browsers systemprÃĻference", "theme_setting_asset_list_storage_indicator_title": "Vis opbevaringsindikator pÃĨ filer", - "theme_setting_asset_list_tiles_per_row_title": "Antal elementer per rÃĻkke ({})", + "theme_setting_asset_list_tiles_per_row_title": "Antal elementer per rÃĻkke ({count})", "theme_setting_colorful_interface_subtitle": "Tilføj primÃĻr farve til baggrundsoverflader.", "theme_setting_colorful_interface_title": "Farverig grÃĻnseflade", "theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten i billedfremviseren", @@ -1763,12 +1785,10 @@ "to_archive": "ArkivÊr", "to_change_password": "Skift adgangskode", "to_favorite": "Gør til favorit", - "to_login": "Login", "to_parent": "GÃĨ op", "to_trash": "Papirkurv", "toggle_settings": "SlÃĨ indstillinger til eller fra", "toggle_theme": "SlÃĨ mørkt tema til eller fra", - "total": "Total", "total_usage": "Samlet forbrug", "trash": "Papirkurv", "trash_all": "Smid alle ud", @@ -1778,13 +1798,14 @@ "trash_no_results_message": "Billeder og videoer markeret til sletning vil blive vist her.", "trash_page_delete_all": "Slet alt", "trash_page_empty_trash_dialog_content": "Vil du tømme papirkurven? Disse elementer vil blive permanent fjernet fra Immich", - "trash_page_info": "Slettede elementer vil blive slettet permanent efter {} dage", + "trash_page_info": "Slettede elementer vil blive slettet permanent efter {days} dage", "trash_page_no_assets": "Ingen slettede elementer", "trash_page_restore_all": "Gendan alt", "trash_page_select_assets_btn": "VÃĻlg elementer", - "trash_page_title": "Papirkurv ({})", + "trash_page_title": "Papirkurv ({count})", "trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.", - "type": "Type", + "unable_to_change_pin_code": "Kunne ikke ÃĻndre PIN kode", + "unable_to_setup_pin_code": "Kunne ikke sÃĻtte PIN kode", "unarchive": "AfakivÊr", "unarchived_count": "{count, plural, other {Uarkiveret #}}", "unfavorite": "Fjern favorit", @@ -1808,8 +1829,8 @@ "untracked_files": "Ikke overvÃĨgede filer", "untracked_files_decription": "Disse filer bliver ikke sporet af applikationen. De kan vÃĻre resultatet af mislykkede flytninger, afbrudte uploads eller efterladt pÃĨ grund af en fejl", "up_next": "NÃĻste", + "updated_at": "Opdateret", "updated_password": "Opdaterede adgangskode", - "upload": "Upload", "upload_concurrency": "Upload samtidighed", "upload_dialog_info": "Vil du sikkerhedskopiere de(t) valgte element(er) til serveren?", "upload_dialog_title": "Upload element", @@ -1820,15 +1841,19 @@ "upload_status_errors": "Fejl", "upload_status_uploaded": "Uploadet", "upload_success": "Upload gennemført. Opdater siden for at se nye uploadaktiver.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", + "upload_to_immich": "Upload til Immich ({count})", + "uploading": "Uploader", "url": "URL", "usage": "Forbrug", + "use_biometric": "Brug biometrisk", "use_current_connection": "brug nuvÃĻrende forbindelse", "use_custom_date_range": "Brug tilpasset datointerval i stedet", "user": "Bruger", + "user_has_been_deleted": "Denne bruger er slettet.", "user_id": "Bruger-ID", "user_liked": "{user} kunne lide {type, select, photo {dette billede} video {denne video} asset {dette aktiv} other {det}}", + "user_pin_code_settings": "PIN Kode", + "user_pin_code_settings_description": "Administrer din PIN kode", "user_purchase_settings": "Køb", "user_purchase_settings_description": "Administrer dit køb", "user_role_set": "Indstil {user} som {role}", @@ -1841,7 +1866,6 @@ "validate": "ValidÊr", "validate_endpoint_error": "Indtast en gyldig URL", "variables": "Variabler", - "version": "Version", "version_announcement_closing": "Din ven, Alex", "version_announcement_message": "Hej! En ny version af Immich er tilgÃĻngelig. Brug venligst lidt tid pÃĨ at lÃĻse udgivelsesbemÃĻrkningerne for at sikre, at din opsÃĻtning er opdateret for at forhindre fejlkonfigurationer, isÃĻr hvis du bruger WatchTower eller en mekanisme, der hÃĨndterer automatisk opdatering af din Immich-instans.", "version_announcement_overlay_release_notes": "udgivelsesnoterne", @@ -1851,7 +1875,6 @@ "version_announcement_overlay_title": "Ny serverversion er tilgÃĻngelig 🎉", "version_history": "Versionshistorik", "version_history_item": "Installerede {version} den {date}", - "video": "Video", "video_hover_setting": "Afspil miniaturevisning af video nÃĨr musemarkøren er over den", "video_hover_setting_description": "Afspil miniaturevisning for videoer nÃĨr musemarkøren holdes over elementet. Selv nÃĨr det er deaktiveret, kan afspilning startes ved at holde musen over afspilningsikonet.", "videos": "Videoer", @@ -1866,6 +1889,7 @@ "view_name": "Se", "view_next_asset": "Se nÃĻste medie", "view_previous_asset": "Se forrige medie", + "view_qr_code": "Vis QR kode", "view_stack": "Vis stak", "viewer_remove_from_stack": "Fjern fra stak", "viewer_stack_use_as_main_asset": "Brug som hovedelement", @@ -1876,11 +1900,12 @@ "week": "Uge", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "WiFi-navn", + "wifi_name": "Wi-Fi navn", + "wrong_pin_code": "Forkert PIN kode", "year": "År", "years_ago": "{years, plural, one {# ÃĨr} other {# ÃĨr}} siden", "yes": "Ja", "you_dont_have_any_shared_links": "Du har ikke nogen delte links", - "your_wifi_name": "Dit WiFi-navn", + "your_wifi_name": "Dit Wi-Fi navn", "zoom_image": "Zoom billede" } diff --git a/i18n/de.json b/i18n/de.json index bc4bc28575..34cda82c81 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -26,6 +26,7 @@ "add_to_album": "Zu Album hinzufÃŧgen", "add_to_album_bottom_sheet_added": "Zu {album} hinzugefÃŧgt", "add_to_album_bottom_sheet_already_exists": "Bereits in {album}", + "add_to_locked_folder": "Zum gesperrten Ordner hinzufÃŧgen", "add_to_shared_album": "Zu geteiltem Album hinzufÃŧgen", "add_url": "URL hinzufÃŧgen", "added_to_archive": "Zum Archiv hinzugefÃŧgt", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbank sichern", - "backup_database_enable_description": "Sicherung der Datenbank aktivieren", - "backup_keep_last_amount": "Anzahl der aufzubewahrenden frÃŧheren Sicherungen", - "backup_settings": "Datensicherungs-Einstellungen", - "backup_settings_description": "Datensicherungs-Einstellungen verwalten", + "backup_database": "Datenbanksicherung regelmäßig erstellen", + "backup_database_enable_description": "Datenbank regeläßig sichern", + "backup_keep_last_amount": "Anzahl der aufzubewahrenden frÃŧheren Backups", + "backup_settings": "Datenbank Sicherung", + "backup_settings_description": "Einstellungen zur regemäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht Ãŧberwacht und du wirst nicht Ãŧber Fehler informiert.", "check_all": "Alle ÃŧberprÃŧfen", "cleanup": "Aufräumen", "cleared_jobs": "Folgende Aufgaben zurÃŧckgesetzt: {job}", @@ -53,10 +54,11 @@ "confirm_email_below": "Bestätige, indem du unten \"{email}\" eingibst", "confirm_reprocess_all_faces": "Bist du sicher, dass du alle Gesichter erneut verarbeiten mÃļchtest? Dies lÃļscht auch alle bereits benannten Personen.", "confirm_user_password_reset": "Bist du sicher, dass du das Passwort fÃŧr {user} zurÃŧcksetzen mÃļchtest?", + "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurÃŧcksetzen mÃļchtest?", "create_job": "Aufgabe erstellen", - "cron_expression": "Cron-Ausdruck", - "cron_expression_description": "Stellen Sie das Scanintervall im Cron-Format ein. Weitere Informationen finden Sie beispielsweise unter Crontab Guru", - "cron_expression_presets": "Cron-Ausdruck-Vorlagen", + "cron_expression": "Cron Zeitangabe", + "cron_expression_description": "Setze ein Intervall fÃŧr die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der Crontab Guru", + "cron_expression_presets": "NÃŧtzliche Zeitangaben fÃŧr Cron", "disable_login": "Login deaktvieren", "duplicate_detection_job_description": "Diese Aufgabe fÃŧhrt das maschinelle Lernen fÃŧr jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern kÃļnnen Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nÃŧtzlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren mÃļchtest, wie z. B. RAW-Dateien.", @@ -106,7 +108,7 @@ "library_scanning_enable_description": "Regelmäßiges Scannen der Bibliothek aktivieren", "library_settings": "Externe Bibliothek", "library_settings_description": "Einstellungen externer Bibliotheken verwalten", - "library_tasks_description": "ÜberprÃŧfe externe Bibliotheken auf neue oder veränderte Medien", + "library_tasks_description": "ÜberprÃŧfe externe Bibliotheken auf neue und/oder veränderte Medien", "library_watching_enable_description": "Überwache externe Bibliotheken auf Dateiänderungen", "library_watching_settings": "BibliotheksÃŧberwachung (EXPERIMENTELL)", "library_watching_settings_description": "Automatisch auf geänderte Dateien prÃŧfen", @@ -192,32 +194,28 @@ "oauth_auto_register": "Automatische Registrierung", "oauth_auto_register_description": "Automatische Registrierung neuer Benutzer nach der OAuth-Anmeldung", "oauth_button_text": "Button-Text", - "oauth_client_id": "Client-ID", - "oauth_client_secret": "Client-Geheimnis", + "oauth_client_secret_description": "Erforderlich wenn PKCE (Proof Key for Code Exchange) nicht vom OAuth- Anbieter unterstÃŧtzt wird", "oauth_enable_description": "Anmeldung mit OAuth", - "oauth_issuer_url": "Aussteller-URL", "oauth_mobile_redirect_uri": "Mobile Umleitungs-URI", "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI Ãŧberschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie '{callback}' erlaubt", - "oauth_profile_signing_algorithm": "Algorithmus zur Profilsignierung", - "oauth_profile_signing_algorithm_description": "Dieser Algorithmus wird fÃŧr die Signatur des Benutzerprofils verwendet.", - "oauth_scope": "Umfang", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", - "oauth_signing_algorithm": "Signier-Algorithmus", "oauth_storage_label_claim": "Speicherpfadbezeichnung", "oauth_storage_label_claim_description": "Die Speicherpfadbezeichnung des Benutzers automatisch auf den Wert dieser Eingabe setzen.", "oauth_storage_quota_claim": "Speicherkontingentangabe", "oauth_storage_quota_claim_description": "Setzen Sie das Speicherkontingent des Benutzers automatisch auf den angegebenen Wert.", "oauth_storage_quota_default": "Standard-Speicherplatzkontingent (GiB)", "oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines Ãŧbermittelt wird (gib 0 fÃŧr ein unbegrenztes Kontingent ein).", + "oauth_timeout": "ZeitÃŧberschreitung bei Anfrage", + "oauth_timeout_description": "ZeitÃŧberschreitung fÃŧr Anfragen in Millisekunden", "offline_paths": "Offline-Pfade", - "offline_paths_description": "Die Ergebnisse kÃļnnten durch manuelles LÃļschen von Dateien, die nicht Teil einer externen Bibliothek sind, verursacht sein.", - "password_enable_description": "Login mit E-Mail und Passwort", - "password_settings": "Passwort-Login", + "offline_paths_description": "Dies kÃļnnte durch manuelles LÃļschen von Dateien, die nicht Teil einer externen Bibliothek sind, verursacht sein.", + "password_enable_description": "Mit E-Mail und Passwort anmelden", + "password_settings": "Passwort-Anmeldung", "password_settings_description": "Passwort-Anmeldeeinstellungen verwalten", - "paths_validated_successfully": "Alle Pfade wurden erfolgreich validiert", + "paths_validated_successfully": "Alle Pfade erfolgreich ÃŧberprÃŧft", "person_cleanup_job": "Personen aufräumen", "quota_size_gib": "Kontingent (GiB)", "refreshing_all_libraries": "Alle Bibliotheken aktualisieren", @@ -240,7 +238,7 @@ "server_settings_description": "Servereinstellungen verwalten", "server_welcome_message": "Willkommensnachricht", "server_welcome_message_description": "Eine Mitteilung, welche auf der Anmeldeseite angezeigt wird.", - "sidecar_job": "Filialdatei-Metadaten", + "sidecar_job": "Sidecar Metadaten", "sidecar_job_description": "Durch diese Aufgabe werden Filialdatei-Metadaten im Dateisystem entdeckt oder synchronisiert", "slideshow_duration_description": "Dauer der Anzeige jedes Bildes in Sekunden", "smart_search_job_description": "Diese Aufgabe wendet das maschinelle Lernen auf Dateien an, um die intelligente Suche zu ermÃļglichen", @@ -251,7 +249,7 @@ "storage_template_hash_verification_enabled_description": "Aktiviert die Hash-Verifizierung. Deaktiviere diese Option nur, wenn du dir Ãŧber die damit verbundenen Auswirkungen im Klaren bist", "storage_template_migration": "Migration von Speichervorlagen", "storage_template_migration_description": "Diese Aufgabe wendet die aktuelle {template} auf zuvor hochgeladene Dateien an", - "storage_template_migration_info": "Die Vorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur fÃŧr neue Dateien. Um die Vorlage rÃŧckwirkend auf bereits hochgeladene Assets anzuwenden, fÃŧhre den {job} aus.", + "storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur fÃŧr neue Dateien. Um die Vorlage rÃŧckwirkend auf bereits hochgeladene Assets anzuwenden, fÃŧhre den {job} aus.", "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", "storage_template_onboarding_description": "Wenn aktiviert, sortiert diese Funktion Dateien automatisch basierend auf einer benutzerdefinierten Vorlage. Aufgrund von Stabilitätsproblemen ist die Funktion standardmäßig deaktiviert. Weitere Informationen findest du in der Dokumentation.", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "Gibt die Anzahl der Tage bis zur endgÃŧltigen LÃļschung eines Kontos und seiner Dateien an. Der BenutzerlÃļschauftrag wird täglich um Mitternacht ausgefÃŧhrt, um zu ÃŧberprÃŧfen, ob Nutzer zur LÃļschung bereit sind. Änderungen an dieser Einstellung werden erst bei der nächsten AusfÃŧhrung berÃŧcksichtigt.", "user_delete_immediately": "Das Konto und die Dateien von {user} werden sofort fÃŧr eine permanente LÃļschung in die Warteschlange gestellt.", "user_delete_immediately_checkbox": "Benutzer und Dateien zur sofortigen LÃļschung in die Warteschlange stellen", + "user_details": "Benutzerdetails", "user_management": "Benutzerverwaltung", "user_password_has_been_reset": "Das Passwort des Benutzers wurde zurÃŧckgesetzt:", "user_password_reset_description": "Bitte gib dem Benutzer das temporäre Passwort und informiere ihn, dass das Passwort beim nächsten Login geändert werden muss.", @@ -371,13 +370,17 @@ "admin_password": "Administrator Passwort", "administration": "Verwaltung", "advanced": "Erweitert", - "advanced_settings_log_level_title": "Log-Level: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter fÃŧr Synchronisierung der Gerätealben", + "advanced_settings_log_level_title": "Log-Level: {level}", "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", "advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen", "advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll", "advanced_settings_proxy_headers_title": "Proxy-Headers", "advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server Ãŧberspringen. Notwendig bei selbstsignierten Zertifikaten.", "advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben", + "advanced_settings_sync_remote_deletions_subtitle": "Automatisches LÃļschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgefÃŧhrt wird", + "advanced_settings_sync_remote_deletions_title": "Synchrone Remote-LÃļschungen [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", "album_thumbnail_card_item": "1 Element", - "album_thumbnail_card_items": "{} Elemente", + "album_thumbnail_card_items": "{count} Elemente", "album_thumbnail_card_shared": " ¡ Geteilt", - "album_thumbnail_shared_by": "Geteilt von {}", + "album_thumbnail_shared_by": "Geteilt von {user}", "album_updated": "Album aktualisiert", "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", "album_user_left": "{album} verlassen", @@ -440,15 +443,15 @@ "archive": "Archiv", "archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben", "archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden", - "archive_page_title": "Archiv ({})", + "archive_page_title": "Archiv ({count})", "archive_size": "ArchivgrÃļße", "archive_size_description": "ArchivgrÃļße fÃŧr Downloads konfigurieren (in GiB)", "archived": "Archiviert", "archived_count": "{count, plural, other {# archiviert}}", "are_these_the_same_person": "Ist das dieselbe Person?", "are_you_sure_to_do_this": "Bist du sicher, dass du das tun willst?", - "asset_action_delete_err_read_only": "SchreibgeschÃŧtzte Inhalte kÃļnnen nicht gelÃļscht werden, Ãŧberspringen...", - "asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, Ãŧberspringen...", + "asset_action_delete_err_read_only": "SchreibgeschÃŧtzte Inhalte kÃļnnen nicht gelÃļscht werden, Ãŧberspringen", + "asset_action_share_err_offline": "Die Offline-Inhalte konnten nicht gelesen werden, Ãŧberspringen", "asset_added_to_album": "Zum Album hinzugefÃŧgt", "asset_adding_to_album": "HinzufÃŧgen zum Albumâ€Ļ", "asset_description_updated": "Die Beschreibung der Datei wurde aktualisiert", @@ -460,7 +463,6 @@ "asset_list_layout_settings_group_automatically": "Automatisch", "asset_list_layout_settings_group_by": "Gruppiere Elemente nach", "asset_list_layout_settings_group_by_month_day": "Monat + Tag", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Einstellungen fÃŧr das Fotogitter-Layout", "asset_list_settings_title": "Fotogitter", "asset_offline": "Datei offline", @@ -477,51 +479,50 @@ "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefÃŧgt", "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefÃŧgt", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", - "assets_deleted_permanently": "{} Datei/en permanent gelÃļscht", - "assets_deleted_permanently_from_server": "{} Datei/en wurden permanent vom Immich Server gelÃļscht", + "assets_deleted_permanently": "{count} Element(e) permanent gelÃļscht", + "assets_deleted_permanently_from_server": "{count} Element(e) permanent vom Immich-Server gelÃļscht", "assets_moved_to_trash_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben", "assets_permanently_deleted_count": "{count, plural, one {# Datei} other {# Dateien}} endgÃŧltig gelÃļscht", "assets_removed_count": "{count, plural, one {# Datei} other {# Dateien}} entfernt", - "assets_removed_permanently_from_device": "{} Datei/en wurden permanent vom Gerät gelÃļscht", + "assets_removed_permanently_from_device": "{count} Element(e) permanent von Ihrem Gerät gelÃļscht", "assets_restore_confirmation": "Bist du sicher, dass du alle Dateien aus dem Papierkorb wiederherstellen willst? Diese Aktion kann nicht rÃŧckgängig gemacht werden! Beachte, dass Offline-Dateien auf diese Weise nicht wiederhergestellt werden kÃļnnen.", "assets_restored_count": "{count, plural, one {# Datei} other {# Dateien}} wiederhergestellt", - "assets_restored_successfully": "{} Datei/en erfolgreich wiederhergestellt", - "assets_trashed": "{} Datei/en gelÃļscht", + "assets_restored_successfully": "{count} Element(e) erfolgreich wiederhergestellt", + "assets_trashed": "{count} Element(e) gelÃļscht", "assets_trashed_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben", - "assets_trashed_from_server": "{} Datei/en vom Immich-Server gelÃļscht", + "assets_trashed_from_server": "{count} Element(e) vom Immich-Server gelÃļscht", "assets_were_part_of_album_count": "{count, plural, one {# Datei ist} other {# Dateien sind}} bereits im Album vorhanden", "authorized_devices": "Verwendete Geräte", - "automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal Ãŧber ein bestimmtes WLAN, wenn es verfÃŧgbar ist, und verwenden Sie andere VerbindungsmÃļglichkeiten anderswo.", + "automatic_endpoint_switching_subtitle": "Verbinden Sie sich lokal Ãŧber ein bestimmtes WLAN, wenn es verfÃŧgbar ist, und verwenden Sie andere VerbindungsmÃļglichkeiten anderswo", "automatic_endpoint_switching_title": "Automatische URL-Umschaltung", "back": "ZurÃŧck", "back_close_deselect": "ZurÃŧck, Schließen oder Abwählen", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu kÃļnnen, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", - "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})", - "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern.", + "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", + "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) kÃļnnen sich Ãŧber mehrere Alben verteilen. Daher kÃļnnen diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", "backup_album_selection_page_select_albums": "Alben auswählen", "backup_album_selection_page_selection_info": "Information", "backup_album_selection_page_total_assets": "Elemente", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Es trat ein Fehler bei der Sicherung auf. Erneuter Versuch...", - "backup_background_service_connection_failed_message": "Es konnte keine Verbindung zum Server hergestellt werden. Erneuter Versuch...", - "backup_background_service_current_upload_notification": "Lädt {} hoch", + "backup_background_service_backup_failed_message": "Es trat ein Fehler bei der Sicherung auf. Erneuter Versuchâ€Ļ", + "backup_background_service_connection_failed_message": "Es konnte keine Verbindung zum Server hergestellt werden. Erneuter Versuchâ€Ļ", + "backup_background_service_current_upload_notification": "Lädt {filename} hoch", "backup_background_service_default_notification": "Suche nach neuen Elementenâ€Ļ", "backup_background_service_error_title": "Fehler bei der Sicherung", - "backup_background_service_in_progress_notification": "Elemente werden gesichert...", - "backup_background_service_upload_failure_notification": "Konnte {} nicht hochladen", + "backup_background_service_in_progress_notification": "Elemente werden gesichertâ€Ļ", + "backup_background_service_upload_failure_notification": "Konnte {filename} nicht hochladen", "backup_controller_page_albums": "Gesicherte Alben", - "backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermÃļglichen. ", - "backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert.", + "backup_controller_page_background_app_refresh_disabled_content": "Aktiviere Hintergrundaktualisierungen in Einstellungen -> Allgemein -> Hintergrundaktualisierungen um Sicherungen im Hintergrund zu ermÃļglichen.", + "backup_controller_page_background_app_refresh_disabled_title": "Hintergrundaktualisierungen sind deaktiviert", "backup_controller_page_background_app_refresh_enable_button_text": "Gehe zu Einstellungen", "backup_controller_page_background_battery_info_link": "Zeige mir wie", "backup_controller_page_background_battery_info_message": "FÃŧr die besten Ergebnisse fÃŧr Sicherungen im Hintergrund, deaktiviere alle Batterieoptimierungen und Einschränkungen fÃŧr die Hintergrundaktivitäten von Immich.\n\nDa dies gerätespezifisch ist, schlage diese Informationen fÃŧr deinen Gerätehersteller nach.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Batterieoptimierungen", "backup_controller_page_background_charging": "Nur während des Ladens", "backup_controller_page_background_configure_error": "Konnte Hintergrundservice nicht konfigurieren", - "backup_controller_page_background_delay": "Sicherung neuer Elemente verzÃļgern um: {}", + "backup_controller_page_background_delay": "Sicherung neuer Elemente verzÃļgern um: {duration}", "backup_controller_page_background_description": "Schalte den Hintergrundservice ein, um neue Elemente automatisch im Hintergrund zu sichern ohne die App zu Ãļffnen", "backup_controller_page_background_is_off": "Automatische Sicherung im Hintergrund ist deaktiviert", "backup_controller_page_background_is_on": "Automatische Sicherung im Hintergrund ist aktiviert", @@ -531,12 +532,11 @@ "backup_controller_page_backup": "Sicherung", "backup_controller_page_backup_selected": "Ausgewählt: ", "backup_controller_page_backup_sub": "Gesicherte Fotos und Videos", - "backup_controller_page_created": "Erstellt: {}", + "backup_controller_page_created": "Erstellt am: {date}", "backup_controller_page_desc_backup": "Aktiviere die Sicherung, um Elemente immer automatisch auf den Server zu laden, während du die App benutzt.", "backup_controller_page_excluded": "Ausgeschlossen: ", - "backup_controller_page_failed": "Fehlgeschlagen ({})", - "backup_controller_page_filename": "Dateiname: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Fehlgeschlagen ({count})", + "backup_controller_page_filename": "Dateiname: {filename} [{size}]", "backup_controller_page_info": "Informationen zur Sicherung", "backup_controller_page_none_selected": "Keine ausgewählt", "backup_controller_page_remainder": "Verbleibend", @@ -545,7 +545,7 @@ "backup_controller_page_start_backup": "Sicherung starten", "backup_controller_page_status_off": "Sicherung im Vordergrund ist inaktiv", "backup_controller_page_status_on": "Sicherung im Vordergrund ist aktiv", - "backup_controller_page_storage_format": "{} von {} genutzt", + "backup_controller_page_storage_format": "{used} von {total} genutzt", "backup_controller_page_to_backup": "Zu sichernde Alben", "backup_controller_page_total_sub": "Alle Fotos und Videos", "backup_controller_page_turn_off": "Sicherung im Vordergrund ausschalten", @@ -554,37 +554,40 @@ "backup_err_only_album": "Das einzige Album kann nicht entfernt werden", "backup_info_card_assets": "Elemente", "backup_manual_cancelled": "Abgebrochen", - "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut.", + "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut", "backup_manual_success": "Erfolgreich", "backup_manual_title": "Sicherungsstatus", "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "RÃŧckwärts", + "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", + "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", + "biometric_no_options": "Keine biometrischen Optionen verfÃŧgbar", + "biometric_not_available": "Die biometrische Authentifizierung ist auf diesem Gerät nicht verfÃŧgbar", "birthdate_saved": "Geburtsdatum erfolgreich gespeichert", "birthdate_set_description": "Das Geburtsdatum wird verwendet, um das Alter dieser Person zum Zeitpunkt eines Fotos zu berechnen.", "blurred_background": "Unscharfer Hintergrund", "bugs_and_feature_requests": "Fehler & Verbesserungsvorschläge", - "build": "Build", "build_image": "Build Abbild", "bulk_delete_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} lÃļschen mÃļchtest? Dabei wird die grÃļßte Datei jeder Gruppe behalten und alle anderen Duplikate endgÃŧltig gelÃļscht. Diese Aktion kann nicht rÃŧckgängig gemacht werden!", "bulk_keep_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien}} behalten mÃļchtest? Dies wird alle Duplikat-Gruppen auflÃļsen ohne etwas zu lÃļschen.", "bulk_trash_duplicates_confirmation": "Bist du sicher, dass du {count, plural, one {# duplizierte Datei} other {# duplizierte Dateien gemeinsam}} in den Papierkorb verschieben mÃļchtest? Dies wird die grÃļßte Datei jeder Gruppe behalten und alle anderen Duplikate in den Papierkorb verschieben.", "buy": "Immich erwerben", - "cache_settings_album_thumbnails": "Vorschaubilder der Bibliothek ({} Elemente)", + "cache_settings_album_thumbnails": "Vorschaubilder der Bibliothek ({count} Elemente)", "cache_settings_clear_cache_button": "Zwischenspeicher lÃļschen", "cache_settings_clear_cache_button_title": "LÃļscht den Zwischenspeicher der App. Dies wird die Leistungsfähigkeit der App deutlich einschränken, bis der Zwischenspeicher wieder aufgebaut wurde.", "cache_settings_duplicated_assets_clear_button": "LEEREN", "cache_settings_duplicated_assets_subtitle": "Fotos und Videos, die von der App blockiert werden", - "cache_settings_duplicated_assets_title": "Duplikate ({})", - "cache_settings_image_cache_size": "{} Bilder im Zwischenspeicher", + "cache_settings_duplicated_assets_title": "Duplikate ({count})", + "cache_settings_image_cache_size": "Bilder im Zwischenspeicher ({count} Bilder)", "cache_settings_statistics_album": "Vorschaubilder der Bibliothek", - "cache_settings_statistics_assets": "{} Elemente ({})", + "cache_settings_statistics_assets": "{count} Elemente ({size})", "cache_settings_statistics_full": "Originalbilder", "cache_settings_statistics_shared": "Vorschaubilder geteilter Alben", "cache_settings_statistics_thumbnail": "Vorschaubilder", "cache_settings_statistics_title": "Zwischenspeicher-Nutzung", "cache_settings_subtitle": "Kontrollieren, wie Immich den Zwischenspeicher nutzt", - "cache_settings_thumbnail_size": "{} Vorschaubilder im Zwischenspeicher", + "cache_settings_thumbnail_size": "Vorschaubilder im Zwischenspeicher ({count} Bilder)", "cache_settings_tile_subtitle": "Lokalen Speicher verwalten", "cache_settings_tile_title": "Lokaler Speicher", "cache_settings_title": "Zwischenspeicher Einstellungen", @@ -597,7 +600,9 @@ "cannot_merge_people": "Personen kÃļnnen nicht zusammengefÃŧhrt werden", "cannot_undo_this_action": "Diese Aktion kann nicht rÃŧckgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", + "cast": "Übertragen", "change_date": "Datum ändern", + "change_description": "Beschreibung anpassen", "change_display_order": "Anzeigereihenfolge ändern", "change_expiration_time": "Verfallszeitpunkt ändern", "change_location": "Ort ändern", @@ -610,6 +615,7 @@ "change_password_form_new_password": "Neues Passwort", "change_password_form_password_mismatch": "PasswÃļrter stimmen nicht Ãŧberein", "change_password_form_reenter_new_password": "Passwort erneut eingeben", + "change_pin_code": "PIN Code ändern", "change_your_password": "Ändere dein Passwort", "changed_visibility_successfully": "Die Sichtbarkeit wurde erfolgreich geändert", "check_all": "Alle prÃŧfen", @@ -624,14 +630,13 @@ "clear_all_recent_searches": "Alle letzten Suchvorgänge lÃļschen", "clear_message": "Nachrichten leeren", "clear_value": "Wert leeren", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Passwort eingeben", "client_cert_import": "Importieren", "client_cert_import_success_msg": "Client Zertifikat wurde importiert", "client_cert_invalid_msg": "UngÃŧltige Zertifikatsdatei oder falsches Passwort", "client_cert_remove_msg": "Client Zertifikat wurde entfernt", - "client_cert_subtitle": "UnterstÃŧtzt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login mÃļglich.", - "client_cert_title": "SSL-Client-Zertifikat ", + "client_cert_subtitle": "UnterstÃŧtzt nur das PKCS12 (.p12, .pfx) Format. Zertifikatsimporte oder -entfernungen sind nur vor dem Login mÃļglich", + "client_cert_title": "SSL-Client-Zertifikat", "clockwise": "Im Uhrzeigersinn", "close": "Schließen", "collapse": "Zusammenklappen", @@ -643,24 +648,26 @@ "comments_and_likes": "Kommentare & Likes", "comments_are_disabled": "Kommentare sind deaktiviert", "common_create_new_album": "Neues Album erstellen", - "common_server_error": "Bitte ÃŧberprÃŧfe Deine Netzwerkverbindung und stelle sicher, dass die App und Server Versionen kompatibel sind.", - "completed": "Fertig\n", + "common_server_error": "Bitte ÃŧberprÃŧfe deine Netzwerkverbindung und stelle sicher, dass die App und Server Versionen kompatibel sind.", + "completed": "Abgeschlossen", "confirm": "Bestätigen", "confirm_admin_password": "Administrator Passwort bestätigen", "confirm_delete_face": "Bist du sicher dass du das Gesicht von {name} aus der Datei entfernen willst?", "confirm_delete_shared_link": "Bist du sicher, dass du diesen geteilten Link lÃļschen willst?", "confirm_keep_this_delete_others": "Alle anderen Dateien im Stapel bis auf diese werden gelÃļscht. Bist du sicher, dass du fortfahren mÃļchten?", + "confirm_new_pin_code": "Neuen PIN Code bestätigen", "confirm_password": "Passwort bestätigen", + "connected_to": "Verbunden mit", "contain": "Vollständig", "context": "Kontext", "continue": "Fortsetzen", - "control_bottom_app_bar_album_info_shared": "{} Elemente ¡ Geteilt", + "control_bottom_app_bar_album_info_shared": "{count} Elemente ¡ Geteilt", "control_bottom_app_bar_create_new_album": "Neues Album erstellen", "control_bottom_app_bar_delete_from_immich": "Aus Immich lÃļschen", "control_bottom_app_bar_delete_from_local": "Vom Gerät lÃļschen", "control_bottom_app_bar_edit_location": "Ort bearbeiten", "control_bottom_app_bar_edit_time": "Datum und Uhrzeit bearbeiten", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Link teilen", "control_bottom_app_bar_share_to": "Teilen mit", "control_bottom_app_bar_trash_from_immich": "in den Papierkorb schieben", "copied_image_to_clipboard": "Das Bild wurde in die Zwischenablage kopiert.", @@ -692,9 +699,11 @@ "create_tag_description": "Erstelle einen neuen Tag. FÃŧr verschachtelte Tags, gib den gesamten Pfad inklusive Schrägstrich an.", "create_user": "Nutzer erstellen", "created": "Erstellt", + "created_at": "Erstellt", "crop": "Zuschneiden", "curated_object_page_title": "Dinge", "current_device": "Aktuelles Gerät", + "current_pin_code": "Aktueller PIN Code", "current_server_address": "Aktuelle Serveradresse", "custom_locale": "Benutzerdefinierte Sprache", "custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren", @@ -719,9 +728,9 @@ "delete_album": "Album lÃļschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-SchlÃŧssel lÃļschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", - "delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelÃļscht, bleiben aber auf dem Immich-Server.", - "delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelÃļscht.", - "delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelÃļscht.", + "delete_dialog_alert_local": "Diese Inhalte werden vom Gerät gelÃļscht, bleiben aber auf dem Immich-Server", + "delete_dialog_alert_local_non_backed_up": "Einige Inhalte sind nicht in Immich gesichert und werden dauerhaft vom Gerät gelÃļscht", + "delete_dialog_alert_remote": "Diese Inhalte werden dauerhaft vom Immich-Server gelÃļscht", "delete_dialog_ok_force": "Trotzdem lÃļschen", "delete_dialog_title": "EndgÃŧltig lÃļschen", "delete_duplicates_confirmation": "Bist du sicher, dass du diese Duplikate endgÃŧltig lÃļschen willst?", @@ -741,12 +750,10 @@ "deletes_missing_assets": "LÃļscht Dateien, die auf der Festplatte fehlen", "description": "Beschreibung", "description_input_hint_text": "Beschreibung hinzufÃŧgen...", - "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log fÃŧr mehr Details nachsehen.", - "details": "Details", + "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log fÃŧr mehr Details nachsehen", "direction": "Richtung", "disabled": "Deaktiviert", "disallow_edits": "Bearbeitungen verbieten", - "discord": "Discord", "discover": "Entdecken", "dismiss_all_errors": "Alle Fehler ignorieren", "dismiss_error": "Fehler ignorieren", @@ -758,23 +765,22 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", - "download_canceled": "Download abgebrochen!", - "download_complete": "Download vollständig!", - "download_enqueue": "Download in die Warteschlange gesetzt!", + "download_canceled": "Download abgebrochen", + "download_complete": "Download vollständig", + "download_enqueue": "Download in die Warteschlange gesetzt", "download_error": "Download fehlerhaft", - "download_failed": "Download fehlerhaft!", - "download_filename": "Datei: {}", + "download_failed": "Download fehlerhaft", + "download_filename": "Datei: {filename}", "download_finished": "Download abgeschlossen", "download_include_embedded_motion_videos": "Eingebettete Videos", "download_include_embedded_motion_videos_description": "Videos, die in Bewegungsfotos eingebettet sind, als separate Datei einfÃŧgen", - "download_notfound": "Download nicht gefunden!", - "download_paused": "Download pausiert!", - "download_settings": "Download", + "download_notfound": "Download nicht gefunden", + "download_paused": "Download pausiert", "download_settings_description": "Einstellungen fÃŧr das Herunterladen von Dateien verwalten", "download_started": "Download gestartet", "download_sucess": "Download erfolgreich", "download_sucess_android": "Die Datei wurde nach DCIM/Immich heruntergeladen", - "download_waiting_to_retry": "Warte auf erneuten Versuch...", + "download_waiting_to_retry": "Warte auf erneuten Versuch", "downloading": "Herunterladen", "downloading_asset_filename": "Datei {filename} wird heruntergeladen", "downloading_media": "Medien werden heruntergeladen", @@ -787,6 +793,8 @@ "edit_avatar": "Avatar bearbeiten", "edit_date": "Datum bearbeiten", "edit_date_and_time": "Datum und Uhrzeit bearbeiten", + "edit_description": "Beschreibung bearbeiten", + "edit_description_prompt": "Bitte wähle eine neue Beschreibung:", "edit_exclusion_pattern": "Ausschlussmuster bearbeiten", "edit_faces": "Gesichter bearbeiten", "edit_import_path": "Importpfad bearbeiten", @@ -807,19 +815,23 @@ "editor_crop_tool_h2_aspect_ratios": "Seitenverhältnisse", "editor_crop_tool_h2_rotation": "Drehung", "email": "E-Mail", + "email_notifications": "E-Mail Benachrichtigungen", "empty_folder": "Dieser Ordner ist leer", "empty_trash": "Papierkorb leeren", "empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgÃŧltig aus Immich und kann nicht rÃŧckgängig gemacht werden!", "enable": "Aktivieren", + "enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren", "enabled": "Aktiviert", "end_date": "Enddatum", "enqueued": "Eingereiht", "enter_wifi_name": "WLAN-Name eingeben", + "enter_your_pin_code": "PIN Code eingeben", + "enter_your_pin_code_subtitle": "Gib deinen PIN Code ein, um auf den gesperrten Ordner zuzugreifen", "error": "Fehler", "error_change_sort_album": "Ändern der Anzeigereihenfolge fehlgeschlagen", "error_delete_face": "Fehler beim LÃļschen des Gesichts", "error_loading_image": "Fehler beim Laden des Bildes", - "error_saving_image": "Fehler: {}", + "error_saving_image": "Fehler: {error}", "error_title": "Fehler - Etwas ist schief gelaufen", "errors": { "cannot_navigate_next_asset": "Kann nicht zur nächsten Datei navigieren", @@ -849,10 +861,12 @@ "failed_to_keep_this_delete_others": "Fehler beim LÃļschen der anderen Dateien", "failed_to_load_asset": "Fehler beim Laden der Datei", "failed_to_load_assets": "Fehler beim Laden der Dateien", + "failed_to_load_notifications": "Fehler beim Laden der Benachrichtigungen", "failed_to_load_people": "Fehler beim Laden von Personen", "failed_to_remove_product_key": "Fehler beim Entfernen des ProduktschlÃŧssels", "failed_to_stack_assets": "Dateien konnten nicht gestapelt werden", "failed_to_unstack_assets": "Dateien konnten nicht entstapelt werden", + "failed_to_update_notification_status": "Benachrichtigungsstatus aktualisieren fehlgeschlagen", "import_path_already_exists": "Dieser Importpfad existiert bereits.", "incorrect_email_or_password": "UngÃŧltige E-Mail oder Passwort", "paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden", @@ -870,6 +884,7 @@ "unable_to_archive_unarchive": "Konnte nicht {archived, select, true {archivieren} other {entarchivieren}}", "unable_to_change_album_user_role": "Die Rolle des Albumbenutzers kann nicht geändert werden", "unable_to_change_date": "Datum kann nicht verändert werden", + "unable_to_change_description": "Ändern der Beschreibung nicht mÃļglich", "unable_to_change_favorite": "Es konnte der Favoritenstatus fÃŧr diese Datei nicht geändert werden", "unable_to_change_location": "Ort kann nicht verändert werden", "unable_to_change_password": "Passwort konnte nicht geändert werden", @@ -907,6 +922,7 @@ "unable_to_log_out_all_devices": "Konnte nicht von allen Geräten abmelden", "unable_to_log_out_device": "Konnte nicht vom Gerät abmelden", "unable_to_login_with_oauth": "Anmeldung mit OAuth nicht mÃļglich", + "unable_to_move_to_locked_folder": "Konnte nicht in den gesperrten Ordner verschoben werden", "unable_to_play_video": "Das Video kann nicht wiedergegeben werden", "unable_to_reassign_assets_existing_person": "Kann Dateien nicht {name, select, null {einer vorhandenen Person} other {{name}}} zuweisen", "unable_to_reassign_assets_new_person": "Dateien konnten nicht einer neuen Person zugeordnet werden", @@ -920,6 +936,7 @@ "unable_to_remove_reaction": "Reaktion kann nicht entfernt werden", "unable_to_repair_items": "Objekte kÃļnnen nicht repariert werden", "unable_to_reset_password": "Passwort kann nicht zurÃŧckgesetzt werden", + "unable_to_reset_pin_code": "ZurÃŧcksetzen des PIN Code nicht mÃļglich", "unable_to_resolve_duplicate": "Duplikate kÃļnnen nicht aufgelÃļst werden", "unable_to_restore_assets": "Dateien konnten nicht wiederhergestellt werden", "unable_to_restore_trash": "Papierkorb kann nicht wiederhergestellt werden", @@ -949,14 +966,13 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Beschreibung hinzufÃŧgen...", - "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", "exif_bottom_sheet_people": "PERSONEN", "exif_bottom_sheet_person_add_person": "Namen hinzufÃŧgen", - "exif_bottom_sheet_person_age": "Alter {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Alter {age}", + "exif_bottom_sheet_person_age_months": "{months} Monate alt", + "exif_bottom_sheet_person_age_year_months": "1 Jahr, {months} Monate alt", + "exif_bottom_sheet_person_age_years": "Alter {years}", "exit_slideshow": "Diashow beenden", "expand_all": "Alle aufklappen", "experimental_settings_new_asset_list_subtitle": "In Arbeit", @@ -965,7 +981,7 @@ "experimental_settings_title": "Experimentell", "expire_after": "Verfällt nach", "expired": "Verfallen", - "expires_date": "Läuft {date} ab", + "expires_date": "Läuft ab: {date}", "explore": "Erkunden", "explorer": "Datei-Explorer", "export": "Exportieren", @@ -977,6 +993,7 @@ "external_network_sheet_info": "Wenn sich die App nicht im bevorzugten WLAN-Netzwerk befindet, verbindet sie sich mit dem Server Ãŧber die erste der folgenden URLs, die sie erreichen kann (von oben nach unten)", "face_unassigned": "Nicht zugewiesen", "failed": "Fehlgeschlagen", + "failed_to_authenticate": "Authentifizierung fehlgeschlagen", "failed_to_load_assets": "Laden der Assets fehlgeschlagen", "failed_to_load_folder": "Fehler beim Laden des Ordners", "favorite": "Favorit", @@ -990,8 +1007,8 @@ "file_name_or_extension": "Dateiname oder -erweiterung", "filename": "Dateiname", "filetype": "Dateityp", - "filter": "Filter", "filter_people": "Personen filtern", + "filter_places": "Orte filtern", "find_them_fast": "Finde sie schneller mit der Suche nach Namen", "fix_incorrect_match": "Fehlerhafte Übereinstimmung beheben", "folder": "Ordner", @@ -1001,7 +1018,7 @@ "forward": "Vorwärts", "general": "Allgemein", "get_help": "Hilfe erhalten", - "get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist.\n", + "get_wifiname_error": "WLAN-Name konnte nicht ermittelt werden. Vergewissere dich, dass die erforderlichen Berechtigungen erteilt wurden und du mit einem WLAN-Netzwerk verbunden bist", "getting_started": "Erste Schritte", "go_back": "ZurÃŧck", "go_to_folder": "Gehe zu Ordner", @@ -1030,23 +1047,24 @@ "hide_person": "Person verbergen", "hide_unnamed_people": "Unbenannte Personen verbergen", "home_page_add_to_album_conflicts": "{added} Elemente zu {album} hinzugefÃŧgt. {failed} Elemente sind bereits vorhanden.", - "home_page_add_to_album_err_local": "Es kÃļnnen lokale Elemente noch nicht zu Alben hinzugefÃŧgt werden, Ãŧberspringen...", + "home_page_add_to_album_err_local": "Es kÃļnnen lokale Elemente noch nicht zu Alben hinzugefÃŧgt werden, Ãŧberspringen", "home_page_add_to_album_success": "{added} Elemente zu {album} hinzugefÃŧgt.", - "home_page_album_err_partner": "Inhalte von Partnern kÃļnnen derzeit nicht zu Alben hinzugefÃŧgt werden!", - "home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, Ãŧberspringen...", - "home_page_archive_err_partner": "Inhalte von Partnern kÃļnnen nicht archiviert werden!", - "home_page_building_timeline": "Zeitachse wird erstellt.", - "home_page_delete_err_partner": "Inhalte von Partnern kÃļnnen nicht gelÃļscht werden!", - "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, Ãŧberspringen...", - "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, Ãŧberspringen...", - "home_page_favorite_err_partner": "Inhalte von Partnern kÃļnnen nicht favorisiert werden!", - "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefÃŧllt werden kann.", + "home_page_album_err_partner": "Inhalte von Partnern kÃļnnen derzeit nicht zu Alben hinzugefÃŧgt werden", + "home_page_archive_err_local": "Kann lokale Elemente nicht archvieren, Ãŧberspringen", + "home_page_archive_err_partner": "Inhalte von Partnern kÃļnnen nicht archiviert werden", + "home_page_building_timeline": "Zeitachse wird erstellt", + "home_page_delete_err_partner": "Inhalte von Partnern kÃļnnen nicht gelÃļscht werden, Ãŧberspringe", + "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, Ãŧberspringen", + "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, Ãŧberspringen", + "home_page_favorite_err_partner": "Inhalte von Partnern kÃļnnen nicht favorisiert werden, Ãŧberspringe", + "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefÃŧllt werden kann", + "home_page_locked_error_local": "Lokale Dateien kÃļnnen nicht in den gesperrten Ordner verschoben werden, Ãŧberspringe", + "home_page_locked_error_partner": "Dateien von Partnern kÃļnnen nicht in den gesperrten Ordner verschoben werden, Ãŧberspringe", "home_page_share_err_local": "Lokale Inhalte kÃļnnen nicht per Link geteilt werden, Ãŧberspringe", - "home_page_upload_err_limit": "Es kÃļnnen max. 30 Elemente gleichzeitig hochgeladen werden, Ãŧberspringen...", - "host": "Host", + "home_page_upload_err_limit": "Es kÃļnnen max. 30 Elemente gleichzeitig hochgeladen werden, Ãŧberspringen", "hour": "Stunde", "ignore_icloud_photos": "iCloud Fotos ignorieren", - "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", + "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", "image_alt_text_date": "{isVideo, select, true {Video} other {Bild}} aufgenommen am {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Bild}} aufgenommen mit {person1} am {date}", @@ -1073,14 +1091,13 @@ "include_shared_partner_assets": "Geteilte Partner-Dateien mit einbeziehen", "individual_share": "Individuelle Freigabe", "individual_shares": "Individuelles Teilen", - "info": "Info", "interval": { "day_at_onepm": "Täglich um 13:00 Uhr", "hours": "{hours, plural, one {Jede Stunde} other {Alle {hours, number} Stunden}}", "night_at_midnight": "Täglich um Mitternacht", "night_at_twoam": "Täglich nachts um 2:00 Uhr" }, - "invalid_date": "UngÃŧltiges Datum ", + "invalid_date": "UngÃŧltiges Datum", "invalid_date_format": "UngÃŧltiges Datumsformat", "invite_people": "Personen einladen", "invite_to_album": "Zum Album einladen", @@ -1099,7 +1116,6 @@ "leave": "Verlassen", "lens_model": "Objektivmodell", "let_others_respond": "Antworten zulassen", - "level": "Level", "library": "Bibliothek", "library_options": "Bibliotheksoptionen", "library_page_device_albums": "Alben auf dem Gerät", @@ -1120,12 +1136,14 @@ "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt Ãŧber diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", - "location_permission_content": "Um die automatische Umschaltfunktion nutzen zu kÃļnnen, benÃļtigt Immich eine genaue Standortberechtigung, damit es den Namen des aktuellen WLAN-Netzwerks ermitteln kann", + "location_permission_content": "Um die automatische Umschaltfunktion nutzen zu kÃļnnen, benÃļtigt Immich genaue Standortberechtigung, damit es den Namen des aktuellen WLAN-Netzwerks ermitteln kann", "location_picker_choose_on_map": "Auf der Karte auswählen", "location_picker_latitude_error": "GÃŧltigen Breitengrad eingeben", "location_picker_latitude_hint": "Breitengrad eingeben", "location_picker_longitude_error": "GÃŧltigen Längengrad eingeben", "location_picker_longitude_hint": "Längengrad eingeben", + "lock": "Sperren", + "locked_folder": "Gesperrter Ordner", "log_out": "Abmelden", "log_out_all_devices": "Alle Geräte abmelden", "logged_out_all_devices": "Alle Geräte abgemeldet", @@ -1143,7 +1161,7 @@ "login_form_err_leading_whitespace": "Leerzeichen am Anfang", "login_form_err_trailing_whitespace": "Leerzeichen am Ende", "login_form_failed_get_oauth_server_config": "Fehler beim Login per OAuth, bitte Server-URL ÃŧberprÃŧfen", - "login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfÃŧgbar.", + "login_form_failed_get_oauth_server_disable": "Die OAuth-Funktion ist auf diesem Server nicht verfÃŧgbar", "login_form_failed_login": "Fehler beim Login, bitte ÃŧberprÃŧfe die Server-URL, deine E-Mail oder das Passwort", "login_form_handshake_exception": "Fehler beim Verbindungsaufbau mit dem Server. Falls du ein selbstsigniertes Zertifikat verwendest, aktiviere die UnterstÃŧtzung dafÃŧr in den Einstellungen.", "login_form_password_hint": "Passwort", @@ -1151,8 +1169,8 @@ "login_form_server_empty": "Serveradresse eingeben.", "login_form_server_error": "Es Konnte sich nicht mit dem Server verbunden werden.", "login_has_been_disabled": "Die Anmeldung wurde deaktiviert.", - "login_password_changed_error": "Fehler beim Passwort ändern!", - "login_password_changed_success": "Passwort erfolgreich geändert.", + "login_password_changed_error": "Fehler beim Ändern deines Passwort", + "login_password_changed_success": "Passwort erfolgreich geändert", "logout_all_device_confirmation": "Bist du sicher, dass du alle Geräte abmelden willst?", "logout_this_device_confirmation": "Bist du sicher, dass du dieses Gerät abmelden willst?", "longitude": "Längengrad", @@ -1170,9 +1188,9 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-VerknÃŧpfung verwalten", "map": "Karte", - "map_assets_in_bound": "{} Foto", - "map_assets_in_bounds": "{} Fotos", - "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden!", + "map_assets_in_bound": "{count} Foto", + "map_assets_in_bounds": "{count} Fotos", + "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", "map_location_service_disabled_content": "Ortungsdienste mÃŧssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?", @@ -1181,29 +1199,30 @@ "map_marker_with_image": "Kartenmarkierung mit Bild", "map_no_assets_in_bounds": "Keine Fotos in dieser Gegend", "map_no_location_permission_content": "Ortungsdienste mÃŧssen aktiviert sein, um Inhalte am aktuellen Standort anzuzeigen. Willst du die Ortungsdienste jetzt aktivieren?", - "map_no_location_permission_title": "Kein Zugriff auf den Standort!", + "map_no_location_permission_title": "Kein Zugriff auf den Standort", "map_settings": "Karteneinstellungen", "map_settings_dark_mode": "Dunkler Modus", "map_settings_date_range_option_day": "Letzte 24 Stunden", - "map_settings_date_range_option_days": "Letzte {} Tage", + "map_settings_date_range_option_days": "Letzten {days} Tage", "map_settings_date_range_option_year": "Letztes Jahr", - "map_settings_date_range_option_years": "Letzte {} Jahre", + "map_settings_date_range_option_years": "Letzten {years} Jahre", "map_settings_dialog_title": "Karteneinstellungen", "map_settings_include_show_archived": "Archivierte anzeigen", "map_settings_include_show_partners": "Partner einbeziehen", "map_settings_only_show_favorites": "Nur Favoriten anzeigen", "map_settings_theme_settings": "Karten Design", "map_zoom_to_see_photos": "Ansicht verkleinern um Fotos zu sehen", + "mark_all_as_read": "Alle als gelesen markieren", + "mark_as_read": "Als gelesen markieren", + "marked_all_as_read": "Alle als gelesen markiert", "matches": "Treffer", "media_type": "Medientyp", "memories": "Erinnerungen", "memories_all_caught_up": "Alles aufgeholt", - "memories_check_back_tomorrow": "Schau morgen wieder vorbei fÃŧr weitere Erinnerungen!", + "memories_check_back_tomorrow": "Schau morgen wieder vorbei fÃŧr weitere Erinnerungen", "memories_setting_description": "Verwalte, was du in deinen Erinnerungen siehst", "memories_start_over": "Erneut beginnen", "memories_swipe_to_close": "Nach oben Wischen zum schließen", - "memories_year_ago": "ein Jahr her", - "memories_years_ago": "{} Jahre her", "memory": "Erinnerung", "memory_lane_title": "Foto-Erinnerungen {title}", "menu": "MenÃŧ", @@ -1214,18 +1233,21 @@ "merge_people_successfully": "Personen erfolgreich zusammengefÃŧhrt", "merged_people_count": "{count, plural, one {# Person} other {# Personen}} zusammengefÃŧgt", "minimize": "Minimieren", - "minute": "Minute", "missing": "Fehlende", "model": "Modell", "month": "Monat", - "monthly_title_text_date_format": "MMMM y", "more": "Mehr", + "move": "Verschieben", + "move_off_locked_folder": "Aus dem gesperrten Ordner verschieben", + "move_to_locked_folder": "In den gesperrten Ordner verschieben", + "move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und kÃļnnen nur noch im gesperrten Ordner angezeigt werden", + "moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert", + "moved_to_library": "{count, plural, one {# Datei} other {# Dateien}} in die Bibliothek verschoben", "moved_to_trash": "In den Papierkorb verschoben", - "multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen...", - "multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen...", + "multiselect_grid_edit_date_time_err_read_only": "Das Datum und die Uhrzeit von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen", + "multiselect_grid_edit_gps_err_read_only": "Der Aufnahmeort von schreibgeschÃŧtzten Inhalten kann nicht verändert werden, Ãŧberspringen", "mute_memories": "Erinnerungen stumm schalten", "my_albums": "Meine Alben", - "name": "Name", "name_or_nickname": "Name oder Nickname", "networking_settings": "Netzwerk", "networking_subtitle": "Verwaltung von Server-Endpunkt-Einstellungen", @@ -1234,6 +1256,8 @@ "new_api_key": "Neuer API-SchlÃŧssel", "new_password": "Neues Passwort", "new_person": "Neue Person", + "new_pin_code": "Neuer PIN Code", + "new_pin_code_subtitle": "Dies ist dein erster Zugriff auf den gesperrten Ordner. Erstelle einen PIN Code fÃŧr den sicheren Zugriff auf diese Seite", "new_user_created": "Neuer Benutzer wurde erstellt", "new_version_available": "NEUE VERSION VERFÜGBAR", "newest_first": "Neueste zuerst", @@ -1251,7 +1275,10 @@ "no_explore_results_message": "Lade weitere Fotos hoch, um deine Sammlung zu erkunden.", "no_favorites_message": "FÃŧge Favoriten hinzu, um deine besten Bilder und Videos schnell zu finden", "no_libraries_message": "Eine externe Bibliothek erstellen, um deine Fotos und Videos anzusehen", + "no_locked_photos_message": "Fotos und Videos im gesperrten Ordner sind versteckt und werden nicht angezeigt, wenn du deine Bibliothek durchsuchst.", "no_name": "Kein Name", + "no_notifications": "Keine Benachrichtigungen", + "no_people_found": "Keine passenden Personen gefunden", "no_places": "Keine Orte", "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", @@ -1260,19 +1287,17 @@ "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", "notes": "Notizen", - "notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\"", - "notification_permission_list_tile_content": "Erlaube Berechtigung fÃŧr Benachrichtigungen", + "nothing_here_yet": "Noch nichts hier", + "notification_permission_dialog_content": "Um Benachrichtigungen zu aktivieren, navigiere zu Einstellungen und klicke \"Erlauben\".", + "notification_permission_list_tile_content": "Erlaube Berechtigung fÃŧr Benachrichtigungen.", "notification_permission_list_tile_enable_button": "Aktiviere Benachrichtigungen", "notification_permission_list_tile_title": "Benachrichtigungs-Berechtigung", "notification_toggle_setting_description": "E-Mail-Benachrichtigungen aktivieren", "notifications": "Benachrichtigungen", "notifications_setting_description": "Benachrichtigungen verwalten", - "oauth": "OAuth", "official_immich_resources": "Offizielle Immich Quellen", - "offline": "Offline", "offline_paths": "Offline-Pfade", "offline_paths_description": "Diese Ergebnisse kÃļnnen auf das manuelle LÃļschen von Dateien zurÃŧckzufÃŧhren sein, die nicht Teil einer externen Bibliothek sind.", - "ok": "Ok", "oldest_first": "Älteste zuerst", "on_this_device": "Auf diesem Gerät", "onboarding": "Einstieg", @@ -1280,8 +1305,8 @@ "onboarding_theme_description": "Wähle ein Farbschema fÃŧr deine Instanz aus. Du kannst dies später in deinen Einstellungen ändern.", "onboarding_welcome_description": "Lass uns deine Instanz mit einigen allgemeinen Einstellungen konfigurieren.", "onboarding_welcome_user": "Willkommen, {user}", - "online": "Online", "only_favorites": "Nur Favoriten", + "open": "Öffnen", "open_in_map_view": "In Kartenansicht Ãļffnen", "open_in_openstreetmap": "In OpenStreetMap Ãļffnen", "open_the_search_filters": "Die Suchfilter Ãļffnen", @@ -1294,7 +1319,6 @@ "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", - "partner": "Partner", "partner_can_access": "{partner} hat Zugriff", "partner_can_access_assets": "auf alle deine Fotos und Videos, außer die Archivierten und GelÃļschten", "partner_can_access_location": "auf den Ort, an dem deine Fotos aufgenommen wurden", @@ -1305,7 +1329,7 @@ "partner_page_partner_add_failed": "Fehler beim Partner hinzufÃŧgen", "partner_page_select_partner": "Partner auswählen", "partner_page_shared_to_title": "Geteilt mit", - "partner_page_stop_sharing_content": "{} wird nicht mehr auf deine Fotos zugreifen kÃļnnen.", + "partner_page_stop_sharing_content": "{partner} wird nicht mehr auf deine Fotos zugreifen kÃļnnen.", "partner_sharing": "Partner-Sharing", "partners": "Partner", "password": "Passwort", @@ -1319,7 +1343,6 @@ }, "path": "Pfad", "pattern": "Muster", - "pause": "Pause", "pause_memories": "Erinnerungen pausieren", "paused": "Pausiert", "pending": "Ausstehend", @@ -1342,7 +1365,6 @@ "permission_onboarding_permission_granted": "Berechtigung erteilt! Du bist startklar.", "permission_onboarding_permission_limited": "Berechtigungen unzureichend. Um Immich das Sichern von ganzen Sammlungen zu ermÃļglichen, muss der Zugriff auf alle Fotos und Videos in den Einstellungen erlaubt werden.", "permission_onboarding_request": "Immich benÃļtigt Berechtigung um auf deine Fotos und Videos zuzugreifen.", - "person": "Person", "person_birthdate": "Geboren am {date}", "person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}", "photo_shared_all_users": "Es sieht so aus, als hättest du deine Fotos mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", @@ -1351,6 +1373,10 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos von vorherigen Jahren", "pick_a_location": "Wähle einen Ort", + "pin_code_changed_successfully": "PIN Code erfolgreich geändert", + "pin_code_reset_successfully": "PIN Code erfolgreich zurÃŧckgesetzt", + "pin_code_setup_successfully": "PIN Code erfolgreich festgelegt", + "pin_verification": "PIN Code ÜberprÃŧfung", "place": "Ort", "places": "Orte", "places_count": "{count, plural, one {{count, number} Ort} other {{count, number} Orte}}", @@ -1358,7 +1384,7 @@ "play_memories": "Erinnerungen abspielen", "play_motion_photo": "Bewegte Bilder abspielen", "play_or_pause_video": "Video abspielen oder pausieren", - "port": "Port", + "please_auth_to_access": "FÃŧr den Zugriff bitte Authentifizieren", "preferences_settings_subtitle": "App-Einstellungen verwalten", "preferences_settings_title": "Voreinstellungen", "preset": "Voreinstellung", @@ -1368,11 +1394,10 @@ "previous_or_next_photo": "Vorheriges oder nächstes Foto", "primary": "Primär", "privacy": "Privatsphäre", - "profile_drawer_app_logs": "Logs", + "profile": "Profil", "profile_drawer_client_out_of_date_major": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Major-Version.", "profile_drawer_client_out_of_date_minor": "Mobile-App ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.", - "profile_drawer_client_server_up_to_date": "Die App-Version / Server-Version sind aktuell.", - "profile_drawer_github": "GitHub", + "profile_drawer_client_server_up_to_date": "Die App- und Server-Versionen sind aktuell", "profile_drawer_server_out_of_date_major": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Major-Version.", "profile_drawer_server_out_of_date_minor": "Server-Version ist veraltet. Bitte aktualisiere auf die neueste Minor-Version.", "profile_image_of_user": "Profilbild von {user}", @@ -1381,7 +1406,7 @@ "public_share": "Öffentliche Freigabe", "purchase_account_info": "UnterstÃŧtzer", "purchase_activated_subtitle": "Danke fÃŧr die UnterstÃŧtzung von Immich und Open-Source Software", - "purchase_activated_time": "Aktiviert am {date, date}", + "purchase_activated_time": "Aktiviert am {date}", "purchase_activated_title": "Dein SchlÃŧssel wurde erfolgreich aktiviert", "purchase_button_activate": "Aktivieren", "purchase_button_buy": "Kaufen", @@ -1409,7 +1434,6 @@ "purchase_remove_server_product_key_prompt": "Sicher, dass der Server-ProduktschlÃŧssel entfernt werden soll?", "purchase_server_description_1": "FÃŧr den gesamten Server", "purchase_server_description_2": "UnterstÃŧtzerstatus", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Der Server-ProduktschlÃŧssel wird durch den Administrator verwaltet", "rating": "Bewertung", "rating_clear": "Bewertung lÃļschen", @@ -1426,6 +1450,8 @@ "recent_searches": "Letzte Suchen", "recently_added": "KÃŧrzlich hinzugefÃŧgt", "recently_added_page_title": "Zuletzt hinzugefÃŧgt", + "recently_taken": "KÃŧrzlich aufgenommen", + "recently_taken_page_title": "KÃŧrzlich aufgenommen", "refresh": "Aktualisieren", "refresh_encoded_videos": "Kodierte Videos aktualisieren", "refresh_faces": "Gesichter aktualisieren", @@ -1445,6 +1471,8 @@ "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", "remove_from_favorites": "Aus Favoriten entfernen", + "remove_from_locked_folder": "Aus gesperrtem Ordner entfernen", + "remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen mÃļchtest? Sie werden wieder in deiner Bibliothek sichtbar sein", "remove_from_shared_link": "Aus geteiltem Link entfernen", "remove_memory": "Erinnerung entfernen", "remove_photo_from_memory": "Foto aus dieser Erinnerung entfernen", @@ -1461,13 +1489,13 @@ "repair": "Reparatur", "repair_no_results_message": "Nicht auffindbare und fehlende Dateien werden hier angezeigt", "replace_with_upload": "Durch Upload ersetzen", - "repository": "Repository", "require_password": "Passwort erforderlich", "require_user_to_change_password_on_first_login": "Benutzer muss das Passwort beim ersten Login ändern", "rescan": "Erneut scannen", "reset": "ZurÃŧcksetzen", "reset_password": "Passwort zurÃŧcksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurÃŧcksetzen", + "reset_pin_code": "PIN Code zurÃŧcksetzen", "reset_to_default": "Auf Standard zurÃŧcksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelÃļst", @@ -1504,7 +1532,7 @@ "search_city": "Suche nach Stadt...", "search_country": "Suche nach Land...", "search_filter_apply": "Filter anwenden", - "search_filter_camera_title": "Kameratyp auswählen ", + "search_filter_camera_title": "Kameratyp auswählen", "search_filter_date": "Datum", "search_filter_date_interval": "{start} bis {end}", "search_filter_date_title": "Wähle einen Zeitraum", @@ -1512,10 +1540,10 @@ "search_filter_display_options": "Anzeigeeinstellungen", "search_filter_filename": "Suche nach Dateiname", "search_filter_location": "Ort", - "search_filter_location_title": "Ort auswählen ", + "search_filter_location_title": "Ort auswählen", "search_filter_media_type": "Medientyp", - "search_filter_media_type_title": "Medientyp auswählen ", - "search_filter_people_title": "Personen auswählen ", + "search_filter_media_type_title": "Medientyp auswählen", + "search_filter_people_title": "Personen auswählen", "search_for": "Suche nach", "search_for_existing_person": "Suche nach vorhandener Person", "search_no_more_result": "Keine weiteren Ergebnisse", @@ -1529,7 +1557,6 @@ "search_page_no_places": "Keine Informationen Ãŧber Orte verfÃŧgbar", "search_page_screenshots": "Bildschirmfotos", "search_page_search_photos_videos": "Nach deinen Fotos und Videos suchen", - "search_page_selfies": "Selfies", "search_page_things": "Gegenstände und Tiere", "search_page_view_all_button": "Alle anzeigen", "search_page_your_activity": "Deine Aktivität", @@ -1560,6 +1587,7 @@ "select_keep_all": "Alle behalten", "select_library_owner": "Bibliotheksbesitzer auswählen", "select_new_face": "Neues Gesicht auswählen", + "select_person_to_tag": "Wählen Sie eine Person zum Markieren aus", "select_photos": "Fotos auswählen", "select_trash_all": "Alle lÃļschen", "select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden", @@ -1590,13 +1618,13 @@ "setting_languages_apply": "Anwenden", "setting_languages_subtitle": "App-Sprache ändern", "setting_languages_title": "Sprachen", - "setting_notifications_notify_failures_grace_period": "Benachrichtigung bei Fehler/n in der Hintergrundsicherung: {}", - "setting_notifications_notify_hours": "{} Stunden", + "setting_notifications_notify_failures_grace_period": "Benachrichtigung bei Fehler(n) in der Hintergrundsicherung: {duration}", + "setting_notifications_notify_hours": "{count} Stunden", "setting_notifications_notify_immediately": "sofort", - "setting_notifications_notify_minutes": "{} Minuten", + "setting_notifications_notify_minutes": "{count} Minuten", "setting_notifications_notify_never": "niemals", - "setting_notifications_notify_seconds": "{} Sekunden", - "setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt fÃŧr jedes Element.", + "setting_notifications_notify_seconds": "{count} Sekunden", + "setting_notifications_single_progress_subtitle": "Detaillierter Upload-Fortschritt fÃŧr jedes Element", "setting_notifications_single_progress_title": "Zeige den detaillierten Fortschritt der Hintergrundsicherung", "setting_notifications_subtitle": "Benachrichtigungen anpassen", "setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)", @@ -1605,14 +1633,16 @@ "setting_video_viewer_original_video_subtitle": "Beim Streaming eines Videos vom Server wird das Original abgespielt, auch wenn eine Transkodierung verfÃŧgbar ist. Kann zu Pufferung fÃŧhren. Lokal verfÃŧgbare Videos werden unabhängig von dieser Einstellung in Originalqualität wiedergegeben.", "setting_video_viewer_original_video_title": "Originalvideo erzwingen", "settings": "Einstellungen", - "settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden.", + "settings_require_restart": "Bitte starte Immich neu, um diese Einstellung anzuwenden", "settings_saved": "Einstellungen gespeichert", + "setup_pin_code": "Einen PIN Code festlegen", "share": "Teilen", "share_add_photos": "Fotos hinzufÃŧgen", - "share_assets_selected": "{} ausgewählt", + "share_assets_selected": "{count} ausgewählt", "share_dialog_preparing": "Vorbereiten...", + "share_link": "Link teilen", "shared": "Geteilt", - "shared_album_activities_input_disable": "Kommentare sind deaktiviert.", + "shared_album_activities_input_disable": "Kommentare sind deaktiviert", "shared_album_activity_remove_content": "MÃļchtest du diese Aktivität entfernen?", "shared_album_activity_remove_title": "Aktivität entfernen", "shared_album_section_people_action_error": "Fehler beim Verlassen oder Entfernen aus dem Album", @@ -1623,34 +1653,33 @@ "shared_by_user": "Von {user} geteilt", "shared_by_you": "Von dir geteilt", "shared_from_partner": "Fotos von {partner}", - "shared_intent_upload_button_progress_text": "{} / {} hochgeladen", + "shared_intent_upload_button_progress_text": "{current} / {total} hochgeladen", "shared_link_app_bar_title": "Geteilte Links", "shared_link_clipboard_copied_massage": "Link kopiert", - "shared_link_clipboard_text": "Link: {}\nPasswort: {}", + "shared_link_clipboard_text": "Link: {link}\nPasswort: {password}", "shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe", "shared_link_edit_description_hint": "Beschreibung eingeben", "shared_link_edit_expire_after_option_day": "1 Tag", - "shared_link_edit_expire_after_option_days": "{} Tage", + "shared_link_edit_expire_after_option_days": "{count} Tagen", "shared_link_edit_expire_after_option_hour": "1 Stunde", - "shared_link_edit_expire_after_option_hours": "{} Stunden", + "shared_link_edit_expire_after_option_hours": "{count} Stunden", "shared_link_edit_expire_after_option_minute": "1 Minute", - "shared_link_edit_expire_after_option_minutes": "{} Minuten", - "shared_link_edit_expire_after_option_months": "{} Monat/en", - "shared_link_edit_expire_after_option_year": "{} Jahr/en", + "shared_link_edit_expire_after_option_minutes": "{count} Minuten", + "shared_link_edit_expire_after_option_months": "{count} Monaten", + "shared_link_edit_expire_after_option_year": "{count} Jahr", "shared_link_edit_password_hint": "Passwort eingeben", "shared_link_edit_submit_button": "Link aktualisieren", "shared_link_error_server_url_fetch": "Fehler beim Ermitteln der Server-URL", - "shared_link_expires_day": "Verfällt in {} Tag", - "shared_link_expires_days": "Verfällt in {} Tag/en", - "shared_link_expires_hour": "Verfällt in {} Stunde", - "shared_link_expires_hours": "Verfällt in {} Stunde/n", - "shared_link_expires_minute": "Verfällt in {} Minute", - "shared_link_expires_minutes": "Verfällt in {} Minute/n", + "shared_link_expires_day": "Läuft ab in {count} Tag", + "shared_link_expires_days": "Läuft ab in {count} Tagen", + "shared_link_expires_hour": "Läuft ab in {count} Stunde", + "shared_link_expires_hours": "Läuft ab in {count} Stunden", + "shared_link_expires_minute": "Läuft ab in {count} Minute", + "shared_link_expires_minutes": "Läuft ab in {count} Minuten", "shared_link_expires_never": "Läuft nie ab", - "shared_link_expires_second": "Verfällt in {} Sekunde", - "shared_link_expires_seconds": "Verfällt in {} Sekunde/n", + "shared_link_expires_second": "Läuft ab in {count} Sekunde", + "shared_link_expires_seconds": "Läuft ab in {count} Sekunden", "shared_link_individual_shared": "Individuell geteilt", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", "shared_link_options": "Optionen fÃŧr geteilten Link", "shared_links": "Geteilte Links", @@ -1712,17 +1741,16 @@ "stack_select_one_photo": "Hauptfoto fÃŧr den Stapel auswählen", "stack_selected_photos": "Ausgewählte Fotos stapeln", "stacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} gestapelt", - "stacktrace": "Stacktrace", "start": "Starten", "start_date": "Anfangsdatum", "state": "Bundesland / Provinz", - "status": "Status", "stop_motion_photo": "Stop-Motion-Foto", "stop_photo_sharing": "Deine Fotos nicht mehr teilen?", "stop_photo_sharing_description": "{partner} wird keinen Zugriff mehr auf deine Fotos haben.", "stop_sharing_photos_with_user": "AufhÃļren Fotos mit diesem Benutzer zu teilen", "storage": "Speicherplatz", "storage_label": "Speicherpfad", + "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", "suggestions": "Vorschläge", @@ -1735,26 +1763,23 @@ "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", - "tag": "Tag", "tag_assets": "Dateien taggen", "tag_created": "Tag erstellt: {tag}", "tag_feature_description": "Durchsuchen von Fotos und Videos, gruppiert nach logischen Tag-Themen", - "tag_not_found_question": "Kein Tag zu finden? Erstelle einen neuen Tag.", + "tag_not_found_question": "Kein Tag vorhanden? Erstelle einen neuen Tag.", "tag_people": "Personen taggen", "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", - "tags": "Tags", "template": "Vorlage", - "theme": "Theme", "theme_selection": "Themenauswahl", "theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers", "theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild", - "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({})", - "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden", + "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})", + "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", "theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters", "theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters", - "theme_setting_primary_color_subtitle": "Farbauswahl fÃŧr primäre Aktionen und Akzente", + "theme_setting_primary_color_subtitle": "Farbauswahl fÃŧr primäre Aktionen und Akzente.", "theme_setting_primary_color_title": "Primärfarbe", "theme_setting_system_primary_color_title": "Systemfarbe verwenden", "theme_setting_system_theme_switch": "Automatisch (Systemeinstellung)", @@ -1784,13 +1809,15 @@ "trash_no_results_message": "GelÃļschte Fotos und Videos werden hier angezeigt.", "trash_page_delete_all": "Alle lÃļschen", "trash_page_empty_trash_dialog_content": "Elemente im Papierkorb lÃļschen? Diese Elemente werden dauerhaft aus Immich entfernt", - "trash_page_info": "Elemente im Papierkorb werden nach {} Tagen endgÃŧltig gelÃļscht.", + "trash_page_info": "Elemente im Papierkorb werden nach {days} Tagen endgÃŧltig gelÃļscht", "trash_page_no_assets": "Es gibt keine Daten im Papierkorb", "trash_page_restore_all": "Alle wiederherstellen", "trash_page_select_assets_btn": "Elemente auswählen", - "trash_page_title": "Papierkorb ({})", + "trash_page_title": "Papierkorb ({count})", "trashed_items_will_be_permanently_deleted_after": "GelÃļschte Objekte werden nach {days, plural, one {# Tag} other {# Tagen}} endgÃŧltig gelÃļscht.", "type": "Typ", + "unable_to_change_pin_code": "PIN Code konnte nicht geändert werden", + "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", "unarchived_count": "{count, plural, other {# entarchiviert}}", "unfavorite": "Entfavorisieren", @@ -1814,6 +1841,7 @@ "untracked_files": "Unverfolgte Dateien", "untracked_files_decription": "Diese Dateien werden nicht von der Application getrackt. Sie kÃļnnen das Ergebnis fehlgeschlagener Verschiebungen, unterbrochener Uploads oder aufgrund eines Fehlers sein", "up_next": "Weiter", + "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", "upload_concurrency": "Parallelität beim Hochladen", @@ -1826,15 +1854,17 @@ "upload_status_errors": "Fehler", "upload_status_uploaded": "Hochgeladen", "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", - "upload_to_immich": "Zu Immich hochladen ({})", + "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", - "url": "URL", "usage": "Verwendung", + "use_biometric": "Biometrie verwenden", "use_current_connection": "aktuelle Verbindung verwenden", "use_custom_date_range": "Stattdessen einen benutzerdefinierten Datumsbereich verwenden", "user": "Nutzer", + "user_has_been_deleted": "Dieser Benutzer wurde gelÃļscht.", "user_id": "Nutzer-ID", "user_liked": "{type, select, photo {Dieses Foto} video {Dieses Video} asset {Diese Datei} other {Dies}} gefällt {user}", + "user_pin_code_settings_description": "Verwalte deinen PIN Code", "user_purchase_settings": "Kauf", "user_purchase_settings_description": "Kauf verwalten", "user_role_set": "{user} als {role} festlegen", @@ -1847,7 +1877,6 @@ "validate": "Validieren", "validate_endpoint_error": "Bitte gib eine gÃŧltige URL ein", "variables": "Variablen", - "version": "Version", "version_announcement_closing": "Dein Freund, Alex", "version_announcement_message": "Hi! Es gibt eine neue Version von Immich. Bitte nimm dir Zeit, die Versionshinweise zu lesen, um Fehlkonfigurationen zu vermeiden, insbesondere wenn du WatchTower oder ein anderes Verfahren verwendest, das Immich automatisch aktualisiert.", "version_announcement_overlay_release_notes": "Änderungsprotokoll", @@ -1857,11 +1886,8 @@ "version_announcement_overlay_title": "Neue Server-Version verfÃŧgbar 🎉", "version_history": "Versionshistorie", "version_history_item": "{version} am {date} installiert", - "video": "Video", "video_hover_setting": "Videovorschau beim Hovern abspielen", "video_hover_setting_description": "Spiele die Miniaturansicht des Videos ab, wenn sich die Maus Ãŧber dem Element befindet. Auch wenn die Funktion deaktiviert ist, kann die Wiedergabe gestartet werden, indem du mit der Maus Ãŧber das Wiedergabesymbol fährst.", - "videos": "Videos", - "videos_count": "{count, plural, one {# Video} other {# Videos}}", "view": "Ansicht", "view_album": "Album anzeigen", "view_all": "Alles anzeigen", @@ -1884,6 +1910,7 @@ "welcome": "Willkommen", "welcome_to_immich": "Willkommen bei Immich", "wifi_name": "WLAN-Name", + "wrong_pin_code": "PIN Code falsch", "year": "Jahr", "years_ago": "Vor {years, plural, one {einem Jahr} other {# Jahren}}", "yes": "Ja", diff --git a/i18n/el.json b/i18n/el.json index 52efcccd50..613be44e3e 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -14,7 +14,7 @@ "add_a_location": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎ¯ÎąĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "add_a_name": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", "add_a_title": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„Î¯Ī„ÎģÎŋĪ…", - "add_endpoint": "Add endpoint", + "add_endpoint": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„ÎĩÎģΚÎēÎŋĪ ĪƒÎˇÎŧÎĩίÎŋĪ…", "add_exclusion_pattern": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎŋĪ„Î¯Î˛ÎŋĪ… ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ", "add_import_path": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎŋÎŊÎŋĪ€ÎąĪ„ÎšÎŋĪ ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽĪ‚", "add_location": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", @@ -26,6 +26,7 @@ "add_to_album": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΃Îĩ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "add_to_album_bottom_sheet_added": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ĪƒĪ„Îŋ {album}", "add_to_album_bottom_sheet_already_exists": "Ήδη ĪƒĪ„Îŋ {album}", + "add_to_locked_folder": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ„ÎŋÎŊ ΚÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ÎĻÎŦÎēÎĩÎģÎŋ", "add_to_shared_album": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΃Îĩ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "add_url": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "added_to_archive": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ĪƒĪ„Îŋ ÎąĪĪ‡ÎĩίÎŋ", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎŧÎĩÎ¸ĪŒÎ´ÎŋĪ…Ī‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚; Η ĪƒĪÎŊδÎĩĪƒÎˇ θι ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡθÎĩί Ī€ÎģÎŽĪĪ‰Ī‚.", "authentication_settings_reenable": "Για ÎĩĪ€ÎąÎŊÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ, Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎŧÎ¯Îą ΕÎŊĪ„ÎŋÎģÎŽ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ.", "background_task_job": "Î•ĪÎŗÎąĪƒÎ¯ÎĩĪ‚ Î ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", - "backup_database": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ΑÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… Î‘ĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī„ÎˇĪ‚ ΒÎŦĪƒÎˇĪ‚ ΔÎĩδÎŋÎŧέÎŊΉÎŊ", - "backup_database_enable_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", - "backup_keep_last_amount": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΉÎŊ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŗÎšÎą Î´ÎšÎąĪ„ÎŽĪÎˇĪƒÎˇ", - "backup_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΑÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ Î‘ĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎˇĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ΄ΉÎŊ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "backup_database": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą Dump βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "backup_database_enable_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ dumps βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "backup_keep_last_amount": "ΠÎŋĪƒĪŒĪ„ÎˇĪ„Îą ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΉÎŊ dumps Ī€ÎŋĪ… Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Î´ÎšÎąĪ„ÎˇĪÎˇÎ¸ÎŋĪÎŊ", + "backup_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ dump βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "backup_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ dump Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ. ÎŖÎˇÎŧÎĩÎ¯Ī‰ĪƒÎˇ: Î‘Ī…Ī„Î­Ī‚ ÎŋΚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ δÎĩÎŊ Ī€ÎąĪÎąÎēÎŋÎģÎŋĪ…Î¸ÎŋĪÎŊĪ„ÎąÎš ÎēιΚ δÎĩÎŊ θι ÎĩΚδÎŋĪ€ÎŋΚΡθÎĩÎ¯Ī„Îĩ ÎŗÎšÎą ÎąĪ€ÎŋĪ„Ī…Ī‡Î¯Îą.", "check_all": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ΌÎģΉÎŊ", "cleanup": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ", "cleared_jobs": "ΕÎēÎēÎąÎ¸ÎąĪÎ¯ĪƒĪ„ÎˇÎēÎąÎŊ ÎŋΚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎŗÎšÎą: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Για ÎĩĪ€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ, Ī€ÎģΡÎē΄΁ÎŋÎģÎŋÎŗÎŽĪƒĪ„Îĩ \"{email}\" Ī€ÎąĪÎąÎēÎŦ΄Ή", "confirm_reprocess_all_faces": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒĪ„ÎĩÎ¯Ī„Îĩ ΞιÎŊÎŦ ΌÎģÎą Ī„Îą Ī€ĪĪŒĪƒĪ‰Ī€Îą; Î‘Ī…Ī„ĪŒ θι ÎĩÎēÎēÎąÎ¸ÎąĪÎ¯ĪƒÎĩΚ ÎąÎēΌÎŧÎą ÎēιΚ Ī„Îą ÎŦĪ„ÎŋÎŧÎą ĪƒĪ„Îą ÎŋĪ€ÎŋÎ¯Îą Î­Ī‡ÎĩĪ„Îĩ ΎδΡ ÎŋĪÎ¯ĪƒÎĩΚ Ī„Îŋ ΌÎŊÎŋÎŧÎą.", "confirm_user_password_reset": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ {user};", + "confirm_user_pin_code_reset": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ {user};", "create_job": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎĩĪÎŗÎąĪƒÎ¯ÎąĪ‚", "cron_expression": "ÎŖĪÎŊĪ„ÎąÎžÎˇ Cron", "cron_expression_description": "ÎŸĪÎ¯ĪƒĪ„Îĩ Ī„Îŋ δΚÎŦĪƒĪ„ÎˇÎŧÎą ΃ÎŦĪĪ‰ĪƒÎˇĪ‚ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšĪŽÎŊĪ„ÎąĪ‚ Ī„Îˇ ÎŧÎŋĪĪ†ÎŽ cron. Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚, ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ Ī€.·. ĪƒĪ„Îŋ Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎēÎąĪ„ÎąĪ‡ĪŽĪÎˇĪƒÎˇ", "oauth_auto_register_description": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎēÎąĪ„ÎąĪ‡ĪŽĪÎˇĪƒÎˇ ÎŊέÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ÎąĪ†ÎŋĪ ĪƒĪ…ÎŊδÎĩθÎĩί ÎŧÎĩ OAuth", "oauth_button_text": "ΚÎĩίÎŧÎĩÎŊÎŋ ÎēÎŋĪ…ÎŧĪ€ÎšÎŋĪ", - "oauth_client_id": "Î¤ÎąĪ…Ī„ĪŒĪ„ÎˇĪ„Îą Ī€ÎĩÎģÎŦĪ„Îˇ (Client)", - "oauth_client_secret": "ÎœĪ…ĪƒĪ„ÎšÎēĪŒĪ‚ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ÎĩÎģÎŦĪ„Îˇ", + "oauth_client_secret_description": "ÎĨĪ€Îŋ·΁ÎĩĪ‰Ī„ÎšÎēΌ ÎĩÎąÎŊ PKCE (Proof Key for Code Exchange) δÎĩÎŊ Ī…Ī€ÎŋĪƒĪ„ÎˇĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„ÎŋÎŊ OAuth Ī€ÎŦ΁Îŋ·Îŋ", "oauth_enable_description": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ OAuth", - "oauth_issuer_url": "ΔιÎĩĪÎ¸Ī…ÎŊĪƒÎˇ URL ÎĩÎēÎ´ĪŒĪ„Îˇ", "oauth_mobile_redirect_uri": "URI ΑÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", "oauth_mobile_redirect_uri_override": "Î ĪÎŋĪƒĪ€Î­ÎģÎąĪƒÎˇ URI ÎąÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", "oauth_mobile_redirect_uri_override_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪŒĪ„ÎąÎŊ Îŋ Ī€ÎŦ΁Îŋ·ÎŋĪ‚ OAuth δÎĩÎŊ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩΚ ÎŧΚι URI ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ '{callback}'", - "oauth_profile_signing_algorithm": "ΑÎģÎŗĪŒĪÎšÎ¸ÎŧÎŋĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ΀΁ÎŋĪ†Î¯Îģ", - "oauth_profile_signing_algorithm_description": "ΑÎģÎŗĪŒĪÎšÎ¸ÎŧÎŋĪ‚ Ī€ÎŋĪ… Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš ÎŗÎšÎą Ī„ÎˇÎŊ ĪƒĪÎŊδÎĩĪƒÎˇ ΄ΉÎŊ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ.", - "oauth_scope": "Î•ĪĪÎŋĪ‚", "oauth_settings": "OAuth", "oauth_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ OAuth", "oauth_settings_more_details": "Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ ĪƒĪ‡ÎĩĪ„ÎšÎēÎŦ ÎŧÎĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą, ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ ĪƒĪ„ÎˇÎŊ Ī„ÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇ.", - "oauth_signing_algorithm": "ΑÎģÎŗĪŒĪÎšÎ¸ÎŧÎŋĪ‚ Ī…Ī€ÎŋÎŗĪÎąĪ†ÎŽĪ‚", "oauth_storage_label_claim": "ΔήÎģĪ‰ĪƒÎˇ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "oauth_storage_label_claim_description": "ÎŸĪÎ¯ÎļÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„ÎˇÎŊ ÎĩĪ„ÎšÎēÎ­Ī„Îą ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ĪƒĪ„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ.", "oauth_storage_quota_claim": "ΔήÎģĪ‰ĪƒÎˇ Ī€Îŋ΃ÎŋĪƒĪ„ÎŋĪ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "oauth_storage_quota_claim_description": "ÎŸĪÎ¯ÎļÎĩΚ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„Îŋ Ī€Îŋ΃ÎŋĪƒĪ„ĪŒ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ĪƒĪ„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ.", "oauth_storage_quota_default": "Î ĪÎŋÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋ ĪŒĪÎšÎŋ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ (GiB)", "oauth_storage_quota_default_description": "ΠÎŋ΃ÎŋĪƒĪ„ĪŒ ΃Îĩ GiB Ī€ÎŋĪ… θι Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚΡθÎĩί ĪŒĪ„ÎąÎŊ δÎĩÎŊ ÎŋĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„Îˇ δΡÎģΉÎŧέÎŊΡ Ī„ÎšÎŧÎŽ (Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ 0 ÎŗÎšÎą ÎąĪ€ÎĩĪÎšĪŒĪÎšĪƒĪ„Îŋ Ī€Îŋ΃ÎŋĪƒĪ„ĪŒ).", + "oauth_timeout": "Î§ĪÎŋÎŊΚÎēΌ ĪŒĪÎšÎŋ Î‘ÎšĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚", + "oauth_timeout_description": "Î§ĪÎŋÎŊΚÎēΌ ĪŒĪÎšÎŋ Î‘ÎšĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚ ΃Îĩ milliseconds", "offline_paths": "Î”ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ ÎĩÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", "offline_paths_description": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎŋΆÎĩίÎģÎŋÎŊĪ„ÎąÎš ΃Îĩ ·ÎĩÎšĪÎŋÎēίÎŊÎˇĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ Ī€ÎŋĪ… δÎĩÎŊ ÎąÎŊÎŽÎēÎŋĪ…ÎŊ ΃Îĩ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽ βΚβÎģΚÎŋθΎÎēΡ.", "password_enable_description": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ ΡÎģÎĩÎē΄΁ÎŋÎŊΚÎēΌ Ī„ÎąĪ‡Ī…Î´ĪÎŋÎŧÎĩίÎŋ", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ΡÎŧÎĩĪĪŽÎŊ ÎŧÎĩĪ„ÎŦ Ī„ÎˇÎŊ ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇ, ÎŗÎšÎą Ī„ÎˇÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŽ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ Ī„ÎŋĪ… ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ ÎēιΚ ΄ΉÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ ÎĩÎŊĪŒĪ‚ Ī‡ĪÎŽĪƒĪ„Îˇ. Η ÎĩĪÎŗÎąĪƒÎ¯Îą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ ÎĩÎēĪ„ÎĩÎģÎĩÎ¯Ī„ÎąÎš Ī„Îą ÎŧÎĩ΃ÎŦÎŊĪ…Ī‡Ī„Îą, ÎŗÎšÎą ÎŊÎą ÎĩÎģÎ­ÎŗÎžÎĩΚ Ī€ÎŋΚÎŋΚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎĩίÎŊιΚ Î­Ī„ÎŋΚÎŧÎŋΚ ÎŗÎšÎą Î´ÎšÎąÎŗĪÎąĪ†ÎŽ. Οι ÎąÎģÎģÎąÎŗÎ­Ī‚ ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ θι ιΞΚÎŋÎģÎŋÎŗÎˇÎ¸ÎŋĪÎŊ ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩĪ€ĪŒÎŧÎĩÎŊΡ ÎĩÎēĪ„Î­ÎģÎĩĪƒÎˇ.", "user_delete_immediately": "Ο ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧĪŒĪ‚ ÎēιΚ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą Ī„ÎŋĪ…/Ī„ÎˇĪ‚ {user} θι ÎŧĪ€ÎŋĪ…ÎŊ ĪƒĪ„ÎˇÎŊ Îŋ΅΁ÎŦ ÎŗÎšÎą ÎŋĪÎšĪƒĪ„ÎšÎēÎŽ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ, ÎŦÎŧÎĩĪƒÎą.", "user_delete_immediately_checkbox": "ΒÎŦÎģÎĩ Ī„ÎŋÎŊ Ī‡ĪÎŽĪƒĪ„Îˇ ÎēιΚ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą Ī„ÎŋĪ… ĪƒĪ„ÎˇÎŊ Îŋ΅΁ÎŦ ÎŗÎšÎą ÎŦÎŧÎĩĪƒÎˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ", + "user_details": "ΛÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ Ī‡ĪÎŽĪƒĪ„Îˇ", "user_management": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ", "user_password_has_been_reset": "Ο ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ Î­Ī‡ÎĩΚ ÎĩĪ€ÎąÎŊÎąĪĪ…Î¸ÎŧÎšĪƒĪ„Îĩί:", "user_password_reset_description": "Î ÎąĪÎąÎēÎąÎģĪŽ Ī€ÎąĪÎ­Ī‡ÎĩĪ„Îĩ Ī„ÎŋÎŊ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊΌ ÎēĪ‰Î´ÎšÎēΌ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎŊ Ī‡ĪÎŽĪƒĪ„Îˇ ÎēιΚ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎŋÎŊ ĪŒĪ„Îš θι Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Ī„ÎŋÎŊ ÎąÎģÎģÎŦΞÎĩΚ, ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩĪ€ĪŒÎŧÎĩÎŊΡ ĪƒĪÎŊδÎĩĪƒÎŽ Ī„ÎŋĪ….", @@ -371,13 +370,17 @@ "admin_password": "ÎšĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ", "administration": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ", "advanced": "Για ΀΁ÎŋĪ‡Ī‰ĪÎˇÎŧέÎŊÎŋĪ…Ī‚", - "advanced_settings_log_level_title": "Î•Ī€Î¯Ī€ÎĩδÎŋ ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ ÎŗÎšÎą ÎŊÎą Ī†ÎšÎģ΄΁ÎŦ΁ÎĩĪ„Îĩ Ī„Îą ÎŧÎ­ĪƒÎą ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇĪ‚ ÎēÎąĪ„ÎŦ Ī„ÎŋÎŊ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧΌ ÎŧÎĩ βÎŦĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēÎŦ ÎēĪÎšĪ„ÎŽĪÎšÎą. ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą ÎŧΌÎŊÎŋ ÎąÎŊ Î­Ī‡ÎĩĪ„Îĩ ΀΁ÎŋβÎģÎŽÎŧÎąĪ„Îą ÎŧÎĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ Ī€ÎŋĪ… ÎĩÎŊĪ„ÎŋĪ€Î¯ÎļÎĩΚ ΌÎģÎą Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", + "advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Î§ĪÎŽĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēÎŋĪ Ī†Î¯Îģ΄΁ÎŋĪ… ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚", + "advanced_settings_log_level_title": "Î•Ī€Î¯Ī€ÎĩδÎŋ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚: {level}", "advanced_settings_prefer_remote_subtitle": "ΜÎĩĪÎšÎēÎ­Ī‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ÎąĪÎŗÎŋĪÎŊ Ī€ÎŋÎģĪ ÎŊÎą ΆÎŋĪĪ„ĪŽĪƒÎŋĪ…ÎŊ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎąĪ€ĪŒ ÎąĪĪ‡ÎĩÎ¯Îą ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ. ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ ÎŗÎšÎą ÎŊÎą ΆÎŋĪĪ„ĪŽÎŊÎŋÎŊĪ„ÎąÎš ÎąÎŊĪ„Î¯ ÎąĪ…Ī„ÎŋĪ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎĩĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚.", - "advanced_settings_prefer_remote_title": "Î ĪÎŋĪ„Î¯ÎŧÎˇĪƒÎˇ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊΉÎŊ ÎĩΚÎēΌÎŊΉÎŊ.", + "advanced_settings_prefer_remote_title": "Î ĪÎŋĪ„Î¯ÎŧÎˇĪƒÎˇ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊΉÎŊ ÎĩΚÎēΌÎŊΉÎŊ", "advanced_settings_proxy_headers_subtitle": "ΚαθÎŋĪÎšĪƒÎŧĪŒĪ‚ ÎēÎĩĪ†ÎąÎģÎ¯Î´Ī‰ÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎĩ΃ÎŋÎģÎŦÎ˛ÎˇĪƒÎˇĪ‚ Ī€ÎŋĪ… Ī„Îŋ Immich Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą ĪƒĪ„Î­ÎģÎŊÎĩΚ ÎŧÎĩ ÎēÎŦθÎĩ ÎąÎ¯Ī„ÎˇÎŧÎą δΚÎēĪ„ĪÎŋĪ…", "advanced_settings_proxy_headers_title": "ΚÎĩĪ†ÎąÎģίδÎĩĪ‚ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎĩ΃ÎŋÎģÎŦÎ˛ÎˇĪƒÎˇĪ‚", "advanced_settings_self_signed_ssl_subtitle": "Î ÎąĪÎąÎēÎŦÎŧ΀΄ÎĩΚ Ī„ÎŋÎŊ έÎģÎĩÎŗĪ‡Îŋ Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŋĪ SSL Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ. Î‘Ī€ÎąĪÎąÎ¯Ī„ÎˇĪ„Îŋ ÎŗÎšÎą ÎąĪ…Ī„Îŋ-Ī…Ī€ÎŋÎŗÎĩÎŗĪÎąÎŧÎŧέÎŊÎą Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŦ.", "advanced_settings_self_signed_ssl_title": "Να ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎŋÎŊĪ„ÎąÎš ÎąĪ…Ī„Îŋ-Ī…Ī€ÎŋÎŗÎĩÎŗĪÎąÎŧÎŧέÎŊÎą Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŦ SSL", + "advanced_settings_sync_remote_deletions_subtitle": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎŽ ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎĩÎŊĪŒĪ‚ Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŋĪ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ, ĪŒĪ„ÎąÎŊ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι ÎąĪ…Ī„ÎŽ Ī€ĪÎąÎŗÎŧÎąĪ„ÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš ĪƒĪ„Îŋ Î´ÎšÎąÎ´Î¯Îē΄΅Îŋ", + "advanced_settings_sync_remote_deletions_title": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊΉÎŊ Î´ÎšÎąÎŗĪÎąĪ†ĪŽÎŊ [ΠΕΙΡΑΜΑΤΙΚΟ]", "advanced_settings_tile_subtitle": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΀΁ÎŋĪ‡Ī‰ĪÎˇÎŧέÎŊÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", "advanced_settings_troubleshooting_subtitle": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ Ī€ĪĪŒĪƒÎ¸Îĩ΄ΉÎŊ Ī‡ÎąĪÎąÎēĪ„ÎˇĪÎšĪƒĪ„ÎšÎēĪŽÎŊ ÎŗÎšÎą ÎąÎŊĪ„ÎšÎŧÎĩĪ„ĪŽĪ€ÎšĪƒÎˇ ΀΁ÎŋβÎģΡÎŧÎŦ΄ΉÎŊ", "advanced_settings_troubleshooting_title": "ΑÎŊĪ„ÎšÎŧÎĩĪ„ĪŽĪ€ÎšĪƒÎˇ ΀΁ÎŋβÎģΡÎŧÎŦ΄ΉÎŊ", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ Ī„ÎŋÎŊ/Ī„ÎˇÎŊ {user};", "album_share_no_users": "ÎĻÎąÎ¯ÎŊÎĩĪ„ÎąÎš ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩΚ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ΃Îĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŽ δÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŗÎšÎą ÎŊÎą Ī„Îŋ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ.", "album_thumbnail_card_item": "1 ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎŋ", - "album_thumbnail_card_items": "{} ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą", - "album_thumbnail_card_shared": "¡ ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ", - "album_thumbnail_shared_by": "ΚÎŋΚÎŊÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ ÎąĪ€ĪŒ {}", + "album_thumbnail_card_items": "{count} ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą", + "album_thumbnail_card_shared": " ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ", + "album_thumbnail_shared_by": "ΚÎŋΚÎŊÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ ÎąĪ€ĪŒ {user}", "album_updated": "ΤÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ, ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "album_updated_setting_description": "ΛÎŦβÎĩĪ„Îĩ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŧÎ­ĪƒĪ‰ email ĪŒĪ„ÎąÎŊ έÎŊÎą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Î­Ī‡ÎĩΚ ÎŊέι ÎąĪĪ‡ÎĩÎ¯Îą", "album_user_left": "Î‘Ī€ÎŋĪ‡Ī‰ĪÎŽĪƒÎąĪ„Îĩ ÎąĪ€ĪŒ Ī„Îŋ {album}", @@ -440,7 +443,7 @@ "archive": "Î‘ĪĪ‡ÎĩίÎŋ", "archive_or_unarchive_photo": "Î‘ĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ÎŽ ÎąĪ€ÎŋÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚", "archive_page_no_archived_assets": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎˇÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", - "archive_page_title": "Î‘ĪĪ‡ÎĩίÎŋ ({})", + "archive_page_title": "Î‘ĪĪ‡ÎĩίÎŋ ({count})", "archive_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ Î‘ĪĪ‡ÎĩίÎŋĪ…", "archive_size_description": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒĪ„Îĩ Ī„Îŋ ÎŧÎ­ÎŗÎĩθÎŋĪ‚ Ī„ÎŋĪ… ÎąĪĪ‡ÎĩίÎŋĪ… ÎŗÎšÎą ÎģÎŽĪˆÎĩÎšĪ‚ (΃Îĩ GiB)", "archived": "Î‘ĪĪ‡ÎĩίÎŋ", @@ -470,47 +473,47 @@ "asset_skipped_in_trash": "ÎŖĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "asset_uploaded": "ΑÎŊÎĩβÎŦĪƒĪ„ÎˇÎēÎĩ", "asset_uploading": "ΑÎŊÎĩβÎŦÎļÎĩĪ„ÎąÎšâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", + "asset_viewer_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ΀΁ÎŋβÎŋÎģÎŽĪ‚ ĪƒĪ…ÎģÎģÎŋÎŗÎŽĪ‚", "asset_viewer_settings_title": "Î ĪÎŋβÎŋÎģÎŽ ÎŖĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "assets": "ΑÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą", "assets_added_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", "assets_added_to_album_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "assets_added_to_name_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ĪƒĪ„Îŋ {hasName, select, true {{name}} other {ÎŊέÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ}}", "assets_count": "{count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", - "assets_deleted_permanently": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ(Îą) Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", - "assets_deleted_permanently_from_server": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ(Îą) Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", - "assets_moved_to_trash_count": "ΜÎĩĪ„ÎąÎēΚÎŊΎθΡÎēÎĩ/ÎēÎąÎŊ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", - "assets_permanently_deleted_count": "Î”ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ/ÎēÎąÎŊ ÎŧΌÎŊΚÎŧÎą {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", + "assets_deleted_permanently": "{count} Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", + "assets_deleted_permanently_from_server": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", + "assets_moved_to_trash_count": "ΜÎĩĪ„ÎąÎēΚÎŊΎθΡÎēÎąÎŊ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", + "assets_permanently_deleted_count": "Î”ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŧΌÎŊΚÎŧÎą {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", "assets_removed_count": "Î‘Ī†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", - "assets_removed_permanently_from_device": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎēÎąĪ„ÎąĪÎŗÎŽÎ¸ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ĪƒÎąĪ‚", + "assets_removed_permanently_from_device": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎēÎąĪ„ÎąĪÎŗÎŽÎ¸ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ĪƒÎąĪ‚", "assets_restore_confirmation": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩĪ„Îĩ ΌÎģÎą Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î˛ĪÎ¯ĪƒÎēÎŋÎŊĪ„ÎąÎš ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ; Î‘Ī…Ī„ÎŽ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎŊÎąÎšĪÎĩθÎĩί! ΛÎŦβÎĩĪ„Îĩ Ī…Ī€ĪŒĪˆÎˇ ĪŒĪ„Îš δÎĩÎŊ θι ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ÎĩÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚.", "assets_restored_count": "ÎˆÎŗÎšÎŊÎĩ ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ}}", - "assets_restored_successfully": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎąĪ€ÎŋÎēÎąĪ„ÎąĪƒĪ„ÎŦθΡÎēÎąÎŊ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", - "assets_trashed": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†Î­ĪÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", + "assets_restored_successfully": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎąĪ€ÎŋÎēÎąĪ„ÎąĪƒĪ„ÎŦθΡÎēÎąÎŊ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", + "assets_trashed": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†Î­ĪÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "assets_trashed_count": "ΜÎĩĪ„ÎąÎēΚÎŊ. ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŦ΄ΉÎŊ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", - "assets_trashed_from_server": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†Î­ĪÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ÎąĪ€ĪŒ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", + "assets_trashed_from_server": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†Î­ĪÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ÎąĪ€ĪŒ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", "assets_were_part_of_album_count": "{count, plural, one {ΤÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎąÎŊÎŽÎēÎĩΚ} other {Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎąÎŊÎŽÎēÎŋĪ…ÎŊ}} ΎδΡ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "authorized_devices": "ΕξÎŋĪ…ĪƒÎšÎŋδÎŋĪ„ÎˇÎŧέÎŊÎĩĪ‚ ÎŖĪ…ĪƒÎēÎĩĪ…Î­Ī‚", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "ÎŖĪÎŊδÎĩĪƒÎˇ Ī„ÎŋĪ€ÎšÎēÎŦ ÎŧÎ­ĪƒĪ‰ Ī„ÎŋĪ… ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋĪ… Wi-Fi ĪŒĪ„ÎąÎŊ ÎĩίÎŊιΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ ÎēιΚ Ī‡ĪÎŽĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēĪŽÎŊ ĪƒĪ…ÎŊÎ´Î­ĪƒÎĩΉÎŊ ÎąÎģÎģÎŋĪ", + "automatic_endpoint_switching_title": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽ URL", "back": "Î Î¯ĪƒĪ‰", "back_close_deselect": "Î Î¯ĪƒĪ‰, ÎēÎģÎĩÎ¯ĪƒÎšÎŧÎŋ ÎŽ ÎąĪ€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ({})", + "background_location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ", + "background_location_permission_content": "ΤÎŋ Immich ÎŗÎšÎą ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎģÎģÎŦÎļÎĩΚ δίÎēĪ„Ī…Îą ĪŒĪ„ÎąÎŊ Ī„ĪÎ­Ī‡ÎĩΚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ, Ī€ĪÎ­Ī€ÎĩΚ *Ī€ÎŦÎŊĪ„Îą* ÎŊÎą Î­Ī‡ÎĩΚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ĪƒĪ„ÎˇÎŊ ÎąÎēĪÎšÎ˛ÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą ĪŽĪƒĪ„Îĩ Ρ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą δΚιβÎŦÎļÎĩΚ Ī„Îŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… δΚÎēĪ„ĪÎŋĪ… Wi-Fi", + "backup_album_selection_page_albums_device": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ({count})", "backup_album_selection_page_albums_tap": "ΠÎŦĪ„ÎˇÎŧÎą ÎŗÎšÎą ĪƒĪ…ÎŧĪ€ÎĩĪÎ¯ÎģÎˇĪˆÎˇ, Î´ÎšĪ€ÎģΌ Ī€ÎŦĪ„ÎˇÎŧÎą ÎŗÎšÎą ÎĩÎžÎąÎ¯ĪÎĩĪƒÎˇ", - "backup_album_selection_page_assets_scatter": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪƒÎēÎŋĪĪ€ÎšĪƒĪ„ÎŋĪÎŊ ΃Îĩ Ī€ÎŋÎģÎģÎŦ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ. ÎˆĪ„ĪƒÎš, Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą Ī€ÎĩĪÎšÎģÎˇĪ†Î¸ÎŋĪÎŊ ÎŽ ÎŊÎą ÎĩÎžÎąÎšĪÎĩθÎŋĪÎŊ ÎēÎąĪ„ÎŦ Ī„Îˇ δΚιδΚÎēÎąĪƒÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚.", + "backup_album_selection_page_assets_scatter": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪƒÎēÎŋĪĪ€ÎšĪƒĪ„ÎŋĪÎŊ ΃Îĩ Ī€ÎŋÎģÎģÎŦ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ. ÎˆĪ„ĪƒÎš, Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą Ī€ÎĩĪÎšÎģÎˇĪ†Î¸ÎŋĪÎŊ ÎŽ ÎŊÎą ÎĩÎžÎąÎšĪÎĩθÎŋĪÎŊ ÎēÎąĪ„ÎŦ Ī„Îˇ δΚιδΚÎēÎąĪƒÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚.", "backup_album_selection_page_select_albums": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "backup_album_selection_page_selection_info": "ΠÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚", "backup_album_selection_page_total_assets": "ÎŖĪ…ÎŊÎŋÎģΚÎēÎŦ ÎŧÎŋÎŊιδΚÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "backup_all": "ΌÎģÎą", - "backup_background_service_backup_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ...", - "backup_background_service_connection_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ÎŧÎĩ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ...", - "backup_background_service_current_upload_notification": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ {}", - "backup_background_service_default_notification": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ÎŗÎšÎą ÎŊέι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą...", + "backup_background_service_backup_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇâ€Ļ", + "backup_background_service_connection_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ÎŧÎĩ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇâ€Ļ", + "backup_background_service_current_upload_notification": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ {filename}", + "backup_background_service_default_notification": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ÎŗÎšÎą ÎŊέι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îąâ€Ļ", "backup_background_service_error_title": "ÎŖĪ†ÎŦÎģÎŧÎą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_background_service_in_progress_notification": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒÎąĪ‚...", - "backup_background_service_upload_failure_notification": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ {}", + "backup_background_service_in_progress_notification": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒÎąĪ‚â€Ļ", + "backup_background_service_upload_failure_notification": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ {filename}", "backup_controller_page_albums": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "backup_controller_page_background_app_refresh_disabled_content": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎąÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ĪƒĪ„ÎšĪ‚ ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ > ΓÎĩÎŊΚÎēÎŦ > ΑÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ Î•Ī†ÎąĪÎŧÎŋÎŗÎŽĪ‚ ĪƒĪ„Îŋ Î ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎŗÎšÎą ÎŊÎą Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ Ī„ÎˇÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ.", "backup_controller_page_background_app_refresh_disabled_title": "Η ÎąÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ", @@ -521,35 +524,34 @@ "backup_controller_page_background_battery_info_title": "ΒÎĩÎģĪ„ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚ ÎŧĪ€ÎąĪ„ÎąĪÎ¯ÎąĪ‚", "backup_controller_page_background_charging": "ÎœĪŒÎŊÎŋ ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„ÎšĪƒÎˇ", "backup_controller_page_background_configure_error": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪĪÎ¸ÎŧÎšĪƒÎˇĪ‚ Ī„ÎˇĪ‚ Ī…Ī€ÎˇĪÎĩĪƒÎ¯ÎąĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", - "backup_controller_page_background_delay": "ÎšÎąÎ¸Ī…ĪƒĪ„Î­ĪÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŊÎ­Ī‰ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ: {}", + "backup_controller_page_background_delay": "ÎšÎąÎ¸Ī…ĪƒĪ„Î­ĪÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŊÎ­Ī‰ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ: {duration}", "backup_controller_page_background_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎˇÎŊ Ī…Ī€ÎˇĪÎĩĪƒÎ¯Îą Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ… ÎŗÎšÎą ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŊÎ­Ī‰ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ Ī‡Ī‰ĪÎ¯Ī‚ ÎŊÎą ·΁ÎĩΚÎŦÎļÎĩĪ„ÎąÎš ÎŊÎą ÎąÎŊÎŋίΞÎĩĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ", "backup_controller_page_background_is_off": "Η ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ", "backup_controller_page_background_is_on": "Η ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ", "backup_controller_page_background_turn_off": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ Ī…Ī€ÎˇĪÎĩĪƒÎ¯ÎąĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", "backup_controller_page_background_turn_on": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ Ī…Ī€ÎˇĪÎĩĪƒÎ¯ÎąĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", - "backup_controller_page_background_wifi": "ÎœĪŒÎŊÎŋ ΃Îĩ ĪƒĪÎŊδÎĩĪƒÎˇ WiFi", + "backup_controller_page_background_wifi": "ÎœĪŒÎŊÎŋ ΃Îĩ ĪƒĪÎŊδÎĩĪƒÎˇ Wi-Fi", "backup_controller_page_backup": "ΑÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_controller_page_backup_selected": "Î•Ī€ÎšÎģÎĩÎŗÎŧέÎŊÎą:", + "backup_controller_page_backup_selected": "Î•Ī€ÎšÎģÎĩÎŗÎŧέÎŊÎą: ", "backup_controller_page_backup_sub": "ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŗÎšÎą Ī„Îą ÎŋĪ€ÎŋÎ¯Îą Î­Ī‡ÎŋĪ…ÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎˇÎ¸Îĩί ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_controller_page_created": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ ĪƒĪ„ÎšĪ‚: {}", + "backup_controller_page_created": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ ĪƒĪ„ÎšĪ‚: {date}", "backup_controller_page_desc_backup": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎˇÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ ÎŗÎšÎą ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎŊÎ­Ī‰ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪŒĪ„ÎąÎŊ ÎąÎŊÎŋÎ¯ÎŗÎĩĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ.", - "backup_controller_page_excluded": "Î•ÎžÎąÎšĪÎŋĪÎŧÎĩÎŊÎą:", - "backup_controller_page_failed": "Î‘Ī€ÎŋĪ„Ī…Ī‡ÎˇÎŧέÎŊÎą ({})", - "backup_controller_page_filename": "ΌÎŊÎŋÎŧÎą ÎąĪĪ‡ÎĩίÎŋĪ…: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Î•ÎžÎąÎšĪÎŋĪÎŧÎĩÎŊÎą: ", + "backup_controller_page_failed": "Î‘Ī€ÎŋĪ„Ī…Ī‡ÎˇÎŧέÎŊÎą ({count})", + "backup_controller_page_filename": "ΌÎŊÎŋÎŧÎą ÎąĪĪ‡ÎĩίÎŋĪ…: {filename} [{size}]", "backup_controller_page_info": "ΠÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_controller_page_none_selected": "ΚαÎŊέÎŊÎą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋ", "backup_controller_page_remainder": "ÎĨĪ€ĪŒÎģÎŋÎšĪ€Îŋ", "backup_controller_page_remainder_sub": "ÎĨĪ€ĪŒÎģÎŋÎšĪ€ÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŗÎšÎą ÎąÎŊĪ„ÎšÎŗĪÎąĪ†ÎŽ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ", "backup_controller_page_server_storage": "Î§Ī‰ĪÎˇĪ„ÎšÎēĪŒĪ„ÎˇĪ„Îą ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "backup_controller_page_start_backup": "ΈÎŊÎąĪÎžÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_controller_page_status_off": "Η ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ\n", + "backup_controller_page_status_off": "Η ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ, ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ", "backup_controller_page_status_on": "Η ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ", - "backup_controller_page_storage_format": "{} ÎąĪ€ĪŒ {} ΃Îĩ Ī‡ĪÎŽĪƒÎˇ", + "backup_controller_page_storage_format": "{used} ÎąĪ€ĪŒ {total} ΃Îĩ Ī‡ĪÎŽĪƒÎˇ", "backup_controller_page_to_backup": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ ÎŗÎšÎą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_controller_page_total_sub": "ΌÎģÎĩĪ‚ ÎŋΚ ÎŧÎŋÎŊιδΚÎēÎ­Ī‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎąĪ€ĪŒ Ī„Îą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ\n", - "backup_controller_page_turn_off": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ\n", - "backup_controller_page_turn_on": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ\n", + "backup_controller_page_total_sub": "ΌÎģÎĩĪ‚ ÎŋΚ ÎŧÎŋÎŊιδΚÎēÎ­Ī‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎąĪ€ĪŒ Ī„Îą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "backup_controller_page_turn_off": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ", + "backup_controller_page_turn_on": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ", "backup_controller_page_uploading_file_info": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī€ÎģÎˇĪÎŋΆÎŋĪÎšĪŽÎŊ ÎąĪĪ‡ÎĩίÎŋĪ…", "backup_err_only_album": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇ Ī„ÎŋĪ… ÎŧÎŋÎŊιδΚÎēÎŋĪ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "backup_info_card_assets": "ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", @@ -558,8 +560,12 @@ "backup_manual_success": "Î•Ī€ÎšĪ„Ī…Ī‡Î¯Îą", "backup_manual_title": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚", "backup_options_page_title": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_setting_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎēιΚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ", "backward": "Î ĪÎŋĪ‚ Ī„Îą Ī€Î¯ĪƒĪ‰", + "biometric_auth_enabled": "ΒιÎŋÎŧÎĩĪ„ĪÎšÎēÎŽ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ", + "biometric_locked_out": "Î•Î¯ĪƒĪ„Îĩ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋΚ ÎĩÎēĪ„ĪŒĪ‚ Ī„ÎˇĪ‚ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽĪ‚ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", + "biometric_no_options": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋΚ Ī„ĪĪŒĪ€ÎŋΚ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽĪ‚ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", + "biometric_not_available": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎĩΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", "birthdate_saved": "Η ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŗÎ­ÎŊÎŊÎˇĪƒÎˇĪ‚ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡ĪŽĪ‚", "birthdate_set_description": "Η ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŗÎ­ÎŊÎŊÎˇĪƒÎˇĪ‚ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš ÎŗÎšÎą Ī„ÎŋÎŊ Ī…Ī€ÎŋÎģÎŋÎŗÎšĪƒÎŧΌ Ī„ÎˇĪ‚ ΡÎģΚÎēÎ¯ÎąĪ‚ ÎąĪ…Ī„ÎŋĪ Ī„ÎŋĪ… ÎąĪ„ĪŒÎŧÎŋĪ…, Ī„Îˇ ·΁ÎŋÎŊΚÎēÎŽ ĪƒĪ„ÎšÎŗÎŧÎŽ ÎŧÎšÎąĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚.", "blurred_background": "ΘÎŋÎģΌ Ī†ĪŒÎŊĪ„Îŋ", @@ -570,21 +576,21 @@ "bulk_keep_duplicates_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎēĪÎąĪ„ÎŽĪƒÎĩĪ„Îĩ {count, plural, one {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îŋ ÎąĪĪ‡ÎĩίÎŋ} other {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ÎąĪĪ‡ÎĩÎ¯Îą}}; Î‘Ī…Ī„ĪŒ θι ÎĩĪ€ÎšÎģĪĪƒÎĩΚ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎŋÎŧÎŦδÎĩĪ‚ Î´ÎšĪ€ÎģÎŋĪ„ĪĪ€Ī‰ÎŊ Ī‡Ī‰ĪÎ¯Ī‚ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩΚ Ī„Î¯Ī€ÎŋĪ„Îą.", "bulk_trash_duplicates_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą βÎŦÎģÎĩĪ„Îĩ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ {count, plural, one {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îŋ ÎąĪĪ‡ÎĩίÎŋ} other {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ÎąĪĪ‡ÎĩÎ¯Îą}}; Î‘Ī…Ī„ĪŒ θι ÎēĪÎąĪ„ÎŽĪƒÎĩΚ Ī„Îŋ ÎŧÎĩÎŗÎąÎģĪĪ„Îĩ΁Îŋ ÎąĪĪ‡ÎĩίÎŋ ÎąĪ€ĪŒ ÎēÎŦθÎĩ ÎŋÎŧÎŦδι ÎēιΚ θι βÎŦÎģÎĩΚ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ΌÎģÎą Ī„Îą ÎŦÎģÎģÎą Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą.", "buy": "Î‘ÎŗÎŋ΁ÎŦĪƒĪ„Îĩ Ī„Îŋ Immich", - "cache_settings_album_thumbnails": "ΜιÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ΃ÎĩÎģÎ¯Î´ÎąĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚ ({} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", + "cache_settings_album_thumbnails": "ΜιÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ΃ÎĩÎģÎ¯Î´ÎąĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚ ({count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", "cache_settings_clear_cache_button": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚", "cache_settings_clear_cache_button_title": "ÎšÎąÎ¸ÎąĪÎ¯ÎļÎĩΚ Ī„Îˇ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽ ÎŧÎŊÎŽÎŧΡ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚. Î‘Ī…Ī„ĪŒ θι ÎĩĪ€ÎˇĪÎĩÎŦ΃ÎĩΚ ĪƒÎˇÎŧÎąÎŊĪ„ÎšÎēÎŦ Ī„ÎˇÎŊ ÎąĪ€ĪŒÎ´ÎŋĪƒÎˇ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚ ÎŧÎ­Ī‡ĪÎš ÎŊÎą ÎąÎŊιδΡÎŧΚÎŋĪ…ĪÎŗÎˇÎ¸Îĩί Ρ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽ ÎŧÎŊÎŽÎŧΡ.", "cache_settings_duplicated_assets_clear_button": "Î•ÎšÎšÎ‘Î˜Î‘ÎĄÎ™ÎŖÎ—", "cache_settings_duplicated_assets_subtitle": "ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎŧĪ€ÎĩΚ ĪƒĪ„Îˇ ÎŧÎąĪĪÎˇ ÎģÎ¯ĪƒĪ„Îą ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ", - "cache_settings_duplicated_assets_title": "Î”ÎšĪ€ÎģÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ({})", - "cache_settings_image_cache_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎĩΚÎēΌÎŊΉÎŊ ({} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", + "cache_settings_duplicated_assets_title": "Î”ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ({count})", + "cache_settings_image_cache_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎĩΚÎēΌÎŊΉÎŊ ({count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", "cache_settings_statistics_album": "ΜιÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ βΚβÎģΚÎŋθΎÎēÎˇĪ‚", - "cache_settings_statistics_assets": "{} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ({})", + "cache_settings_statistics_assets": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ({size})", "cache_settings_statistics_full": "ΠÎģÎŽĪÎĩÎšĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚", "cache_settings_statistics_shared": "ΜιÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēÎŋΚÎŊÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "cache_settings_statistics_thumbnail": "ΜιÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", "cache_settings_statistics_title": "Î§ĪÎŽĪƒÎˇ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚", "cache_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎˇĪƒÎˇ ĪƒĪ…ÎŧĪ€ÎĩĪÎšĪ†Îŋ΁ÎŦĪ‚ Ī„ÎˇĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚", - "cache_settings_thumbnail_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ({} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", + "cache_settings_thumbnail_size": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ΀΁ÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ÎŧÎŊÎŽÎŧÎˇĪ‚ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ({count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą)", "cache_settings_tile_subtitle": "ΧÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îˇ ĪƒĪ…ÎŧĪ€ÎĩĪÎšĪ†Îŋ΁ÎŦ Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎšÎēÎŽĪ‚ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚", "cache_settings_tile_title": "ΤÎŋĪ€ÎšÎēÎŽ Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇ", "cache_settings_title": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ Î ĪÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ΜÎŊÎŽÎŧÎˇĪ‚", @@ -593,12 +599,14 @@ "camera_model": "ΜÎŋÎŊĪ„Î­ÎģÎŋ ÎēÎŦÎŧÎĩĪÎąĪ‚", "cancel": "ΑÎēĪĪĪ‰ĪƒÎˇ", "cancel_search": "ΑÎēĪĪĪ‰ĪƒÎˇ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", - "canceled": "Canceled", + "canceled": "ΑÎē΅΁ΉÎŧέÎŊÎŋ", "cannot_merge_people": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ĪƒĪ…ÎŗĪ‡ĪŽÎŊÎĩĪ…ĪƒÎˇ ÎąĪ„ĪŒÎŧΉÎŊ", "cannot_undo_this_action": "ΔÎĩÎŊ ÎŧĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩÎŊÎ­ĪÎŗÎĩΚι!", "cannot_update_the_description": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", + "cast": "Î ĪÎŋβÎŋÎģÎŽ", "change_date": "ΑÎģÎģÎąÎŗÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", - "change_display_order": "Change display order", + "change_description": "ΑÎģÎģÎąÎŗÎŽ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", + "change_display_order": "ΑÎģÎģÎąÎŗÎŽ ΃ÎĩÎšĪÎŦĪ‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇĪ‚", "change_expiration_time": "ΑÎģÎģÎąÎŗÎŽ Ī‡ĪĪŒÎŊÎŋĪ… ÎģÎŽÎžÎˇĪ‚", "change_location": "ΑÎģÎģÎąÎŗÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "change_name": "ΑÎģÎģÎąÎŗÎŽ ÎŋÎŊÎŋÎŧÎąĪƒÎ¯ÎąĪ‚", @@ -610,12 +618,13 @@ "change_password_form_new_password": "ΝέÎŋĪ‚ ÎšĪ‰Î´ÎšÎēĪŒĪ‚", "change_password_form_password_mismatch": "Οι ÎēĪ‰Î´ÎšÎēÎŋί δÎĩÎŊ Ī„ÎąÎšĪÎšÎŦÎļÎŋĪ…ÎŊ", "change_password_form_reenter_new_password": "Î•Ī€ÎąÎŊÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽ ΝέÎŋĪ… ÎšĪ‰Î´ÎšÎēÎŋĪ", + "change_pin_code": "ΑÎģÎģÎąÎŗÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "change_your_password": "ΑÎģÎģÎŦÎžĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ ĪƒÎąĪ‚", "changed_visibility_successfully": "Η ΀΁ÎŋβÎŋÎģÎŽ, ÎŦÎģÎģιΞÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", "check_all": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ÎŗÎšÎą ÎēÎąĪ„ÎĩĪƒĪ„ĪÎąÎŧÎŧέÎŊÎą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", + "check_corrupt_asset_backup_button": "ΕÎēĪ„Î­ÎģÎĩĪƒÎˇ ÎĩÎģÎ­ÎŗĪ‡ÎŋĪ…", + "check_corrupt_asset_backup_description": "ΕÎēĪ„Î­ÎģÎĩ΃Îĩ ÎąĪ…Ī„ĪŒÎŊ Ī„ÎŋÎŊ έÎģÎĩÎŗĪ‡Îŋ ÎŧΌÎŊÎŋ ÎŧÎ­ĪƒĪ‰ Wi-Fi ÎēιΚ ÎąĪ†ÎŋĪ Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„Îĩί ΌÎģÎą Ī„Îą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ. Η δΚιδΚÎēÎąĪƒÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪÎēÎ­ĪƒÎĩΚ ÎŧÎĩĪÎšÎēÎŦ ÎģÎĩ΀΄ÎŦ.", "check_logs": "ΕÎģÎ­ÎŗÎžĪ„Îĩ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚", "choose_matching_people_to_merge": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī„Îą ÎąÎŊĪ„Î¯ĪƒĪ„ÎŋÎšĪ‡Îą ÎŦĪ„ÎŋÎŧÎą ÎŗÎšÎą ĪƒĪ…ÎŗĪ‡ĪŽÎŊÎĩĪ…ĪƒÎˇ", "city": "Î ĪŒÎģΡ", @@ -644,23 +653,25 @@ "comments_are_disabled": "Τι ĪƒĪ‡ĪŒÎģΚι ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎą", "common_create_new_album": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊέÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "common_server_error": "ΕÎģÎ­ÎŗÎžĪ„Îĩ Ī„Îˇ ĪƒĪÎŊδÎĩĪƒÎŽ ĪƒÎąĪ‚, βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ÎĩίÎŊιΚ ΀΁ÎŋĪƒÎ˛ÎŦĪƒÎšÎŧÎŋĪ‚ ÎēιΚ ĪŒĪ„Îš ÎŋΚ ÎĩÎēÎ´ĪŒĪƒÎĩÎšĪ‚ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚/δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎĩίÎŊιΚ ĪƒĪ…ÎŧÎ˛ÎąĪ„Î­Ī‚.", - "completed": "Completed", + "completed": "ΟÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ", "confirm": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ", "confirm_admin_password": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ", "confirm_delete_face": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ Ī„Îŋ Ī€ĪĪŒĪƒĪ‰Ī€Îŋ Ī„ÎŋĪ…/Ī„ÎˇĪ‚ {name} ÎąĪ€ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ;", "confirm_delete_shared_link": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒÎŊ Ī„ÎŋÎŊ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ;", "confirm_keep_this_delete_others": "ΌÎģÎą Ī„Îą ÎŦÎģÎģÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī„ÎˇĪ‚ ĪƒĪ„ÎŋÎ¯Î˛ÎąĪ‚ θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ, ÎĩÎēĪ„ĪŒĪ‚ ÎąĪ€ĪŒ ÎąĪ…Ī„ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ. Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ĪƒĪ…ÎŊÎĩĪ‡Î¯ĪƒÎĩĪ„Îĩ;", + "confirm_new_pin_code": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎŊέÎŋĪ… ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "confirm_password": "Î•Ī€ÎšÎ˛ÎĩÎ˛ÎąÎ¯Ī‰ĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ", + "connected_to": "ÎŖĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋ ÎŧÎĩ", "contain": "ΠÎĩĪÎšÎ­Ī‡ÎĩΚ", "context": "ÎŖĪ…ÎŧĪ†ĪÎąÎļΌÎŧÎĩÎŊÎą", "continue": "ÎŖĪ…ÎŊÎ­Ī‡ÎĩΚι", - "control_bottom_app_bar_album_info_shared": "{} ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą ¡ ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îą", + "control_bottom_app_bar_album_info_shared": "{count} ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą ¡ ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îą", "control_bottom_app_bar_create_new_album": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊέÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "control_bottom_app_bar_delete_from_immich": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪ€ĪŒ Ī„Îŋ Immich", "control_bottom_app_bar_delete_from_local": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪ€ĪŒ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", "control_bottom_app_bar_edit_location": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΤÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "control_bottom_app_bar_edit_time": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚ & ÎĪÎąĪ‚", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "ΚÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ", "control_bottom_app_bar_share_to": "ΚÎŋΚÎŊÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŖÎĩ", "control_bottom_app_bar_trash_from_immich": "ΜÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇ ĪƒĪ„Îą Î‘Ī€ÎŋĪĪÎ¯ÎŧÎŧÎąĪ„Îą", "copied_image_to_clipboard": "Η ÎĩΚÎēΌÎŊÎą ÎąÎŊĪ„ÎšÎŗĪÎŦĪ†ÎˇÎēÎĩ ĪƒĪ„Îŋ Ī€ĪĪŒĪ‡ÎĩÎšĪÎŋ.", @@ -692,10 +703,12 @@ "create_tag_description": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊÎ­ÎąĪ‚ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚. Για Ī„ÎšĪ‚ έÎŊθÎĩĪ„ÎĩĪ‚ ÎĩĪ„ÎšÎēÎ­Ī„ÎĩĪ‚, Ī€ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„Îˇ Ī€ÎģÎŽĪÎˇ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ Ī„ÎˇĪ‚, ĪƒĪ…ÎŧĪ€ÎĩĪÎšÎģÎąÎŧβιÎŊÎŋÎŧέÎŊΉÎŊ ΄ΉÎŊ ÎēÎŦθÎĩ΄ΉÎŊ Î´ÎšÎąĪ‡Ī‰ĪÎšĪƒĪ„ÎšÎēĪŽÎŊ.", "create_user": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą Ī‡ĪÎŽĪƒĪ„Îˇ", "created": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ", + "created_at": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ", "crop": "Î‘Ī€ÎŋÎēÎŋĪ€ÎŽ", "curated_object_page_title": "Î ĪÎŦÎŗÎŧÎąĪ„Îą", "current_device": "Î¤ĪÎ­Ī‡ÎŋĪ…ĪƒÎą ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", - "current_server_address": "Current server address", + "current_pin_code": "Î¤ĪÎ­Ī‡Ī‰ÎŊ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ PIN", + "current_server_address": "Î¤ĪÎ­Ī‡ÎŋĪ…ĪƒÎą δΚÎĩĪÎ¸Ī…ÎŊĪƒÎˇ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "custom_locale": "Î ĪÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΡ ΤÎŋĪ€ÎšÎēÎŽ ÎĄĪÎ¸ÎŧÎšĪƒÎˇ", "custom_locale_description": "ΜÎŋ΁ΆÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎšĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊίÎĩĪ‚ ÎēιΚ Ī„ÎŋĪ…Ī‚ ÎąĪÎšÎ¸ÎŧÎŋĪĪ‚, ĪƒĪÎŧΆΉÎŊÎą ÎŧÎĩ Ī„Îˇ ÎŗÎģĪŽĪƒĪƒÎą ÎēιΚ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŋĪ‡ÎŽ", "daily_title_text_date": "Ε, MMM dd", @@ -746,7 +759,7 @@ "direction": "ÎšÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇ", "disabled": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", "disallow_edits": "Î‘Ī€ÎąÎŗĪŒĪÎĩĪ…ĪƒÎˇ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎšĪŽÎŊ", - "discord": "Discord", + "discord": "ΠÎģÎąĪ„Ī†ĪŒĪÎŧÎą Discord", "discover": "ΑÎŊÎ¯Ī‡ÎŊÎĩĪ…ĪƒÎˇ", "dismiss_all_errors": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ΌÎģΉÎŊ ΄ΉÎŊ ĪƒĪ†ÎąÎģÎŧÎŦ΄ΉÎŊ", "dismiss_error": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ ĪƒĪ†ÎŦÎģÎŧÎąĪ„ÎŋĪ‚", @@ -763,7 +776,7 @@ "download_enqueue": "Η ÎģÎŽĪˆÎˇ Ī„Î­Î¸ÎˇÎēÎĩ ΃Îĩ Îŋ΅΁ÎŦ", "download_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎģÎŽĪˆÎˇĪ‚", "download_failed": "Η ÎģÎŽĪˆÎˇ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ", - "download_filename": "ÎąĪĪ‡ÎĩίÎŋ: {}", + "download_filename": "ÎąĪĪ‡ÎĩίÎŋ: {filename}", "download_finished": "Η ÎģÎŽĪˆÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ", "download_include_embedded_motion_videos": "ΕÎŊĪƒĪ‰ÎŧÎąĪ„Ī‰ÎŧέÎŊÎą Î˛Î¯ÎŊĪ„ÎĩÎŋ", "download_include_embedded_motion_videos_description": "ÎŖĪ…ÎŧĪ€ÎĩĪÎšÎģÎŦβÎĩĪ„Îĩ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ Ī€ÎŋĪ… ÎĩίÎŊιΚ ÎĩÎŊĪƒĪ‰ÎŧÎąĪ„Ī‰ÎŧέÎŊÎą ΃Îĩ ÎēΚÎŊÎŋĪÎŧÎĩÎŊÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ Ή΂ ΞÎĩĪ‡Ī‰ĪÎšĪƒĪ„ĪŒ ÎąĪĪ‡ÎĩίÎŋ", @@ -787,6 +800,8 @@ "edit_avatar": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŦÎ˛ÎąĪ„ÎąĪ", "edit_date": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", "edit_date_and_time": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚ ÎēιΚ ĪŽĪÎąĪ‚", + "edit_description": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", + "edit_description_prompt": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩĪ€ÎšÎģÎ­ÎžĪ„Îĩ ÎŊέι Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽ:", "edit_exclusion_pattern": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŧÎŋĪ„Î¯Î˛ÎŋĪ… ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ", "edit_faces": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΀΁ÎŋĪƒĪŽĪ€Ī‰ÎŊ", "edit_import_path": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Î´ÎšÎąÎ´ĪÎŋÎŧÎŽĪ‚ ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽĪ‚", @@ -806,20 +821,23 @@ "editor_close_without_save_title": "ΚÎģÎĩÎ¯ĪƒÎšÎŧÎŋ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒĪ„ÎŽ;", "editor_crop_tool_h2_aspect_ratios": "ΑÎŊÎąÎģÎŋÎŗÎ¯ÎĩĪ‚ Î´ÎšÎąĪƒĪ„ÎŦ΃ÎĩΉÎŊ", "editor_crop_tool_h2_rotation": "ΠÎĩĪÎšĪƒĪ„ĪÎŋĪ†ÎŽ", - "email": "Email", - "empty_folder": "This folder is empty", + "email_notifications": "ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚ email", + "empty_folder": "Î‘Ī…Ī„ĪŒĪ‚ Îŋ ΆÎŦÎēÎĩÎģÎŋĪ‚ ÎĩίÎŊιΚ ÎēÎĩÎŊĪŒĪ‚", "empty_trash": "ΆδÎĩÎšÎąĪƒÎŧÎą ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "empty_trash_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ÎŋĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ιδÎĩΚÎŦ΃ÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ; Î‘Ī…Ī„ĪŒ θι ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩΚ ÎŧΌÎŊΚÎŧÎą ΌÎģÎą Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī„ÎŋĪ… ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ Ī„ÎŋĪ… Immich. \nÎ‘Ī…Ī„ÎŽ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎŊÎąÎšĪÎĩθÎĩί!", "enable": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", + "enable_biometric_auth_description": "Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚ ÎŗÎšÎą ÎŊÎą ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ Ī„ÎˇÎŊ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "enabled": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", "end_date": "ΤÎĩÎģΚÎēÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "ΤÎŋĪ€ÎŋθÎĩĪ„ÎŽÎ¸ÎˇÎēÎĩ ĪƒĪ„Îˇ ÎģÎ¯ĪƒĪ„Îą ÎąÎŊÎąÎŧÎŋÎŊÎŽĪ‚", + "enter_wifi_name": "Î•ÎšĪƒÎąÎŗĪ‰ÎŗÎŽ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚ Wi-Fi", + "enter_your_pin_code": "Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚", + "enter_your_pin_code_subtitle": "Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚ ÎŗÎšÎą ÎŊÎą ÎĩÎšĪƒÎ­ÎģθÎĩĪ„Îĩ ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "error": "ÎŖĪ†ÎŦÎģÎŧÎą", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "Î‘Ī€Î­Ī„Ī…Ī‡Îĩ Ρ ÎąÎģÎģÎąÎŗÎŽ ΃ÎĩÎšĪÎŦĪ‚ Ī„ÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "error_delete_face": "ÎŖĪ†ÎŦÎģÎŧÎą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… ÎąĪ€ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "error_loading_image": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī„ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚", - "error_saving_image": "ÎŖĪ†ÎŦÎģÎŧÎą: {}", + "error_saving_image": "ÎŖĪ†ÎŦÎģÎŧÎą: {error}", "error_title": "ÎŖĪ†ÎŦÎģÎŧÎą - ΚÎŦĪ„Îš Ī€ÎŽÎŗÎĩ ĪƒĪ„ĪÎąÎ˛ÎŦ", "errors": { "cannot_navigate_next_asset": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ Ī€ÎģÎŋÎŽÎŗÎˇĪƒÎˇ ĪƒĪ„Îŋ ÎĩĪ€ĪŒÎŧÎĩÎŊÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", @@ -849,10 +867,12 @@ "failed_to_keep_this_delete_others": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Î´ÎšÎąĪ„ÎŽĪÎˇĪƒÎˇĪ‚ ÎąĪ…Ī„ÎŋĪ Ī„ÎŋĪ… ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… ÎēιΚ Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ ΄ΉÎŊ Ī…Ī€ĪŒÎģÎŋÎšĪ€Ī‰ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "failed_to_load_asset": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "failed_to_load_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", + "failed_to_load_notifications": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", "failed_to_load_people": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ÎąĪ„ĪŒÎŧΉÎŊ", "failed_to_remove_product_key": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇĪ‚ ÎēÎģÎĩΚδΚÎŋĪ ΀΁ÎŋΊΌÎŊĪ„ÎŋĪ‚", "failed_to_stack_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪ„ÎˇÎŊ ĪƒĪ…ÎŧĪ€Î¯ÎĩĪƒÎˇ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "failed_to_unstack_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪ„ÎˇÎŊ ÎąĪ€ÎŋĪƒĪ…ÎŧĪ€Î¯ÎĩĪƒÎˇ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", + "failed_to_update_notification_status": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇĪ‚ Ī„ÎˇĪ‚ ÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "import_path_already_exists": "Î‘Ī…Ī„ÎŽ Ρ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽĪ‚ Ī…Ī€ÎŦ΁·ÎĩΚ ΎδΡ.", "incorrect_email_or_password": "ΛαÎŊÎ¸ÎąĪƒÎŧέÎŊÎŋ email ÎŽ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", "paths_validation_failed": "{paths, plural, one {# Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ} other {# Î´ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚}} ÎąĪ€Î­Ī„Ī…Ī‡ÎąÎŊ ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎēĪĪĪ‰ĪƒÎˇ", @@ -870,6 +890,7 @@ "unable_to_archive_unarchive": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą {archived, select, true {ÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚} other {ÎąĪ€ÎŋÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚}}", "unable_to_change_album_user_role": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ Ī„ÎŋĪ… ΁ΌÎģÎŋĪ… Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "unable_to_change_date": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎŦÎŗÎˇĪ‚ Ī„ÎˇĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", + "unable_to_change_description": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "unable_to_change_favorite": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎŋĪ… ÎŗÎšÎą Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "unable_to_change_location": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "unable_to_change_password": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ Ī„ÎŋĪ… ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", @@ -907,6 +928,7 @@ "unable_to_log_out_all_devices": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąĪ€ÎŋĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ΌÎģΉÎŊ ΄ΉÎŊ ĪƒĪ…ĪƒÎēÎĩĪ…ĪŽÎŊ", "unable_to_log_out_device": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąĪ€ÎŋĪƒĪÎŊδÎĩĪƒÎˇĪ‚ Ī„ÎˇĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚", "unable_to_login_with_oauth": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩÎšĪƒĪŒÎ´ÎŋĪ… ÎŧÎ­ĪƒĪ‰ OAuth", + "unable_to_move_to_locked_folder": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎŧÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "unable_to_play_video": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽĪ‚ Î˛Î¯ÎŊĪ„ÎĩÎŋ", "unable_to_reassign_assets_existing_person": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąÎēÎąĪ„ÎˇÎŗÎŋĪÎšÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ„ÎŋÎŊ/ĪƒĪ„ÎˇÎŊ {name, select, null {Ī…Ī€ÎŦ΁·ÎŋÎŊ ÎŦĪ„ÎŋÎŧÎŋ} other {{name}}}", "unable_to_reassign_assets_new_person": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąÎēÎąĪ„ÎˇÎŗÎŋĪÎšÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ΃Îĩ έÎŊÎą ÎŊέÎŋ ÎŦĪ„ÎŋÎŧÎŋ", @@ -920,6 +942,7 @@ "unable_to_remove_reaction": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇĪ‚ Ī„ÎˇĪ‚ ÎąÎŊĪ„Î¯Î´ĪÎąĪƒÎˇĪ‚", "unable_to_repair_items": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎšĪƒÎēÎĩĪ…ÎŽĪ‚ ÎąÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊΉÎŊ", "unable_to_reset_password": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦĪ‚ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", + "unable_to_reset_pin_code": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦĪ‚ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "unable_to_resolve_duplicate": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€Î¯ÎģĪ…ĪƒÎˇĪ‚ Ī„ÎŋĪ… Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€ÎŋĪ…", "unable_to_restore_assets": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "unable_to_restore_trash": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦĪ‚ Ī„ÎŋĪ… ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", @@ -951,12 +974,12 @@ "exif_bottom_sheet_description": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΠÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚...", "exif_bottom_sheet_details": "Î›Î•Î Î¤ÎŸÎœÎ•ÎĄÎ•Î™Î•ÎŖ", "exif_bottom_sheet_location": "Î¤ÎŸÎ ÎŸÎ˜Î•ÎŖÎ™Î‘", - "exif_bottom_sheet_people": "ΑΝΘΡΩΠΟΙ", + "exif_bottom_sheet_people": "ΑΤΟΜΑ", "exif_bottom_sheet_person_add_person": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "ΗÎģΚÎēÎ¯Îą {age}", + "exif_bottom_sheet_person_age_months": "ΗÎģΚÎēÎ¯Îą {months} ÎŧÎŽÎŊÎĩĪ‚", + "exif_bottom_sheet_person_age_year_months": "ΗÎģΚÎēÎ¯Îą 1 Î­Ī„ÎŋĪ…Ī‚, {months} ÎŧΡÎŊĪŽÎŊ", + "exif_bottom_sheet_person_age_years": "ΗÎģΚÎēÎ¯Îą {years}", "exit_slideshow": "ΈΞÎŋδÎŋĪ‚ ÎąĪ€ĪŒ Ī„ÎˇÎŊ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇ", "expand_all": "ΑÎŊÎŦĪ€Ī„Ī…ÎžÎˇ ΌÎģΉÎŊ", "experimental_settings_new_asset_list_subtitle": "ÎŖÎĩ ÎĩΞέÎģΚΞΡ", @@ -973,12 +996,13 @@ "extension": "Î•Ī€Î­ÎēĪ„ÎąĪƒÎˇ", "external": "Î•ÎžĪ‰Ī„ÎĩĪÎšÎēĪŒĪ‚", "external_libraries": "Î•ÎžĪ‰Ī„ÎĩĪÎšÎēÎ­Ī‚ βΚβÎģΚÎŋθΎÎēÎĩĪ‚", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Î•ÎžĪ‰Ī„ÎĩĪÎšÎēΌ δίÎē΄΅Îŋ", + "external_network_sheet_info": "ÎŒĪ„ÎąÎŊ δÎĩÎŊ ÎĩÎ¯ĪƒĪ„Îĩ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋΚ ĪƒĪ„Îŋ ΀΁ÎŋĪ„ÎšÎŧĪŽÎŧÎĩÎŊÎŋ δίÎē΄΅Îŋ Wi-Fi, Ρ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ θι ĪƒĪ…ÎŊδÎĩθÎĩί ÎŧÎĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎ­ĪƒĪ‰ Ī„ÎŋĪ… Ī€ĪĪŽĪ„ÎŋĪ… ÎąĪ€ĪŒ Ī„Îą Ī€ÎąĪÎąÎēÎŦ΄Ή URLs Ī€ÎŋĪ… ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î˛ĪÎĩΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ, ΞÎĩÎēΚÎŊĪŽÎŊĪ„ÎąĪ‚ ÎąĪ€ĪŒ Ī„Îŋ Ī€ÎŦÎŊΉ ΀΁ÎŋĪ‚ Ī„Îŋ ÎēÎŦ΄Ή", "face_unassigned": "Μη ÎąÎŊÎąĪ„ÎĩθÎĩΚÎŧέÎŊÎŋ", - "failed": "Failed", + "failed": "Î‘Ī€Î­Ī„Ī…Ī‡Îĩ", + "failed_to_authenticate": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "failed_to_load_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ Ī†ÎąÎēέÎģÎŋĪ…", "favorite": "Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎŋ", "favorite_or_unfavorite_photo": "ÎŸĪÎ¯ĪƒĪ„Îĩ ÎŧÎ¯Îą ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą Ή΂ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊΡ ÎŽ ÎąĪ†ÎąÎšĪÎ­ĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎąĪ€ĪŒ Ī„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "favorites": "Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎą", @@ -992,21 +1016,22 @@ "filetype": "Î¤ĪĪ€ÎŋĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ…", "filter": "ÎĻίÎģ΄΁Îŋ", "filter_people": "ÎĻΚÎģ΄΁ÎŦĪÎšĪƒÎŧÎą ÎąĪ„ĪŒÎŧΉÎŊ", + "filter_places": "ÎĻΚÎģ΄΁ÎŦĪÎšĪƒÎŧÎą Ī„ÎŋĪ€ÎŋθÎĩĪƒÎšĪŽÎŊ", "find_them_fast": "Î’ĪÎĩÎ¯Ī„Îĩ Ī„ÎŋĪ…Ī‚ ÎŗĪÎŽÎŗÎŋĪÎą ÎŧÎĩ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ ΌÎŊÎŋÎŧÎą", "fix_incorrect_match": "Î”ÎšĪŒĪÎ¸Ī‰ĪƒÎˇ ÎģÎąÎŊÎ¸ÎąĪƒÎŧέÎŊÎˇĪ‚ ÎąÎŊĪ„ÎšĪƒĪ„ÎŋÎ¯Ī‡ÎšĪƒÎˇĪ‚", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "ÎĻÎŦÎēÎĩÎģÎŋĪ‚", + "folder_not_found": "Ο ΆÎŦÎēÎĩÎģÎŋĪ‚ δÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎĩ", "folders": "ÎĻÎŦÎēÎĩÎģÎŋΚ", "folders_feature_description": "ΠÎĩĪÎšÎŽÎŗÎˇĪƒÎˇ ĪƒĪ„ÎˇÎŊ ΀΁ÎŋβÎŋÎģÎŽ Ī†ÎąÎēέÎģÎŋĪ… ÎŗÎšÎą Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îŋ ĪƒĪĪƒĪ„ÎˇÎŧÎą ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ", "forward": "Î ĪÎŋĪ‚ Ī„Îą ÎĩÎŧĪ€ĪĪŒĪ‚", "general": "ΓÎĩÎŊΚÎēÎŦ", "get_help": "Î–ÎˇĪ„ÎŽĪƒĪ„Îĩ βÎŋΎθÎĩΚι", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "ΔÎĩÎŊ ÎŽĪ„ÎąÎŊ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎģÎŽĪˆÎˇ Ī„ÎŋĪ… ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚ Wi-Fi. ΒÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ Î´ĪŽĪƒÎĩΚ Ī„ÎšĪ‚ ÎąĪ€ÎąĪÎąÎ¯Ī„ÎˇĪ„ÎĩĪ‚ ÎŦδÎĩΚÎĩĪ‚ ÎēιΚ ĪŒĪ„Îš ÎĩÎ¯ĪƒĪ„Îĩ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋΚ ΃Îĩ δίÎē΄΅Îŋ Wi-Fi", "getting_started": "ΞÎĩÎēΚÎŊĪŽÎŊĪ„ÎąĪ‚", "go_back": "Î ÎˇÎŗÎąÎ¯ÎŊÎĩĪ„Îĩ Ī€Î¯ĪƒĪ‰", "go_to_folder": "ΜÎĩĪ„ÎŦÎ˛ÎąĪƒÎˇ ĪƒĪ„Îŋ ΆÎŦÎēÎĩÎģÎŋ", "go_to_search": "Î ÎˇÎŗÎąÎ¯ÎŊÎĩĪ„Îĩ ĪƒĪ„ÎˇÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ", - "grant_permission": "Grant permission", + "grant_permission": "Î•Ī€ÎšĪ„ĪÎ­ĪˆĪ„Îĩ Ī„ÎˇÎŊ ÎŦδÎĩΚι", "group_albums_by": "ΟÎŧιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎēÎąĪ„ÎŦ...", "group_country": "ΟÎŧιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ Ī‡ĪŽĪÎą", "group_no": "ΚαÎŧÎ¯Îą ÎŋÎŧÎŋδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", @@ -1040,7 +1065,8 @@ "home_page_delete_remote_err_local": "ΤÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ĪƒĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎˇĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "home_page_favorite_err_local": "ΔÎĩÎŊ ÎŧĪ€ÎŋĪĪŽ ÎąÎēΌÎŧÎą ÎŊÎą ÎąÎŗÎąĪ€ÎŽĪƒĪ‰ Ī„Îą Ī„ÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "home_page_favorite_err_partner": "ΔÎĩÎŊ ÎĩίÎŊιΚ ÎąÎēΌÎŧÎą Î´Ī…ÎŊÎąĪ„ÎŽ Ρ Ī€ĪĪŒĪƒÎ¸ÎĩĪƒÎˇ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ…ÎŊĪ„ĪĪŒĪ†ÎŋĪ… ĪƒĪ„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", - "home_page_first_time_notice": "ΕÎŦÎŊ ÎąĪ…Ī„ÎŽ ÎĩίÎŊιΚ Ρ Ī€ĪĪŽĪ„Îˇ ΆÎŋ΁ÎŦ Ī€ÎŋĪ… Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ, βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ ÎĩĪ€ÎšÎģέΞÎĩΚ έÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†ÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚, ĪŽĪƒĪ„Îĩ Ī„Îŋ ·΁ÎŋÎŊÎŋδΚÎŦÎŗĪÎąÎŧÎŧÎą ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ĪƒĪ…ÎŧĪ€ÎģÎˇĪĪŽĪƒÎĩΚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", + "home_page_first_time_notice": "ΕÎŦÎŊ ÎąĪ…Ī„ÎŽ ÎĩίÎŊιΚ Ρ Ī€ĪĪŽĪ„Îˇ ΆÎŋ΁ÎŦ Ī€ÎŋĪ… Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ, βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ ÎĩĪ€ÎšÎģέΞÎĩΚ έÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†ÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚, ĪŽĪƒĪ„Îĩ Ī„Îŋ ·΁ÎŋÎŊÎŋδΚÎŦÎŗĪÎąÎŧÎŧÎą ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ĪƒĪ…ÎŧĪ€ÎģÎˇĪĪŽĪƒÎĩΚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "home_page_locked_error_local": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎŧÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇ Ī„ÎŋĪ€ÎšÎēĪŽÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ, Ī€ÎąĪÎŦβÎģÎĩĪˆÎˇ", "home_page_share_err_local": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ Ī„ÎŋĪ€ÎšÎēĪŽÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ÎŧÎ­ĪƒĪ‰ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "home_page_upload_err_limit": "ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎĩβÎŦ΃ÎĩĪ„Îĩ ÎŧΌÎŊÎŋ 30 ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎēÎŦθÎĩ ΆÎŋ΁ÎŦ, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "host": "ÎĻΚÎģÎŋΞÎĩÎŊÎ¯Îą", @@ -1117,10 +1143,9 @@ "list": "Î›Î¯ĪƒĪ„Îą", "loading": "ÎĻĪŒĪĪ„Ī‰ĪƒÎˇ", "loading_search_results_failed": "Η Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎąĪ€ÎŋĪ„ÎĩÎģÎĩ΃ÎŧÎŦ΄ΉÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "Η ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ θι ĪƒĪ…ÎŊδÎĩθÎĩί ÎŧÎĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎ­ĪƒĪ‰ ÎąĪ…Ī„ÎŋĪ Ī„ÎŋĪ… URL ĪŒĪ„ÎąÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš Ī„Îŋ ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋ δίÎē΄΅Îŋ Wi-Fi", + "location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", + "location_permission_content": "Για ÎŊÎą Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚΡθÎĩί Ρ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎąĪ…Ī„ĪŒÎŧÎąĪ„ÎˇĪ‚ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽĪ‚, Ī„Îŋ Immich ·΁ÎĩΚÎŦÎļÎĩĪ„ÎąÎš ÎŦδÎĩΚι ÎŗÎšÎą Ī„ÎˇÎŊ ÎąÎēĪÎšÎ˛ÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą Ī„ÎˇĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚ ĪŽĪƒĪ„Îĩ ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą δΚιβÎŦÎļÎĩΚ Ī„Îŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… Ī„ĪÎ­Ī‡ÎŋÎŊĪ„ÎŋĪ‚ δΚÎēĪ„ĪÎŋĪ… Wi-Fi", "location_picker_choose_on_map": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ĪƒĪ„Îŋ ·ÎŦĪĪ„Îˇ", "location_picker_latitude_error": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ έÎŊÎą Î­ÎŗÎē΅΁Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ Ī€ÎģÎŦĪ„ÎŋĪ‚", "location_picker_latitude_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ Ī„Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ Ī€ÎģÎŦĪ„ÎŋĪ‚ ĪƒÎąĪ‚ ÎĩÎ´ĪŽ", @@ -1170,8 +1195,8 @@ "manage_your_devices": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎĩĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ĪƒÎąĪ‚", "manage_your_oauth_connection": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îˇ ĪƒĪÎŊδÎĩĪƒÎŽ ĪƒÎąĪ‚ OAuth", "map": "ΧÎŦĪĪ„ÎˇĪ‚", - "map_assets_in_bound": "{} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", - "map_assets_in_bounds": "{} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", + "map_assets_in_bound": "{count} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", + "map_assets_in_bounds": "{count} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", "map_cannot_get_user_location": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎģÎŽĪˆÎˇ Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", "map_location_dialog_yes": "Ναι", "map_location_picker_page_use_location": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", @@ -1185,15 +1210,18 @@ "map_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ·ÎŦĪĪ„Îˇ", "map_settings_dark_mode": "ÎŖÎēÎŋĪ„ÎĩΚÎŊÎŽ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą", "map_settings_date_range_option_day": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎĩĪ‚ 24 ĪŽĪÎĩĪ‚", - "map_settings_date_range_option_days": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎĩĪ‚ {} ΡÎŧÎ­ĪÎĩĪ‚", + "map_settings_date_range_option_days": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎĩĪ‚ {days} ΡÎŧÎ­ĪÎĩĪ‚", "map_settings_date_range_option_year": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎŋ Î­Ī„ÎŋĪ‚", - "map_settings_date_range_option_years": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎą {} Î­Ī„Îˇ", + "map_settings_date_range_option_years": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎą {years} Î­Ī„Îˇ", "map_settings_dialog_title": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΧÎŦĪĪ„Îˇ", "map_settings_include_show_archived": "ÎŖĪ…ÎŧĪ€ÎĩĪÎšÎģÎŦβÎĩĪ„Îĩ Î‘ĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎˇÎŧέÎŊÎą", "map_settings_include_show_partners": "ÎŖĪ…ÎŧĪ€ÎĩĪÎšÎģÎŦβÎĩĪ„Îĩ ÎŖĪ…ÎŊĪ„ĪĪŒĪ†ÎŋĪ…Ī‚", "map_settings_only_show_favorites": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ÎŧΌÎŊÎŋ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊΉÎŊ", "map_settings_theme_settings": "ΘέÎŧÎą ·ÎŦĪĪ„Îˇ", "map_zoom_to_see_photos": "ÎŖÎŧΚÎēĪĪÎŊÎĩĪ„Îĩ ÎŗÎšÎą ÎŊÎą δÎĩÎ¯Ī„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", + "mark_all_as_read": "Î•Ī€ÎšĪƒÎŽÎŧÎąÎŊĪƒÎˇ ΌÎģΉÎŊ Ή΂ ÎąÎŊÎąÎŗÎŊĪ‰ĪƒÎŧέÎŊÎą", + "mark_as_read": "Î•Ī€ÎšĪƒÎŽÎŧÎąÎŊĪƒÎˇ Ή΂ ÎąÎŊÎąÎŗÎŊĪ‰ĪƒÎŧέÎŊÎŋ", + "marked_all_as_read": "ΌÎģÎą ÎĩĪ€ÎšĪƒÎˇÎŧÎŦÎŊθΡÎēÎąÎŊ Ή΂ ÎąÎŊÎąÎŗÎŊĪ‰ĪƒÎŧέÎŊÎą", "matches": "ΑÎŊĪ„ÎšĪƒĪ„ÎŋÎšĪ‡Î¯ÎĩĪ‚", "media_type": "Î¤ĪĪ€ÎŋĪ‚ Ī€ÎŋÎģĪ…ÎŧÎ­ĪƒÎŋĪ…", "memories": "ΑÎŊÎąÎŧÎŊÎŽĪƒÎĩÎšĪ‚", @@ -1202,8 +1230,6 @@ "memories_setting_description": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îš θι ÎĩÎŧĪ†ÎąÎŊίÎļÎĩĪ„ÎąÎš ĪƒĪ„ÎšĪ‚ ÎąÎŊÎąÎŧÎŊÎŽĪƒÎĩÎšĪ‚ ĪƒÎąĪ‚", "memories_start_over": "ΞÎĩÎēΚÎŊÎŽĪƒĪ„Îĩ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎąĪĪ‡ÎŽ", "memories_swipe_to_close": "ÎŖĪĪÎĩĪ„Îĩ ΀΁ÎŋĪ‚ Ī„Îą Ī€ÎŦÎŊΉ ÎŗÎšÎą ÎŊÎą ÎēÎģÎĩÎ¯ĪƒÎĩĪ„Îĩ", - "memories_year_ago": "Î ĪÎšÎŊ έÎŊÎą Ī‡ĪĪŒÎŊÎŋ", - "memories_years_ago": "Î ĪÎšÎŊ ÎąĪ€ĪŒ {} Ī‡ĪĪŒÎŊΚι", "memory": "ΑÎŊÎŦÎŧÎŊÎˇĪƒÎˇ", "memory_lane_title": "Î”ÎšÎąÎ´ĪÎŋÎŧÎŽ ΑÎŊÎąÎŧÎŊÎŽĪƒÎĩΉÎŊ {title}", "menu": "ΜÎĩÎŊÎŋĪ", @@ -1227,8 +1253,8 @@ "my_albums": "Τι ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŧÎŋĪ…", "name": "ΌÎŊÎŋÎŧÎą", "name_or_nickname": "ΌÎŊÎŋÎŧÎą ÎŽ ΈÎĩĪ…Î´ĪŽÎŊĪ…ÎŧÎŋ", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "ΔιÎēĪ„ĪĪ‰ĪƒÎˇ", + "networking_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ Ī„ÎĩÎģΚÎēĪŽÎŊ ĪƒÎˇÎŧÎĩÎ¯Ī‰ÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "never": "ΠÎŋĪ„Î­", "new_album": "ΝέÎŋ ΆÎģÎŧĪ€ÎŋĪ…Îŧ", "new_api_key": "ΝέÎŋ API Key", @@ -1252,12 +1278,13 @@ "no_favorites_message": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą ÎŗÎšÎą ÎŊÎą Î˛ĪÎĩÎ¯Ī„Îĩ ÎŗĪÎŽÎŗÎŋĪÎą Ī„ÎšĪ‚ ÎēÎąÎģĪĪ„Îĩ΁ÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚", "no_libraries_message": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ ÎŧΚι ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽ βΚβÎģΚÎŋθΎÎēΡ ÎŗÎšÎą ÎŊÎą ΀΁ÎŋβÎŦÎģÎĩĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚", "no_name": "Î§Ī‰ĪÎ¯Ī‚ ΌÎŊÎŋÎŧÎą", + "no_notifications": "ΚαÎŧÎ¯Îą ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "no_places": "ΚαÎŧÎ¯Îą Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", "no_results": "ΚαÎŊέÎŊÎą ÎąĪ€ÎŋĪ„Î­ÎģÎĩ΃ÎŧÎą", "no_results_description": "ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ έÎŊÎą ĪƒĪ…ÎŊĪŽÎŊĪ…ÎŧÎŋ ÎŽ Ī€ÎšÎŋ ÎŗÎĩÎŊΚÎēÎŽ ÎģέΞΡ-ÎēÎģÎĩΚδί", "no_shared_albums_message": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ έÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŗÎšÎą ÎŊÎą ÎŧÎŋÎšĪÎŦÎļÎĩĪƒĪ„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŧÎĩ ÎŦĪ„ÎŋÎŧÎą ĪƒĪ„Îŋ δίÎēĪ„Ī…ĪŒ ĪƒÎąĪ‚", "not_in_any_album": "ÎŖÎĩ ÎēÎąÎŊέÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", - "not_selected": "Not selected", + "not_selected": "ΔÎĩÎŊ ÎĩĪ€ÎšÎģÎ­Ī‡Î¸ÎˇÎēÎĩ", "note_apply_storage_label_to_previously_uploaded assets": "ÎŖÎˇÎŧÎĩÎ¯Ī‰ĪƒÎˇ: Για ÎŊÎą ÎĩĪ†ÎąĪÎŧΌ΃ÎĩĪ„Îĩ Ī„ÎˇÎŊ Î•Ī„ÎšÎēÎ­Ī„Îą Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ ΃Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸Îĩί ΀΁ÎŋÎˇÎŗÎŋĪ…ÎŧέÎŊΉ΂, ÎĩÎēĪ„ÎĩÎģÎ­ĪƒĪ„Îĩ Ī„Îŋ", "notes": "ÎŖÎˇÎŧÎĩÎšĪŽĪƒÎĩÎšĪ‚", "notification_permission_dialog_content": "Για ÎŊÎą ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ Ī„ÎšĪ‚ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚, ÎŧÎĩĪ„ÎąÎ˛ÎĩÎ¯Ī„Îĩ ĪƒĪ„ÎšĪ‚ ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎēιΚ ÎĩĪ€ÎšÎģÎ­ÎžĪ„Îĩ ÎŊÎą ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩĪ„ÎąÎš.", @@ -1267,7 +1294,6 @@ "notification_toggle_setting_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ ÎŧÎ­ĪƒĪ‰ email", "notifications": "ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚", "notifications_setting_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", - "oauth": "OAuth", "official_immich_resources": "Î•Ī€Î¯ĪƒÎˇÎŧÎŋΚ Î ĪŒĪÎŋΚ Ī„ÎŋĪ… Immich", "offline": "ΕÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", "offline_paths": "Î”ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚ ÎĩÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", @@ -1282,6 +1308,7 @@ "onboarding_welcome_user": "ΚαÎģĪ‰ĪƒĪŒĪÎšĪƒÎĩĪ‚, {user}", "online": "ÎŖÎĩ ĪƒĪÎŊδÎĩĪƒÎˇ", "only_favorites": "ÎœĪŒÎŊÎŋ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", + "open": "ΆÎŊÎŋÎšÎŗÎŧÎą", "open_in_map_view": "ΆÎŊÎŋÎšÎŗÎŧÎą ΃Îĩ ΀΁ÎŋβÎŋÎģÎŽ ·ÎŦĪĪ„Îˇ", "open_in_openstreetmap": "ΆÎŊÎŋÎšÎŗÎŧÎą ĪƒĪ„Îŋ OpenStreetMap", "open_the_search_filters": "ΑÎŊÎŋÎ¯ÎžĪ„Îĩ Ī„Îą Ī†Î¯ÎģĪ„ĪÎą ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", @@ -1305,7 +1332,7 @@ "partner_page_partner_add_failed": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ΀΁ÎŋĪƒÎ¸ÎŽÎēÎˇĪ‚ ĪƒĪ…ÎŊĪ„ĪĪŒĪ†ÎŋĪ…", "partner_page_select_partner": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ĪƒĪ…ÎŊĪ„ĪĪŒĪ†ÎŋĪ…", "partner_page_shared_to_title": "ΔιαÎŧÎŋÎšĪÎŦÎļÎĩĪ„ÎąÎš ÎŧÎĩ", - "partner_page_stop_sharing_content": "Ο/Η {} δÎĩÎŊ θι ÎŧĪ€Îŋ΁Îĩί Ī€ÎģέÎŋÎŊ ÎŊÎą δÎĩΚ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ĪƒÎąĪ‚.", + "partner_page_stop_sharing_content": "Ο/Η {partner} δÎĩÎŊ θι ÎŧĪ€Îŋ΁Îĩί Ī€ÎģέÎŋÎŊ ÎŊÎą δÎĩΚ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ĪƒÎąĪ‚.", "partner_sharing": "ΚÎŋΚÎŊÎŽ Î§ĪÎŽĪƒÎˇ ÎŖĪ…ÎŊÎĩĪÎŗÎąĪ„ĪŽÎŊ", "partners": "ÎŖĪ…ÎŊÎĩĪÎŗÎŦĪ„ÎĩĪ‚", "password": "ÎšĪ‰Î´ÎšÎēĪŒĪ‚ Î ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", @@ -1359,7 +1386,7 @@ "play_motion_photo": "ΑÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ΚιÎŊÎŋĪÎŧÎĩÎŊÎˇĪ‚ ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚", "play_or_pause_video": "ΑÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ÎŽ Ī€ÎąĪĪƒÎˇ Î˛Î¯ÎŊĪ„ÎĩÎŋ", "port": "Î˜ĪĪÎą", - "preferences_settings_subtitle": "Manage the app's preferences", + "preferences_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ΀΁ÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "preferences_settings_title": "Î ĪÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚", "preset": "Î ĪÎŋÎēιθÎŋĪÎšĪƒÎŧέÎŊΡ ĪĪÎ¸ÎŧÎšĪƒÎˇ", "preview": "Î ĪÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ", @@ -1372,7 +1399,6 @@ "profile_drawer_client_out_of_date_major": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ ÎēĪĪÎšÎą έÎēδÎŋĪƒÎˇ.", "profile_drawer_client_out_of_date_minor": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ δÎĩĪ…Ī„Îĩ΁ÎĩĪÎŋĪ…ĪƒÎą έÎēδÎŋĪƒÎˇ.", "profile_drawer_client_server_up_to_date": "Ο Ī€ÎĩÎģÎŦĪ„ÎˇĪ‚ ÎēιΚ Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ÎĩίÎŊιΚ ÎĩÎŊΡÎŧÎĩ΁ΉÎŧέÎŊÎŋΚ", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ ÎēĪĪÎšÎą έÎēδÎŋĪƒÎˇ.", "profile_drawer_server_out_of_date_minor": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ δÎĩĪ…Ī„Îĩ΁ÎĩĪÎŋĪ…ĪƒÎą έÎēδÎŋĪƒÎˇ.", "profile_image_of_user": "ΕιÎēΌÎŊÎą ΀΁ÎŋĪ†Î¯Îģ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ {user}", @@ -1381,7 +1407,7 @@ "public_share": "ΔηÎŧĪŒĪƒÎšÎą ΚÎŋΚÎŊÎŽ Î§ĪÎŽĪƒÎˇ", "purchase_account_info": "ÎĨĪ€ÎŋĪƒĪ„ÎˇĪÎšÎēĪ„ÎŽĪ‚", "purchase_activated_subtitle": "ÎŖÎąĪ‚ ÎĩĪ…Ī‡ÎąĪÎšĪƒĪ„ÎŋĪÎŧÎĩ ÎŗÎšÎą Ī„ÎˇÎŊ Ī…Ī€ÎŋĪƒĪ„ÎŽĪÎšÎžÎˇ Ī„ÎŋĪ… Immich ÎēιΚ ÎģÎŋÎŗÎšĪƒÎŧΚÎēĪŽÎŊ ÎąÎŊÎŋÎšĪ‡Ī„ÎŋĪ ÎēĪŽÎ´ÎšÎēÎą", - "purchase_activated_time": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ ĪƒĪ„ÎšĪ‚ {date, date}", + "purchase_activated_time": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ ĪƒĪ„ÎšĪ‚ {date}", "purchase_activated_title": "ΤÎŋ ÎēÎģÎĩΚδί ĪƒÎąĪ‚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", "purchase_button_activate": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "purchase_button_buy": "Î‘ÎŗÎŋ΁ÎŦ", @@ -1426,6 +1452,8 @@ "recent_searches": "Î ĪĪŒĪƒĪ†ÎąĪ„ÎĩĪ‚ ÎąÎŊÎąÎļÎˇĪ„ÎŽĪƒÎĩÎšĪ‚", "recently_added": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îą", "recently_added_page_title": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ Î ĪĪŒĪƒĪ†ÎąĪ„Îą", + "recently_taken": "Î›ÎŽĪ†Î¸ÎˇÎēÎąÎŊ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îą", + "recently_taken_page_title": "Î›ÎŽĪ†Î¸ÎˇÎēÎąÎŊ Î ĪĪŒĪƒĪ†ÎąĪ„Îą", "refresh": "ΑÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ", "refresh_encoded_videos": "ΑÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋΚΡÎŧέÎŊΉÎŊ Î˛Î¯ÎŊĪ„ÎĩÎŋ", "refresh_faces": "ΑÎŊÎąÎŊÎ­Ī‰ĪƒÎˇ ΀΁ÎŋĪƒĪŽĪ€Ī‰ÎŊ", @@ -1510,7 +1538,7 @@ "search_filter_date_title": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎĩĪĪÎŋĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎšĪŽÎŊ", "search_filter_display_option_not_in_album": "ÎŒĪ‡Îš ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "search_filter_display_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇĪ‚", - "search_filter_filename": "Search by file name", + "search_filter_filename": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŧÎĩ ΌÎŊÎŋÎŧÎą ÎąĪĪ‡ÎĩίÎŋĪ…", "search_filter_location": "ΤÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", "search_filter_location_title": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", "search_filter_media_type": "Î¤ĪĪ€ÎŋĪ‚ ÎœÎ­ĪƒÎŋĪ…", @@ -1518,17 +1546,17 @@ "search_filter_people_title": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎŦĪ„ÎŋÎŧÎą", "search_for": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŗÎšÎą", "search_for_existing_person": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ Ī…Ī€ÎŦ΁·ÎŋÎŊĪ„ÎŋĪ‚ ÎąĪ„ĪŒÎŧÎŋĪ…", - "search_no_more_result": "No more results", + "search_no_more_result": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ ÎŦÎģÎģÎą ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą", "search_no_people": "ΚαÎŊέÎŊÎą ÎŦĪ„ÎŋÎŧÎŋ", "search_no_people_named": "ΚαÎŊέÎŊÎą ÎŦĪ„ÎŋÎŧÎŋ ÎŧÎĩ ΌÎŊÎŋÎŧÎą \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ÎŋĪ„ÎĩÎģÎ­ĪƒÎŧÎąĪ„Îą, ΀΁ÎŋĪƒĪ€ÎąÎ¸ÎŽĪƒĪ„Îĩ ÎŧÎĩ Î´ÎšÎąĪ†Îŋ΁ÎĩĪ„ÎšÎēÎ­Ī‚ Îŋ΁ÎŋÎģÎŋÎŗÎ¯ÎĩĪ‚ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚ ÎŽ ĪƒĪ…ÎŊÎ´Ī…ÎąĪƒÎŧÎŋĪĪ‚", "search_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", "search_page_categories": "ÎšÎąĪ„ÎˇÎŗÎŋĪÎ¯ÎĩĪ‚", "search_page_motion_photos": "ΚιÎŊÎŋĪÎŧÎĩÎŊÎĩĪ‚ ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", "search_page_no_objects": "Μη Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎąÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊΉÎŊ", "search_page_no_places": "Μη Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎŗÎšÎą ÎŧÎ­ĪÎˇ", "search_page_screenshots": "ÎŖĪ„ÎšÎŗÎŧÎšĪŒĪ„Ī…Ī€Îą ÎŋÎ¸ĪŒÎŊÎˇĪ‚", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŗÎšÎą Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚", "search_page_selfies": "ÎŖÎ­ÎģĪ†Îš", "search_page_things": "Î ĪÎŦÎŗÎŧÎąĪ„Îą", "search_page_view_all_button": "Î ĪÎŋβÎŋÎģÎŽ ΌÎģΉÎŊ", @@ -1540,7 +1568,7 @@ "search_result_page_new_search_hint": "Νέα ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ", "search_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", "search_state": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŊÎŋÎŧÎŋĪ...", - "search_suggestion_list_smart_search_hint_1": "Η Î­ÎžĪ…Ī€ÎŊΡ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ ÎąĪ€ĪŒ ΀΁ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ, ÎŗÎšÎą ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŧÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊΉÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪƒĪ…ÎŊĪ„ÎąÎēĪ„ÎšÎēΌ", + "search_suggestion_list_smart_search_hint_1": "Η Î­ÎžĪ…Ī€ÎŊΡ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ ÎąĪ€ĪŒ ΀΁ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ, ÎŗÎšÎą ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŧÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊΉÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪƒĪ…ÎŊĪ„ÎąÎēĪ„ÎšÎēΌ ", "search_suggestion_list_smart_search_hint_2": "m:Ό΁ÎŋĪ‚-ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", "search_tags": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎĩĪ„ÎšÎēÎĩĪ„ĪŽÎŊ...", "search_timezone": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎļĪŽÎŊÎˇĪ‚ ĪŽĪÎąĪ‚...", @@ -1567,7 +1595,7 @@ "selected_count": "{count, plural, other {# ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋΚ}}", "send_message": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎģÎŽ ÎŧΡÎŊĪÎŧÎąĪ„ÎŋĪ‚", "send_welcome_email": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎģÎŽ email ÎēÎąÎģĪ‰ĪƒÎŋĪÎ¯ĪƒÎŧÎąĪ„ÎŋĪ‚", - "server_endpoint": "Server Endpoint", + "server_endpoint": "ΤÎĩÎģΚÎēΌ ĪƒÎˇÎŧÎĩίÎŋ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "server_info_box_app_version": "ΈÎēδÎŋĪƒÎˇ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "server_info_box_server_url": "URL δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "server_offline": "ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ΕÎēĪ„ĪŒĪ‚ ÎŖĪÎŊδÎĩĪƒÎˇĪ‚", @@ -1588,28 +1616,28 @@ "setting_image_viewer_preview_title": "ÎĻĪŒĪĪ„Ī‰ĪƒÎˇ ÎĩΚÎēΌÎŊÎąĪ‚ ΀΁ÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇĪ‚", "setting_image_viewer_title": "ΕιÎēΌÎŊÎĩĪ‚", "setting_languages_apply": "Î•Ī†ÎąĪÎŧÎŋÎŗÎŽ", - "setting_languages_subtitle": "Change the app's language", + "setting_languages_subtitle": "ΑÎģÎģÎŦÎžĪ„Îĩ Ī„Îˇ ÎŗÎģĪŽĪƒĪƒÎą Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "setting_languages_title": "ΓÎģĪŽĪƒĪƒÎĩĪ‚", - "setting_notifications_notify_failures_grace_period": "ΕιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąĪ€ÎŋĪ„Ī…Ī‡ÎšĪŽÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ: {}", - "setting_notifications_notify_hours": "{} ĪŽĪÎĩĪ‚", + "setting_notifications_notify_failures_grace_period": "ΕιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąĪ€ÎŋĪ„Ī…Ī‡ÎšĪŽÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ: {duration}", + "setting_notifications_notify_hours": "{count} ĪŽĪÎĩĪ‚", "setting_notifications_notify_immediately": "ÎąÎŧÎ­ĪƒĪ‰Ī‚", - "setting_notifications_notify_minutes": "{} ÎģÎĩ΀΄ÎŦ", + "setting_notifications_notify_minutes": "{count} ÎģÎĩ΀΄ÎŦ", "setting_notifications_notify_never": "Ī€ÎŋĪ„Î­", - "setting_notifications_notify_seconds": "{} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩĪ€Ī„Îą", + "setting_notifications_notify_seconds": "{count} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩĪ€Ī„Îą", "setting_notifications_single_progress_subtitle": "ΛÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎ¯Ī‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ΀΁ÎŋĪŒÎ´ÎŋĪ… ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ÎąÎŊÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", "setting_notifications_single_progress_title": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ΀΁ÎŋĪŒÎ´ÎŋĪ… ÎģÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎšĪŽÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", "setting_notifications_subtitle": "Î ĪÎŋĪƒÎąĪÎŧĪŒĪƒĪ„Îĩ Ī„ÎšĪ‚ ΀΁ÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", "setting_notifications_total_progress_subtitle": "ÎŖĪ…ÎŊÎŋÎģΚÎēÎŽ Ī€ĪĪŒÎŋδÎŋĪ‚ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ (ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ/ĪƒĪÎŊÎŋÎģÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ)", "setting_notifications_total_progress_title": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ĪƒĪ…ÎŊÎŋÎģΚÎēÎŽĪ‚ ΀΁ÎŋĪŒÎ´ÎŋĪ… δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", "setting_video_viewer_looping_title": "ÎŖĪ…ÎŊÎĩĪ‡ÎŽĪ‚ Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_subtitle": "ÎŒĪ„ÎąÎŊ ÎŧÎĩĪ„ÎąÎ´Î¯Î´ÎĩĪ„Îĩ έÎŊÎą Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎąĪ€ĪŒ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ, ÎąÎŊÎąĪ€ÎąĪÎŦÎŗÎĩĪ„Îĩ Ī„Îŋ ÎąĪ…Î¸ÎĩÎŊĪ„ÎšÎēΌ ÎąÎēΌÎŧΡ ÎēιΚ ĪŒĪ„ÎąÎŊ Ī…Ī€ÎŦ΁·ÎĩΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ ÎŧÎĩ Î´ÎšÎąĪ†Îŋ΁ÎĩĪ„ÎšÎēÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ. ÎœĪ€Îŋ΁Îĩί ÎŊÎą ΀΁ÎŋÎēÎąÎģÎ­ĪƒÎĩΚ ÎēÎąÎ¸Ī…ĪƒĪ„Î­ĪÎˇĪƒÎˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚. Τι Î˛Î¯ÎŊĪ„ÎĩÎŋ Ī€ÎŋĪ… ÎĩίÎŊιΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎą Ī„ÎŋĪ€ÎšÎēÎŦ, ÎąÎŊÎąĪ€ÎąĪÎŦÎŗÎŋÎŊĪ„ÎąÎš ĪƒĪ„ÎˇÎŊ ÎąĪ…Î¸ÎĩÎŊĪ„ÎšÎēÎŽ Ī€ÎŋÎšĪŒĪ„ÎˇĪ„Îą, ÎąÎŊÎĩÎžÎąĪĪ„ÎŽĪ„Ī‰Ī‚ ÎąĪ…Ī„ÎŽĪ‚ Ī„ÎˇĪ‚ ĪĪÎ¸ÎŧÎšĪƒÎˇĪ‚.", + "setting_video_viewer_original_video_title": "ΑÎŊÎąÎŗÎēÎąĪƒĪ„ÎšÎēÎŽ ÎąÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ ÎąĪ…Î¸ÎĩÎŊĪ„ÎšÎēÎŋĪ Î˛Î¯ÎŊĪ„ÎĩÎŋ", "settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚", "settings_require_restart": "Î•Ī€ÎąÎŊÎĩÎēÎēΚÎŊÎŽĪƒĪ„Îĩ Ī„Îŋ Immich ÎŗÎšÎą ÎŊÎą ÎĩĪ†ÎąĪÎŧΌ΃ÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ", "settings_saved": "Οι ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎąÎŊ", "share": "ΚÎŋΚÎŊÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "share_add_photos": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", - "share_assets_selected": "{} ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą", + "share_assets_selected": "{count} ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą", "share_dialog_preparing": "Î ĪÎŋÎĩĪ„ÎŋΚÎŧÎąĪƒÎ¯Îą...", "shared": "ÎŖÎĩ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ", "shared_album_activities_input_disable": "ΤÎŋ ĪƒĪ‡ĪŒÎģΚÎŋ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", @@ -1618,39 +1646,38 @@ "shared_album_section_people_action_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎąĪ€ÎŋĪ‡ĪŽĪÎˇĪƒÎˇĪ‚/ÎēÎąĪ„ÎŦĪÎŗÎˇĪƒÎˇĪ‚ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "shared_album_section_people_action_leave": "Î‘Ī€ÎŋĪ‡ĪŽĪÎˇĪƒÎˇ Ī‡ĪÎŽĪƒĪ„Îˇ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "shared_album_section_people_action_remove_user": "ÎšÎąĪ„ÎŦĪÎŗÎˇĪƒÎˇ Ī‡ĪÎŽĪƒĪ„Îˇ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", - "shared_album_section_people_title": "ΑΝΘΡΩΠΟΙ", + "shared_album_section_people_title": "ΑΤΟΜΑ", "shared_by": "ÎŖÎĩ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ ÎąĪ€ĪŒ", "shared_by_user": "ÎŖÎĩ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ ÎąĪ€ĪŒ {user}", "shared_by_you": "ÎŖÎĩ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ ÎąĪ€ĪŒ Îĩ΃ÎŦĪ‚", "shared_from_partner": "ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎąĪ€ĪŒ {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} ΜÎĩĪ„ÎąĪ†Îŋ΁΄ΉÎŧέÎŊÎą", "shared_link_app_bar_title": "ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋΚ ÎŖĪÎŊδÎĩ΃ÎŧÎŋΚ", "shared_link_clipboard_copied_massage": "ΑÎŊĪ„ÎšÎŗĪÎŦĪ†ÎˇÎēÎĩ ĪƒĪ„Îŋ Ī€ĪĪŒĪ‡ÎĩÎšĪÎŋ", - "shared_link_clipboard_text": "ÎŖĪÎŊδÎĩ΃ÎŧÎŋĪ‚: {}\nÎšĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚: {}", + "shared_link_clipboard_text": "ÎŖĪÎŊδÎĩ΃ÎŧÎŋĪ‚: {link}\nÎšĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚: {password}", "shared_link_create_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "shared_link_edit_description_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽ Ī„ÎˇĪ‚ ÎēÎŋΚÎŊÎŽĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚", "shared_link_edit_expire_after_option_day": "1 ΡÎŧÎ­ĪÎą", - "shared_link_edit_expire_after_option_days": "{} ΡÎŧÎ­ĪÎĩĪ‚", + "shared_link_edit_expire_after_option_days": "{count} ΡÎŧÎ­ĪÎĩĪ‚", "shared_link_edit_expire_after_option_hour": "1 ĪŽĪÎą", - "shared_link_edit_expire_after_option_hours": "{} ĪŽĪÎĩĪ‚", + "shared_link_edit_expire_after_option_hours": "{count} ĪŽĪÎĩĪ‚", "shared_link_edit_expire_after_option_minute": "1 ÎģÎĩĪ€Ī„ĪŒ", - "shared_link_edit_expire_after_option_minutes": "{} ÎģÎĩ΀΄ÎŦ", - "shared_link_edit_expire_after_option_months": "{} ÎŧÎŽÎŊÎĩĪ‚", - "shared_link_edit_expire_after_option_year": "{} Î­Ī„ÎŋĪ‚", + "shared_link_edit_expire_after_option_minutes": "{count} ÎģÎĩ΀΄ÎŦ", + "shared_link_edit_expire_after_option_months": "{count} ÎŧÎŽÎŊÎĩĪ‚", + "shared_link_edit_expire_after_option_year": "{count} Î­Ī„ÎŋĪ‚", "shared_link_edit_password_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ ÎēÎŋΚÎŊÎŽĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚", "shared_link_edit_submit_button": "ΕÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "shared_link_error_server_url_fetch": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎąÎŊÎŦÎēĪ„ÎˇĪƒÎˇ Ī„ÎŋĪ… URL Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", - "shared_link_expires_day": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ΡÎŧÎ­ĪÎą", - "shared_link_expires_days": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ΡÎŧÎ­ĪÎĩĪ‚", - "shared_link_expires_hour": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ĪŽĪÎą", - "shared_link_expires_hours": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ĪŽĪÎĩĪ‚", - "shared_link_expires_minute": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ÎģÎĩĪ€Ī„ĪŒ", - "shared_link_expires_minutes": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} ÎģÎĩ΀΄ÎŦ", + "shared_link_expires_day": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ΡÎŧÎ­ĪÎą", + "shared_link_expires_days": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ΡÎŧÎ­ĪÎĩĪ‚", + "shared_link_expires_hour": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ĪŽĪÎą", + "shared_link_expires_hours": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ĪŽĪÎĩĪ‚", + "shared_link_expires_minute": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ÎģÎĩĪ€Ī„ĪŒ", + "shared_link_expires_minutes": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} ÎģÎĩ΀΄ÎŦ", "shared_link_expires_never": "Î›ÎŽÎŗÎĩΚ ∞", - "shared_link_expires_second": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩ΀΄Îŋ", - "shared_link_expires_seconds": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩĪ€Ī„Îą", + "shared_link_expires_second": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩ΀΄Îŋ", + "shared_link_expires_seconds": "Î›ÎŽÎŗÎĩΚ ΃Îĩ {count} δÎĩĪ…Ī„Îĩ΁ΌÎģÎĩĪ€Ī„Îą", "shared_link_individual_shared": "ΜÎĩÎŧÎŋÎŊΉÎŧέÎŊÎŋ ÎēÎŋΚÎŊΌ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Ī‰ÎŊ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧΉÎŊ", "shared_link_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "shared_links": "ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋΚ ĪƒĪÎŊδÎĩ΃ÎŧÎŋΚ", @@ -1749,7 +1776,7 @@ "theme_selection": "Î•Ī€ÎšÎģÎŋÎŗÎŽ θέÎŧÎąĪ„ÎŋĪ‚", "theme_selection_description": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒĪ„Îĩ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„Îŋ θέÎŧÎą ΃Îĩ ÎąÎŊÎŋÎšĪ‡Ī„ĪŒ ÎŽ ΃ÎēÎŋĪĪÎŋ ÎŧÎĩ βÎŦĪƒÎˇ Ī„ÎšĪ‚ ΀΁ÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚ ĪƒĪ…ĪƒĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚ Ī„ÎŋĪ… ΀΁ÎŋÎŗĪÎŦÎŧÎŧÎąĪ„ÎŋĪ‚ Ī€ÎĩĪÎšÎŽÎŗÎˇĪƒÎŽĪ‚ ĪƒÎąĪ‚", "theme_setting_asset_list_storage_indicator_title": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ έÎŊδÎĩÎšÎžÎˇĪ‚ ÎąĪ€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ ΃Îĩ Ī€ÎģÎąÎēÎ¯Î´ÎšÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", - "theme_setting_asset_list_tiles_per_row_title": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ÎąÎŊÎŦ ΃ÎĩÎšĪÎŦ ({})", + "theme_setting_asset_list_tiles_per_row_title": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ ÎąÎŊÎŦ ΃ÎĩÎšĪÎŦ ({count})", "theme_setting_colorful_interface_subtitle": "Î•Ī†ÎąĪÎŧĪŒĪƒĪ„Îĩ Î˛ÎąĪƒÎšÎēΌ Ī‡ĪĪŽÎŧÎą ΃Îĩ ÎĩĪ€ÎšĪ†ÎŦÎŊÎĩΚÎĩĪ‚ Ī†ĪŒÎŊĪ„ÎŋĪ….", "theme_setting_colorful_interface_title": "ΠÎŋÎģĪĪ‡ĪĪ‰ÎŧΡ δΚÎĩĪ€ÎąĪ†ÎŽ", "theme_setting_image_viewer_quality_subtitle": "Î ĪÎŋĪƒÎąĪÎŧĪŒĪƒĪ„Îĩ Ī„ÎˇÎŊ Ī€ÎŋÎšĪŒĪ„ÎˇĪ„Îą Ī„ÎŋĪ… ΀΁ÎŋÎŗĪÎŦÎŧÎŧÎąĪ„ÎŋĪ‚ ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎšĪŽÎŊ", @@ -1784,11 +1811,11 @@ "trash_no_results_message": "Οι ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ Ī€ÎŋĪ… Î˛ĪÎ¯ĪƒÎēÎŋÎŊĪ„ÎąÎš ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ θι ÎĩÎŧĪ†ÎąÎŊίÎļÎŋÎŊĪ„ÎąÎš ÎĩÎ´ĪŽ.", "trash_page_delete_all": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΌÎģΉÎŊ", "trash_page_empty_trash_dialog_content": "ΘέÎģÎĩĪ„Îĩ ÎŊÎą ιδÎĩΚÎŦ΃ÎĩĪ„Îĩ Ī„Îą Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŦ ĪƒÎąĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ; Î‘Ī…Ī„ÎŦ Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą θι ÎēÎąĪ„ÎąĪÎŗÎˇÎ¸ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îŋ Immich", - "trash_page_info": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋĪĪÎšĪ†Î¸Îĩί θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎŧÎĩĪ„ÎŦ ÎąĪ€ĪŒ {} ΡÎŧÎ­ĪÎĩĪ‚", + "trash_page_info": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋĪĪÎšĪ†Î¸Îĩί θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎŧÎĩĪ„ÎŦ ÎąĪ€ĪŒ {days} ΡÎŧÎ­ĪÎĩĪ‚", "trash_page_no_assets": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋĪĪÎšĪ†Î¸Îĩί", "trash_page_restore_all": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ΌÎģΉÎŊ", "trash_page_select_assets_btn": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", - "trash_page_title": "ΚÎŦδÎŋĪ‚ Î‘Ī€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ({})", + "trash_page_title": "ΚÎŦδÎŋĪ‚ Î‘Ī€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ({count})", "trashed_items_will_be_permanently_deleted_after": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î˛ĪÎ¯ĪƒÎēÎŋÎŊĪ„ÎąÎš ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎŧÎĩĪ„ÎŦ ÎąĪ€ĪŒ {days, plural, one {# ΡÎŧÎ­ĪÎą} other {# ΡÎŧÎ­ĪÎĩĪ‚}}.", "type": "Î¤ĪĪ€ÎŋĪ‚", "unarchive": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚", @@ -1802,7 +1829,7 @@ "unlink_motion_video": "Î‘Ī€ÎŋĪƒĪ…ÎŊÎ´Î­ĪƒĪ„Îĩ Ī„Îŋ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎēίÎŊÎˇĪƒÎˇĪ‚", "unlink_oauth": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ OAuth", "unlinked_oauth_account": "Ο ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧĪŒĪ‚ OAuth ÎąĪ€ÎŋĪƒĪ…ÎŊδέθΡÎēÎĩ", - "unmute_memories": "Î‘Ī€ÎŋĪƒĪ…ÎŊÎ´Î­ĪƒĪ„Îĩ Ī„ÎšĪ‚ ÎąÎŊÎąÎŧÎŊÎŽĪƒÎĩÎšĪ‚", + "unmute_memories": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ΑÎŊÎąÎŧÎŊÎŽĪƒÎĩΉÎŊ", "unnamed_album": "ΑÎŊĪŽÎŊĪ…ÎŧÎŋ ΆÎģÎŧĪ€ÎŋĪ…Îŧ", "unnamed_album_delete_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ;", "unnamed_share": "ΑÎŊĪŽÎŊĪ…ÎŧΡ ΚÎŋΚÎŊÎŽ Î§ĪÎŽĪƒÎˇ", @@ -1826,11 +1853,10 @@ "upload_status_errors": "ÎŖĪ†ÎŦÎģÎŧÎąĪ„Îą", "upload_status_uploaded": "ΜÎĩĪ„ÎąĪ†ÎŋĪĪ„ĪŽÎ¸ÎˇÎēÎąÎŊ", "upload_success": "Η ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ, ÎąÎŊÎąÎŊÎĩĪŽĪƒĪ„Îĩ Ī„Îˇ ΃ÎĩÎģÎ¯Î´Îą ÎŗÎšÎą ÎŊÎą δÎĩÎ¯Ī„Îĩ Ī„Îą ÎŊέι ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ĪƒĪ„Îŋ Immich ({count})", + "uploading": "ΜÎĩĪ„ÎąĪ†ÎŋĪĪ„ĪŽÎŊÎĩĪ„ÎąÎš", "usage": "Î§ĪÎŽĪƒÎˇ", - "use_current_connection": "use current connection", + "use_current_connection": "Ī‡ĪÎŽĪƒÎˇ Ī„ĪÎ­Ī‡ÎŋĪ…ĪƒÎąĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", "use_custom_date_range": "Î§ĪÎŽĪƒÎˇ ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊÎŋĪ… ÎĩĪĪÎŋĪ…Ī‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎšĪŽÎŊ", "user": "Î§ĪÎŽĪƒĪ„ÎˇĪ‚", "user_id": "ID Î§ĪÎŽĪƒĪ„Îˇ", @@ -1845,14 +1871,14 @@ "users": "Î§ĪÎŽĪƒĪ„ÎĩĪ‚", "utilities": "ΒÎŋÎˇÎ¸ÎˇĪ„ÎšÎēÎŦ ΀΁ÎŋÎŗĪÎŦÎŧÎŧÎąĪ„Îą", "validate": "Î•Ī€ÎšÎēĪĪĪ‰ĪƒÎˇ", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ έÎŊÎą Î­ÎŗÎē΅΁Îŋ URL", "variables": "ΜÎĩĪ„ÎąÎ˛ÎģÎˇĪ„Î­Ī‚", "version": "ΈÎēδÎŋĪƒÎˇ", "version_announcement_closing": "Ο Ī†Î¯ÎģÎŋĪ‚ ΃ÎŋĪ…, Alex", "version_announcement_message": "ΓÎĩΚÎŦ ĪƒÎąĪ‚! Μια ÎŊέι έÎēδÎŋĪƒÎˇ Ī„ÎŋĪ… Immich ÎĩίÎŊιΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ. Î ÎąĪÎąÎēÎąÎģÎŋĪÎŧÎĩ ÎąĪ†ÎšÎĩĪĪŽĪƒĪ„Îĩ ÎģÎ¯ÎŗÎŋ Ī‡ĪĪŒÎŊÎŋ ÎŗÎšÎą ÎŊÎą δΚιβÎŦ΃ÎĩĪ„Îĩ Ī„ÎšĪ‚ ĪƒÎˇÎŧÎĩÎšĪŽĪƒÎĩÎšĪ‚ έÎēδÎŋĪƒÎˇĪ‚ ĪŽĪƒĪ„Îĩ ÎŊÎą βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Ρ ĪĪÎ¸ÎŧÎšĪƒÎˇ ĪƒÎąĪ‚ ÎĩίÎŊιΚ ÎĩÎŊΡÎŧÎĩ΁ΉÎŧέÎŊΡ ÎēιΚ ÎŊÎą ÎąĪ€ÎŋĪ†ĪÎŗÎĩĪ„Îĩ Ī„Ī…Ī‡ĪŒÎŊ ĪƒĪ†ÎŦÎģÎŧÎąĪ„Îą, ÎĩΚδΚÎēÎŦ ÎąÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ Ī„Îŋ WatchTower ÎŽ ÎŋĪ€ÎŋΚÎŋÎ´ÎŽĪ€ÎŋĪ„Îĩ ÎŧÎˇĪ‡ÎąÎŊÎšĪƒÎŧΌ Ī€ÎŋĪ… Î´ÎšÎąĪ‡ÎĩÎšĪÎ¯ÎļÎĩĪ„ÎąÎš ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îą Ī„ÎˇÎŊ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ ÎĩÎŗÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚ Ī„ÎŋĪ… Immich ĪƒÎąĪ‚.", "version_announcement_overlay_release_notes": "ĪƒÎˇÎŧÎĩÎšĪŽĪƒÎĩÎšĪ‚ έÎēδÎŋĪƒÎˇĪ‚", "version_announcement_overlay_text_1": "ΓÎĩΚÎŦ ĪƒÎąĪ‚, Ī…Ī€ÎŦ΁·ÎĩΚ ÎŧΚι ÎŊέι έÎēδÎŋĪƒÎˇ Ī„ÎŋĪ…", - "version_announcement_overlay_text_2": "Ī€ÎąĪÎąÎēÎąÎģĪŽ ÎąĪ†ÎšÎĩĪĪŽĪƒĪ„Îĩ Ī‡ĪĪŒÎŊÎŋ ÎŊÎą ÎĩĪ€ÎšĪƒÎēÎĩĪ†Î¸ÎĩÎ¯Ī„Îĩ Ī„Îŋ", + "version_announcement_overlay_text_2": "Ī€ÎąĪÎąÎēÎąÎģĪŽ ÎąĪ†ÎšÎĩĪĪŽĪƒĪ„Îĩ Ī‡ĪĪŒÎŊÎŋ ÎŊÎą ÎĩĪ€ÎšĪƒÎēÎĩĪ†Î¸ÎĩÎ¯Ī„Îĩ Ī„Îŋ ", "version_announcement_overlay_text_3": " ÎēιΚ βÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Ī„Îŋ docker-compose ÎēιΚ Ī„Îŋ .env ĪƒÎąĪ‚ ÎĩίÎŊιΚ ÎĩÎŊΡÎŧÎĩ΁ΉÎŧέÎŊΡ ÎŗÎšÎą Ī„ÎˇÎŊ ÎąĪ€ÎŋĪ†Ī…ÎŗÎŽ Ī„Ī…Ī‡ĪŒÎŊ ÎĩĪƒĪ†ÎąÎģÎŧέÎŊΉÎŊ δΚιÎŧÎŋĪĪ†ĪŽĪƒÎĩΉÎŊ, ÎĩΚδΚÎēÎŦ ÎĩÎŦÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ Ī„Îŋ WatchTower ÎŽ ÎŋĪ€ÎŋΚÎŋÎŊÎ´ÎŽĪ€ÎŋĪ„Îĩ ÎŧÎˇĪ‡ÎąÎŊÎšĪƒÎŧΌ Ī€ÎŋĪ… ·ÎĩÎšĪÎ¯ÎļÎĩĪ„ÎąÎš Ī„ÎˇÎŊ ÎąĪ…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒÎąĪ‚.", "version_announcement_overlay_title": "Î”ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ ÎŊέι έÎēδÎŋĪƒÎˇ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ 🎉", "version_history": "Î™ĪƒĪ„ÎŋĪÎšÎēΌ ΕÎēÎ´ĪŒĪƒÎĩΉÎŊ", @@ -1883,11 +1909,12 @@ "week": "ΕβδÎŋÎŧÎŦδι", "welcome": "ΚαÎģĪ‰ĪƒÎŋĪÎ¯ĪƒÎąĪ„Îĩ", "welcome_to_immich": "ΚαÎģĪ‰ĪƒÎŋĪÎ¯ĪƒÎąĪ„Îĩ ĪƒĪ„Îŋ Ιmmich", - "wifi_name": "WiFi Name", + "wifi_name": "ΌÎŊÎŋÎŧÎą Wi-Fi", + "wrong_pin_code": "ΛÎŦθÎŋĪ‚ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ PIN", "year": "ÎˆĪ„ÎŋĪ‚", "years_ago": "Ī€ĪÎšÎŊ ÎąĪ€ĪŒ {years, plural, one {# Ī‡ĪĪŒÎŊÎŋ} other {# Ī‡ĪĪŒÎŊΚι}}", "yes": "Ναι", "you_dont_have_any_shared_links": "ΔÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ…Ī‚ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…Ī‚", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "ΤÎŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… Wi-Fi ĪƒÎąĪ‚", "zoom_image": "ΖÎŋĪ…Îŧ ΕιÎēΌÎŊÎąĪ‚" } diff --git a/i18n/en.json b/i18n/en.json index 3b52c2019e..56e38cf816 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -26,6 +26,7 @@ "add_to_album": "Add to album", "add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_locked_folder": "Add to locked folder", "add_to_shared_album": "Add to shared album", "add_url": "Add URL", "added_to_archive": "Added to archive", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Are you sure you want to disable all login methods? Login will be completely disabled.", "authentication_settings_reenable": "To re-enable, use a Server Command.", "background_task_job": "Background Tasks", - "backup_database": "Backup Database", - "backup_database_enable_description": "Enable database backups", - "backup_keep_last_amount": "Amount of previous backups to keep", - "backup_settings": "Backup Settings", - "backup_settings_description": "Manage database backup settings", + "backup_database": "Create Database Dump", + "backup_database_enable_description": "Enable database dumps", + "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_settings": "Database Dump Settings", + "backup_settings_description": "Manage database dump settings. Note: These jobs are not monitored and you will not be notified of failure.", "check_all": "Check All", "cleanup": "Cleanup", "cleared_jobs": "Cleared jobs for: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "To confirm, type \"{email}\" below", "confirm_reprocess_all_faces": "Are you sure you want to reprocess all faces? This will also clear named people.", "confirm_user_password_reset": "Are you sure you want to reset {user}'s password?", + "confirm_user_pin_code_reset": "Are you sure you want to reset {user}'s PIN code?", "create_job": "Create job", "cron_expression": "Cron expression", "cron_expression_description": "Set the scanning interval using the cron format. For more information please refer to e.g. Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "Auto register", "oauth_auto_register_description": "Automatically register new users after signing in with OAuth", "oauth_button_text": "Button text", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Client Secret", + "oauth_client_secret_description": "Required if PKCE (Proof Key for Code Exchange) is not supported by the OAuth provider", "oauth_enable_description": "Login with OAuth", - "oauth_issuer_url": "Issuer URL", "oauth_mobile_redirect_uri": "Mobile redirect URI", "oauth_mobile_redirect_uri_override": "Mobile redirect URI override", "oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like '{callback}'", - "oauth_profile_signing_algorithm": "Profile signing algorithm", - "oauth_profile_signing_algorithm_description": "Algorithm used to sign the user profile.", - "oauth_scope": "Scope", "oauth_settings": "OAuth", "oauth_settings_description": "Manage OAuth login settings", "oauth_settings_more_details": "For more details about this feature, refer to the docs.", - "oauth_signing_algorithm": "Signing algorithm", "oauth_storage_label_claim": "Storage label claim", "oauth_storage_label_claim_description": "Automatically set the user's storage label to the value of this claim.", "oauth_storage_quota_claim": "Storage quota claim", "oauth_storage_quota_claim_description": "Automatically set the user's storage quota to the value of this claim.", "oauth_storage_quota_default": "Default storage quota (GiB)", "oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota).", + "oauth_timeout": "Request Timeout", + "oauth_timeout_description": "Timeout for requests in milliseconds", "offline_paths": "Offline Paths", "offline_paths_description": "These results may be due to manual deletion of files that are not part of an external library.", "password_enable_description": "Login with email and password", @@ -270,7 +268,7 @@ "template_email_update_album": "Update Album Template", "template_email_welcome": "Welcome email template", "template_settings": "Notification Templates", - "template_settings_description": "Manage custom templates for notifications.", + "template_settings_description": "Manage custom templates for notifications", "theme_custom_css_settings": "Custom CSS", "theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.", "theme_settings": "Theme Settings", @@ -303,10 +301,9 @@ "transcoding_encoding_options": "Encoding Options", "transcoding_encoding_options_description": "Set codecs, resolution, quality and other options for the encoded videos", "transcoding_hardware_acceleration": "Hardware Acceleration", - "transcoding_hardware_acceleration_description": "Experimental; much faster, but will have lower quality at the same bitrate", + "transcoding_hardware_acceleration_description": "Experimental: faster transcoding but may reduce quality at same bitrate", "transcoding_hardware_decoding": "Hardware decoding", "transcoding_hardware_decoding_setting_description": "Enables end-to-end acceleration instead of only accelerating encoding. May not work on all videos.", - "transcoding_hevc_codec": "HEVC codec", "transcoding_max_b_frames": "Maximum B-frames", "transcoding_max_b_frames_description": "Higher values improve compression efficiency, but slow down encoding. May not be compatible with hardware acceleration on older devices. 0 disables B-frames, while -1 sets this value automatically.", "transcoding_max_bitrate": "Maximum bitrate", @@ -352,6 +349,7 @@ "user_delete_delay_settings_description": "Number of days after removal to permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.", "user_delete_immediately": "{user}'s account and assets will be queued for permanent deletion immediately.", "user_delete_immediately_checkbox": "Queue user and assets for immediate deletion", + "user_details": "User Details", "user_management": "User Management", "user_password_has_been_reset": "The user's password has been reset:", "user_password_reset_description": "Please provide the temporary password to the user and inform them they will need to change the password at their next login.", @@ -373,7 +371,7 @@ "advanced": "Advanced", "advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter", - "advanced_settings_log_level_title": "Log level: {}", + "advanced_settings_log_level_title": "Log level: {level}", "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", "advanced_settings_prefer_remote_title": "Prefer remote images", "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", @@ -404,9 +402,9 @@ "album_remove_user_confirmation": "Are you sure you want to remove {user}?", "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.", "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", + "album_thumbnail_card_items": "{count} items", "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", + "album_thumbnail_shared_by": "Shared by {user}", "album_updated": "Album updated", "album_updated_setting_description": "Receive an email notification when a shared album has new assets", "album_user_left": "Left {album}", @@ -444,7 +442,7 @@ "archive": "Archive", "archive_or_unarchive_photo": "Archive or unarchive photo", "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", + "archive_page_title": "Archive ({count})", "archive_size": "Archive size", "archive_size_description": "Configure the archive size for downloads (in GiB)", "archived": "Archived", @@ -481,18 +479,18 @@ "assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album", "assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} asset(s) deleted permanently", + "assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash", "assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{count} asset(s) removed permanently from your device", "assets_restore_confirmation": "Are you sure you want to restore all your trashed assets? You cannot undo this action! Note that any offline assets cannot be restored this way.", "assets_restored_count": "Restored {count, plural, one {# asset} other {# assets}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{count} asset(s) restored successfully", + "assets_trashed": "{count} asset(s) trashed", "assets_trashed_count": "Trashed {count, plural, one {# asset} other {# assets}}", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{count} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} already part of the album", "authorized_devices": "Authorized Devices", "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", @@ -501,7 +499,7 @@ "back_close_deselect": "Back, close, or deselect", "background_location_permission": "Background location permission", "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", + "backup_album_selection_page_albums_device": "Albums on device ({count})", "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", "backup_album_selection_page_select_albums": "Select albums", @@ -510,11 +508,11 @@ "backup_all": "All", "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", + "backup_background_service_current_upload_notification": "Uploading {filename}", "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", "backup_background_service_error_title": "Backup error", "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", + "backup_background_service_upload_failure_notification": "Failed to upload {filename}", "backup_controller_page_albums": "Backup Albums", "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", @@ -525,22 +523,22 @@ "backup_controller_page_background_battery_info_title": "Battery optimizations", "backup_controller_page_background_charging": "Only while charging", "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", + "backup_controller_page_background_delay": "Delay new assets backup: {duration}", "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", "backup_controller_page_background_is_off": "Automatic background backup is off", "backup_controller_page_background_is_on": "Automatic background backup is on", "backup_controller_page_background_turn_off": "Turn off background service", "backup_controller_page_background_turn_on": "Turn on background service", - "backup_controller_page_background_wifi": "Only on WiFi", + "backup_controller_page_background_wifi": "Only on Wi-Fi", "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selected: ", "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", + "backup_controller_page_created": "Created on: {date}", "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Failed ({count})", + "backup_controller_page_filename": "File name: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Backup Information", "backup_controller_page_none_selected": "None selected", "backup_controller_page_remainder": "Remainder", @@ -549,7 +547,7 @@ "backup_controller_page_start_backup": "Start Backup", "backup_controller_page_status_off": "Automatic foreground backup is off", "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", + "backup_controller_page_storage_format": "{used} of {total} used", "backup_controller_page_to_backup": "Albums to be backed up", "backup_controller_page_total_sub": "All unique photos and videos from selected albums", "backup_controller_page_turn_off": "Turn off foreground backup", @@ -564,6 +562,10 @@ "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Backward", + "biometric_auth_enabled": "Biometric authentication enabled", + "biometric_locked_out": "You are locked out of biometric authentication", + "biometric_no_options": "No biometric options available", + "biometric_not_available": "Biometric authentication is not available on this device", "birthdate_saved": "Date of birth saved successfully", "birthdate_set_description": "Date of birth is used to calculate the age of this person at the time of a photo.", "blurred_background": "Blurred background", @@ -574,21 +576,21 @@ "bulk_keep_duplicates_confirmation": "Are you sure you want to keep {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will resolve all duplicate groups without deleting anything.", "bulk_trash_duplicates_confirmation": "Are you sure you want to bulk trash {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will keep the largest asset of each group and trash all other duplicates.", "buy": "Purchase Immich", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", + "cache_settings_album_thumbnails": "Library page thumbnails ({count} assets)", "cache_settings_clear_cache_button": "Clear cache", "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", "cache_settings_duplicated_assets_clear_button": "CLEAR", "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", + "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})", + "cache_settings_image_cache_size": "Image cache size ({count} assets)", "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", + "cache_settings_statistics_assets": "{count} assets ({size})", "cache_settings_statistics_full": "Full images", "cache_settings_statistics_shared": "Shared album thumbnails", "cache_settings_statistics_thumbnail": "Thumbnails", "cache_settings_statistics_title": "Cache usage", "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", + "cache_settings_thumbnail_size": "Thumbnail cache size ({count} assets)", "cache_settings_tile_subtitle": "Control the local storage behaviour", "cache_settings_tile_title": "Local Storage", "cache_settings_title": "Caching Settings", @@ -601,12 +603,14 @@ "cannot_merge_people": "Cannot merge people", "cannot_undo_this_action": "You cannot undo this action!", "cannot_update_the_description": "Cannot update the description", + "cast": "Cast", "change_date": "Change date", + "change_description": "Change description", "change_display_order": "Change display order", "change_expiration_time": "Change expiration time", "change_location": "Change location", "change_name": "Change name", - "change_name_successfully": "Change name successfully", + "change_name_successfully": "Changed name successfully", "change_password": "Change Password", "change_password_description": "This is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", "change_password_form_confirm_password": "Confirm Password", @@ -614,6 +618,7 @@ "change_password_form_new_password": "New Password", "change_password_form_password_mismatch": "Passwords do not match", "change_password_form_reenter_new_password": "Re-enter New Password", + "change_pin_code": "Change PIN code", "change_your_password": "Change your password", "changed_visibility_successfully": "Changed visibility successfully", "check_all": "Check All", @@ -654,11 +659,15 @@ "confirm_delete_face": "Are you sure you want to delete {name} face from the asset?", "confirm_delete_shared_link": "Are you sure you want to delete this shared link?", "confirm_keep_this_delete_others": "All other assets in the stack will be deleted except for this asset. Are you sure you want to continue?", + "confirm_new_pin_code": "Confirm new PIN code", "confirm_password": "Confirm password", + "confirm_tag_face": "Do you want to tag this face as {name}?", + "confirm_tag_face_unnamed": "Do you want to tag this face?", + "connected_to": "Connected to", "contain": "Contain", "context": "Context", "continue": "Continue", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", + "control_bottom_app_bar_album_info_shared": "{count} items ¡ Shared", "control_bottom_app_bar_create_new_album": "Create new album", "control_bottom_app_bar_delete_from_immich": "Delete from Immich", "control_bottom_app_bar_delete_from_local": "Delete from device", @@ -696,9 +705,11 @@ "create_tag_description": "Create a new tag. For nested tags, please enter the full path of the tag including forward slashes.", "create_user": "Create user", "created": "Created", + "created_at": "Created", "crop": "Crop", "curated_object_page_title": "Things", "current_device": "Current device", + "current_pin_code": "Current PIN code", "current_server_address": "Current server address", "custom_locale": "Custom Locale", "custom_locale_description": "Format dates and numbers based on the language and the region", @@ -767,7 +778,7 @@ "download_enqueue": "Download enqueued", "download_error": "Download Error", "download_failed": "Download failed", - "download_filename": "file: {}", + "download_filename": "file: {filename}", "download_finished": "Download finished", "download_include_embedded_motion_videos": "Embedded videos", "download_include_embedded_motion_videos_description": "Include videos embedded in motion photos as a separate file", @@ -791,6 +802,8 @@ "edit_avatar": "Edit avatar", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", + "edit_description": "Edit description", + "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", "edit_faces": "Edit faces", "edit_import_path": "Edit import path", @@ -811,19 +824,24 @@ "editor_crop_tool_h2_aspect_ratios": "Aspect ratios", "editor_crop_tool_h2_rotation": "Rotation", "email": "Email", + "email_notifications": "Email notifications", "empty_folder": "This folder is empty", "empty_trash": "Empty trash", "empty_trash_confirmation": "Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.\nYou cannot undo this action!", "enable": "Enable", + "enable_biometric_auth_description": "Enter your PIN code to enable biometric authentication", "enabled": "Enabled", "end_date": "End date", "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enter_wifi_name": "Enter Wi-Fi name", + "enter_your_pin_code": "Enter your PIN code", + "enter_your_pin_code_subtitle": "Enter your PIN code to access the locked folder", "error": "Error", "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Error deleting face from asset", "error_loading_image": "Error loading image", - "error_saving_image": "Error: {}", + "error_saving_image": "Error: {error}", + "error_tag_face_bounding_box": "Error tagging face - cannot get bounding box coordinates", "error_title": "Error - Something went wrong", "errors": { "cannot_navigate_next_asset": "Cannot navigate to the next asset", @@ -853,10 +871,12 @@ "failed_to_keep_this_delete_others": "Failed to keep this asset and delete the other assets", "failed_to_load_asset": "Failed to load asset", "failed_to_load_assets": "Failed to load assets", + "failed_to_load_notifications": "Failed to load notifications", "failed_to_load_people": "Failed to load people", "failed_to_remove_product_key": "Failed to remove product key", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "Failed to update notification status", "import_path_already_exists": "This import path already exists.", "incorrect_email_or_password": "Incorrect email or password", "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation", @@ -874,6 +894,7 @@ "unable_to_archive_unarchive": "Unable to {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "Unable to change the album user's role", "unable_to_change_date": "Unable to change date", + "unable_to_change_description": "Unable to change description", "unable_to_change_favorite": "Unable to change favorite for asset", "unable_to_change_location": "Unable to change location", "unable_to_change_password": "Unable to change password", @@ -911,6 +932,7 @@ "unable_to_log_out_all_devices": "Unable to log out all devices", "unable_to_log_out_device": "Unable to log out device", "unable_to_login_with_oauth": "Unable to login with OAuth", + "unable_to_move_to_locked_folder": "Unable to move to locked folder", "unable_to_play_video": "Unable to play video", "unable_to_reassign_assets_existing_person": "Unable to reassign assets to {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "Unable to reassign assets to a new person", @@ -924,6 +946,7 @@ "unable_to_remove_reaction": "Unable to remove reaction", "unable_to_repair_items": "Unable to repair items", "unable_to_reset_password": "Unable to reset password", + "unable_to_reset_pin_code": "Unable to reset PIN code", "unable_to_resolve_duplicate": "Unable to resolve duplicate", "unable_to_restore_assets": "Unable to restore assets", "unable_to_restore_trash": "Unable to restore trash", @@ -957,10 +980,9 @@ "exif_bottom_sheet_location": "LOCATION", "exif_bottom_sheet_people": "PEOPLE", "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age_months": "Age {months} months", + "exif_bottom_sheet_person_age_year_months": "Age 1 year, {months} months", + "exif_bottom_sheet_person_age_years": "Age {years}", "exit_slideshow": "Exit Slideshow", "expand_all": "Expand all", "experimental_settings_new_asset_list_subtitle": "Work in progress", @@ -978,9 +1000,10 @@ "external": "External", "external_libraries": "External Libraries", "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network_sheet_info": "When not on the preferred Wi-Fi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "Unassigned", "failed": "Failed", + "failed_to_authenticate": "Failed to authenticate", "failed_to_load_assets": "Failed to load assets", "failed_to_load_folder": "Failed to load folder", "favorite": "Favorite", @@ -996,6 +1019,7 @@ "filetype": "Filetype", "filter": "Filter", "filter_people": "Filter people", + "filter_places": "Filter places", "find_them_fast": "Find them fast by name with search", "fix_incorrect_match": "Fix incorrect match", "folder": "Folder", @@ -1045,10 +1069,13 @@ "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album so that the timeline can populate photos and videos in it", + "home_page_locked_error_local": "Can not move local assets to locked folder, skipping", + "home_page_locked_error_partner": "Can not move partner assets to locked folder, skipping", "home_page_share_err_local": "Can not share local assets via link, skipping", "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "host": "Host", "hour": "Hour", + "id": "ID", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Image", @@ -1124,12 +1151,14 @@ "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current Wi-Fi network's name", "location_picker_choose_on_map": "Choose on map", "location_picker_latitude_error": "Enter a valid latitude", "location_picker_latitude_hint": "Enter your latitude here", "location_picker_longitude_error": "Enter a valid longitude", "location_picker_longitude_hint": "Enter your longitude here", + "lock": "Lock", + "locked_folder": "Locked folder", "log_out": "Log out", "log_out_all_devices": "Log Out All Devices", "logged_out_all_devices": "Logged out all devices", @@ -1174,8 +1203,8 @@ "manage_your_devices": "Manage your logged-in devices", "manage_your_oauth_connection": "Manage your OAuth connection", "map": "Map", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", + "map_assets_in_bound": "{count} photo", + "map_assets_in_bounds": "{count} photos", "map_cannot_get_user_location": "Cannot get user's location", "map_location_dialog_yes": "Yes", "map_location_picker_page_use_location": "Use this location", @@ -1189,15 +1218,18 @@ "map_settings": "Map settings", "map_settings_dark_mode": "Dark mode", "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", + "map_settings_date_range_option_days": "Past {days} days", "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", + "map_settings_date_range_option_years": "Past {years} years", "map_settings_dialog_title": "Map Settings", "map_settings_include_show_archived": "Include Archived", "map_settings_include_show_partners": "Include Partners", "map_settings_only_show_favorites": "Show Favorite Only", "map_settings_theme_settings": "Map Theme", "map_zoom_to_see_photos": "Zoom out to see photos", + "mark_all_as_read": "Mark all as read", + "mark_as_read": "Mark as read", + "marked_all_as_read": "Marked all as read", "matches": "Matches", "media_type": "Media type", "memories": "Memories", @@ -1206,8 +1238,6 @@ "memories_setting_description": "Manage what you see in your memories", "memories_start_over": "Start Over", "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Memory", "memory_lane_title": "Memory Lane {title}", "menu": "Menu", @@ -1224,6 +1254,12 @@ "month": "Month", "monthly_title_text_date_format": "MMMM y", "more": "More", + "move": "Move", + "move_off_locked_folder": "Move out of locked folder", + "move_to_locked_folder": "Move to locked folder", + "move_to_locked_folder_confirmation": "These photos and video will be removed from all albums, and only viewable from the locked folder", + "moved_to_archive": "Moved {count, plural, one {# asset} other {# assets}} to archive", + "moved_to_library": "Moved {count, plural, one {# asset} other {# assets}} to library", "moved_to_trash": "Moved to trash", "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", @@ -1238,6 +1274,8 @@ "new_api_key": "New API Key", "new_password": "New password", "new_person": "New person", + "new_pin_code": "New PIN code", + "new_pin_code_subtitle": "This is your first time accessing the locked folder. Create a PIN code to securely access this page", "new_user_created": "New user created", "new_version_available": "NEW VERSION AVAILABLE", "newest_first": "Newest first", @@ -1255,7 +1293,10 @@ "no_explore_results_message": "Upload more photos to explore your collection.", "no_favorites_message": "Add favorites to quickly find your best pictures and videos", "no_libraries_message": "Create an external library to view your photos and videos", + "no_locked_photos_message": "Photos and videos in the locked folder are hidden and won't show up as you browse or search your library.", "no_name": "No Name", + "no_notifications": "No notifications", + "no_people_found": "No matching people found", "no_places": "No places", "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", @@ -1264,6 +1305,7 @@ "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", "notes": "Notes", + "nothing_here_yet": "Nothing here yet", "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", "notification_permission_list_tile_content": "Grant permission to enable notifications.", "notification_permission_list_tile_enable_button": "Enable Notifications", @@ -1310,7 +1352,7 @@ "partner_page_partner_add_failed": "Failed to add partner", "partner_page_select_partner": "Select partner", "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", + "partner_page_stop_sharing_content": "{partner} will no longer be able to access your photos.", "partner_sharing": "Partner Sharing", "partners": "Partners", "password": "Password", @@ -1339,6 +1381,8 @@ "permanently_delete_assets_prompt": "Are you sure you want to permanently delete {count, plural, one {this asset?} other {these # assets?}} This will also remove {count, plural, one {it from its} other {them from their}} album(s).", "permanently_deleted_asset": "Permanently deleted asset", "permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}", + "permission": "Permission", + "permission_empty": "Your permission shouldn't be empty", "permission_onboarding_back": "Back", "permission_onboarding_continue_anyway": "Continue anyway", "permission_onboarding_get_started": "Get started", @@ -1356,6 +1400,10 @@ "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos from previous years", "pick_a_location": "Pick a location", + "pin_code_changed_successfully": "Successfully changed PIN code", + "pin_code_reset_successfully": "Successfully reset PIN code", + "pin_code_setup_successfully": "Successfully setup a PIN code", + "pin_verification": "PIN code verification", "place": "Place", "places": "Places", "places_count": "{count, plural, one {{count, number} Place} other {{count, number} Places}}", @@ -1363,6 +1411,7 @@ "play_memories": "Play memories", "play_motion_photo": "Play Motion Photo", "play_or_pause_video": "Play or pause video", + "please_auth_to_access": "Please authenticate to access", "port": "Port", "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "Preferences", @@ -1370,9 +1419,13 @@ "preview": "Preview", "previous": "Previous", "previous_memory": "Previous memory", - "previous_or_next_photo": "Previous or next photo", + "previous_or_next_day": "Day forward/back", + "previous_or_next_month": "Month forward/back", + "previous_or_next_photo": "Photo forward/back", + "previous_or_next_year": "Year forward/back", "primary": "Primary", "privacy": "Privacy", + "profile": "Profile", "profile_drawer_app_logs": "Logs", "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", @@ -1386,7 +1439,7 @@ "public_share": "Public Share", "purchase_account_info": "Supporter", "purchase_activated_subtitle": "Thank you for supporting Immich and open-source software", - "purchase_activated_time": "Activated on {date, date}", + "purchase_activated_time": "Activated on {date}", "purchase_activated_title": "Your key has been successfully activated", "purchase_button_activate": "Activate", "purchase_button_buy": "Buy", @@ -1431,6 +1484,8 @@ "recent_searches": "Recent searches", "recently_added": "Recently added", "recently_added_page_title": "Recently Added", + "recently_taken": "Recently taken", + "recently_taken_page_title": "Recently Taken", "refresh": "Refresh", "refresh_encoded_videos": "Refresh encoded videos", "refresh_faces": "Refresh faces", @@ -1450,6 +1505,8 @@ "remove_deleted_assets": "Remove Deleted Assets", "remove_from_album": "Remove from album", "remove_from_favorites": "Remove from favorites", + "remove_from_locked_folder": "Remove from locked folder", + "remove_from_locked_folder_confirmation": "Are you sure you want to move these photos and videos out of the locked folder? They will be visible in your library.", "remove_from_shared_link": "Remove from shared link", "remove_memory": "Remove memory", "remove_photo_from_memory": "Remove photo from this memory", @@ -1473,6 +1530,7 @@ "reset": "Reset", "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", + "reset_pin_code": "Reset PIN code", "reset_to_default": "Reset to default", "resolve_duplicates": "Resolve duplicates", "resolved_all_duplicates": "Resolved all duplicates", @@ -1565,6 +1623,7 @@ "select_keep_all": "Select keep all", "select_library_owner": "Select library owner", "select_new_face": "Select new face", + "select_person_to_tag": "Select a person to tag", "select_photos": "Select photos", "select_trash_all": "Select trash all", "select_user_for_sharing_page_err_album": "Failed to create album", @@ -1595,12 +1654,12 @@ "setting_languages_apply": "Apply", "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", + "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {duration}", + "setting_notifications_notify_hours": "{count} hours", "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", + "setting_notifications_notify_minutes": "{count} minutes", "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", + "setting_notifications_notify_seconds": "{count} seconds", "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", "setting_notifications_single_progress_title": "Show background backup detail progress", "setting_notifications_subtitle": "Adjust your notification preferences", @@ -1612,10 +1671,12 @@ "settings": "Settings", "settings_require_restart": "Please restart Immich to apply this setting", "settings_saved": "Settings saved", + "setup_pin_code": "Setup a PIN code", "share": "Share", "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", + "share_assets_selected": "{count} selected", "share_dialog_preparing": "Preparing...", + "share_link": "Share Link", "shared": "Shared", "shared_album_activities_input_disable": "Comment is disabled", "shared_album_activity_remove_content": "Do you want to delete this activity?", @@ -1628,32 +1689,32 @@ "shared_by_user": "Shared by {user}", "shared_by_you": "Shared by you", "shared_from_partner": "Photos from {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Uploaded", "shared_link_app_bar_title": "Shared Links", "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", + "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Error while creating shared link", "shared_link_edit_description_hint": "Enter the share description", "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", + "shared_link_edit_expire_after_option_days": "{count} days", "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", + "shared_link_edit_expire_after_option_hours": "{count} hours", "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", + "shared_link_edit_expire_after_option_minutes": "{count} minutes", + "shared_link_edit_expire_after_option_months": "{count} months", + "shared_link_edit_expire_after_option_year": "{count} year", "shared_link_edit_password_hint": "Enter the share password", "shared_link_edit_submit_button": "Update link", "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", + "shared_link_expires_day": "Expires in {count} day", + "shared_link_expires_days": "Expires in {count} days", + "shared_link_expires_hour": "Expires in {count} hour", + "shared_link_expires_hours": "Expires in {count} hours", + "shared_link_expires_minute": "Expires in {count} minute", + "shared_link_expires_minutes": "Expires in {count} minutes", "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", + "shared_link_expires_second": "Expires in {count} second", + "shared_link_expires_seconds": "Expires in {count} seconds", "shared_link_individual_shared": "Individual shared", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Manage Shared links", @@ -1728,6 +1789,7 @@ "stop_sharing_photos_with_user": "Stop sharing your photos with this user", "storage": "Storage space", "storage_label": "Storage label", + "storage_quota": "Storage Quota", "storage_usage": "{used} of {available} used", "submit": "Submit", "suggestions": "Suggestions", @@ -1754,7 +1816,7 @@ "theme_selection": "Theme selection", "theme_selection_description": "Automatically set the theme to light or dark based on your browser's system preference", "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", + "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({count})", "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", "theme_setting_colorful_interface_title": "Colorful interface", "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", @@ -1789,13 +1851,15 @@ "trash_no_results_message": "Trashed photos and videos will show up here.", "trash_page_delete_all": "Delete All", "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", + "trash_page_info": "Trashed items will be permanently deleted after {days} days", "trash_page_no_assets": "No trashed assets", "trash_page_restore_all": "Restore All", "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", + "trash_page_title": "Trash ({count})", "trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.", "type": "Type", + "unable_to_change_pin_code": "Unable to change PIN code", + "unable_to_setup_pin_code": "Unable to setup PIN code", "unarchive": "Unarchive", "unarchived_count": "{count, plural, other {Unarchived #}}", "unfavorite": "Unfavorite", @@ -1819,6 +1883,7 @@ "untracked_files": "Untracked files", "untracked_files_decription": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug", "up_next": "Up next", + "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", "upload_concurrency": "Upload concurrency", @@ -1831,15 +1896,19 @@ "upload_status_errors": "Errors", "upload_status_uploaded": "Uploaded", "upload_success": "Upload success, refresh the page to see new upload assets.", - "upload_to_immich": "Upload to Immich ({})", + "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", "url": "URL", "usage": "Usage", + "use_biometric": "Use biometric", "use_current_connection": "use current connection", "use_custom_date_range": "Use custom date range instead", "user": "User", + "user_has_been_deleted": "This user has been deleted.", "user_id": "User ID", "user_liked": "{user} liked {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_pin_code_settings": "PIN Code", + "user_pin_code_settings_description": "Manage your PIN code", "user_purchase_settings": "Purchase", "user_purchase_settings_description": "Manage your purchase", "user_role_set": "Set {user} as {role}", @@ -1879,6 +1948,7 @@ "view_previous_asset": "View previous asset", "view_qr_code": "View QR code", "view_stack": "View Stack", + "view_user": "View User", "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", "viewer_unstack": "Un-Stack", @@ -1888,11 +1958,12 @@ "week": "Week", "welcome": "Welcome", "welcome_to_immich": "Welcome to Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Wi-Fi Name", + "wrong_pin_code": "Wrong PIN code", "year": "Year", "years_ago": "{years, plural, one {# year} other {# years}} ago", "yes": "Yes", "you_dont_have_any_shared_links": "You don't have any shared links", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Your Wi-Fi name", "zoom_image": "Zoom Image" } diff --git a/i18n/es.json b/i18n/es.json index 0fe78eb66f..13d242f9f8 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -14,7 +14,7 @@ "add_a_location": "Agregar ubicaciÃŗn", "add_a_name": "Agregar nombre", "add_a_title": "Agregar título", - "add_endpoint": "Add endpoint", + "add_endpoint": "AÃąadir endpoint", "add_exclusion_pattern": "Agregar patrÃŗn de exclusiÃŗn", "add_import_path": "Agregar ruta de importaciÃŗn", "add_location": "Agregar ubicaciÃŗn", @@ -26,6 +26,7 @@ "add_to_album": "Incluir en ÃĄlbum", "add_to_album_bottom_sheet_added": "Agregado a {album}", "add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}", + "add_to_locked_folder": "AÃąadir a carpeta bloqueada", "add_to_shared_album": "Incluir en ÃĄlbum compartido", "add_url": "AÃąadir URL", "added_to_archive": "Agregado al Archivado", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "ÂŋEstÃĄs seguro de que deseas desactivar todos los mÊtodos de inicio de sesiÃŗn? Esto desactivarÃĄ por completo el inicio de sesiÃŗn.", "authentication_settings_reenable": "Para reactivarlo, utiliza un Comando del servidor.", "background_task_job": "Tareas en segundo plano", - "backup_database": "Respaldar base de datos", - "backup_database_enable_description": "Activar respaldo de base de datos", - "backup_keep_last_amount": "Cantidad de respaldos previos a mantener", - "backup_settings": "Ajustes de respaldo", - "backup_settings_description": "Administrar configuraciÃŗn de respaldo de base de datos", + "backup_database": "Crear volcado de base de datos", + "backup_database_enable_description": "Activar volcado de base de datos", + "backup_keep_last_amount": "Cantidad de volcados previos a mantener", + "backup_settings": "Ajustes de volcado de base de datos", + "backup_settings_description": "Administrar configuraciÃŗn de volcado de base de datos. Nota: estas tareas no estÃĄn monitorizadas y no se notificarÃĄn los fallos.", "check_all": "Verificar todo", "cleanup": "Limpieza", "cleared_jobs": "Trabajos borrados para: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Para confirmar, escribe \"{email}\" a continuaciÃŗn", "confirm_reprocess_all_faces": "ÂŋEstÃĄs seguro de que deseas reprocesar todas las caras? Esto borrarÃĄ a todas las personas que nombraste.", "confirm_user_password_reset": "ÂŋEstÃĄs seguro de que quieres restablecer la contraseÃąa de {user}?", + "confirm_user_pin_code_reset": "EstÃĄ seguro de que quiere restablecer el PIN de {user}?", "create_job": "Crear trabajo", "cron_expression": "ExpresiÃŗn CRON", "cron_expression_description": "Establece el intervalo de escaneo utilizando el formato CRON. Para mÃĄs informaciÃŗn puedes consultar, por ejemplo, Crontab Guru", @@ -91,9 +93,9 @@ "image_thumbnail_quality_description": "Calidad de miniatura de 1 a 100. Es mejor cuanto mÃĄs alto es el valor pero genera archivos mÃĄs grandes y puede reducir la capacidad de respuesta de la aplicaciÃŗn.", "image_thumbnail_title": "Ajustes de las miniaturas", "job_concurrency": "{job}: Procesos simultÃĄneos", - "job_created": "Trabajo creado", + "job_created": "Tarea creada", "job_not_concurrency_safe": "Esta tarea no es segura para la simultaneidad.", - "job_settings": "ConfiguraciÃŗn tareas", + "job_settings": "ConfiguraciÃŗn de tareas", "job_settings_description": "Administrar tareas simultÃĄneas", "job_status": "Estado de la tarea", "jobs_delayed": "{jobCount, plural, one {# retrasado} other {# retrasados}}", @@ -169,7 +171,7 @@ "migration_job_description": "Migrar miniaturas de archivos y caras a la estructura de carpetas mÃĄs reciente", "no_paths_added": "No se han aÃąadido carpetas", "no_pattern_added": "No se han aÃąadido patrones", - "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamient a un elemento anteriormente cargado, lanza el", + "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", "note_cannot_be_changed_later": "NOTA: ÂĄNo se puede cambiar posteriormente!", "notification_email_from_address": "Desde", "notification_email_from_address_description": "DirecciÃŗn de correo electrÃŗnico del remitente, por ejemplo: \"Immich Photo Server \"", @@ -192,26 +194,22 @@ "oauth_auto_register": "Registro automÃĄtico", "oauth_auto_register_description": "Registre automÃĄticamente nuevos usuarios despuÊs de iniciar sesiÃŗn con OAuth", "oauth_button_text": "Texto del botÃŗn", - "oauth_client_id": "ID Cliente", - "oauth_client_secret": "Secreto Cliente", + "oauth_client_secret_description": "Requerido si PKCE (Prueba de clave para el intercambio de cÃŗdigos) no es compatible con el proveedor OAuth", "oauth_enable_description": "Iniciar sesiÃŗn con OAuth", - "oauth_issuer_url": "URL del emisor", "oauth_mobile_redirect_uri": "URI de redireccionamiento mÃŗvil", "oauth_mobile_redirect_uri_override": "Sobreescribir URI de redirecciÃŗn mÃŗvil", "oauth_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI mÃŗvil, como '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmo de firma de perfiles", - "oauth_profile_signing_algorithm_description": "Algoritmo utilizado para firmar el perfil del usuario.", - "oauth_scope": "Ámbito", "oauth_settings": "OAuth", "oauth_settings_description": "Administrar la configuraciÃŗn de inicio de sesiÃŗn de OAuth", "oauth_settings_more_details": "Para mÃĄs detalles acerca de esta característica, consulte la documentaciÃŗn.", - "oauth_signing_algorithm": "Algoritmo de firma", "oauth_storage_label_claim": "PeticiÃŗn de etiqueta de almacenamiento", "oauth_storage_label_claim_description": "Establece la etiqueta del almacenamiento del usuario automÃĄticamente a este valor reclamado.", "oauth_storage_quota_claim": "Reclamar quota de almacenamiento", "oauth_storage_quota_claim_description": "Establezca automÃĄticamente la cuota de almacenamiento del usuario al valor de esta solicitud.", "oauth_storage_quota_default": "Cuota de almacenamiento predeterminada (GiB)", "oauth_storage_quota_default_description": "Cuota en GiB que se utilizarÃĄ cuando no se proporcione ninguna por defecto (ingrese 0 para una cuota ilimitada).", + "oauth_timeout": "ExpiraciÃŗn de solicitud", + "oauth_timeout_description": "Tiempo de espera de solicitudes en milisegundos", "offline_paths": "Rutas sin conexiÃŗn", "offline_paths_description": "Estos resultados pueden deberse al eliminar manualmente archivos que no son parte de una biblioteca externa.", "password_enable_description": "Iniciar sesiÃŗn con correo electrÃŗnico y contraseÃąa", @@ -252,12 +250,12 @@ "storage_template_migration": "MigraciÃŗn de plantillas de almacenamiento", "storage_template_migration_description": "Aplicar la {template} actual a los elementos subidos previamente", "storage_template_migration_info": "La plantilla de almacenamiento convertirÃĄ todas las extensiones a minÃēscula. Los cambios en las plantillas solo se aplican a los elementos nuevos. Para aplicarlos retroactivamente a los elementos subidos previamente ejecute la {job}.", - "storage_template_migration_job": "MigraciÃŗn de la plantilla de almacenamiento", + "storage_template_migration_job": "Tarea de migraciÃŗn de la plantilla de almacenamiento", "storage_template_more_details": "Para obtener mÃĄs detalles sobre esta funciÃŗn, consulte la Plantilla de almacenamiento y sus implicaciones", "storage_template_onboarding_description": "Cuando estÃĄ habilitada, esta funciÃŗn organizarÃĄ automÃĄticamente los archivos segÃēn una plantilla definida por el usuario. Debido a problemas de estabilidad, la funciÃŗn se ha desactivado de forma predeterminada. Para obtener mÃĄs informaciÃŗn, consulte la documentaciÃŗn.", "storage_template_path_length": "Límite aproximado de la longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administre la estructura de carpetas y el nombre de archivo del recurso cargado", + "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", "storage_template_user_label": "{label} es la etiqueta de almacenamiento del usuario", "system_settings": "Ajustes del Sistema", "tag_cleanup_job": "Limpieza de etiquetas", @@ -345,13 +343,14 @@ "trash_settings": "ConfiguraciÃŗn papelera", "trash_settings_description": "Administrar la configuraciÃŗn de la papelera", "untracked_files": "Archivos sin seguimiento", - "untracked_files_description": "La aplicaciÃŗn no rastrea estos archivos. Puede ser el resultado de movimientos fallidos, cargas interrumpidas o sin procesar debido a un error", + "untracked_files_description": "La aplicaciÃŗn no rastrea estos archivos. Puede ser el resultado de movimientos fallidos, subidas interrumpidas o sin procesar debido a un error", "user_cleanup_job": "Limpieza de usuarios", "user_delete_delay": "La cuenta {user} y los archivos se programarÃĄn para su eliminaciÃŗn permanente en {delay, plural, one {# día} other {# días}}.", "user_delete_delay_settings": "Eliminar retardo", "user_delete_delay_settings_description": "NÃēmero de días despuÊs de la eliminaciÃŗn para eliminar permanentemente la cuenta y los activos de un usuario. El trabajo de eliminaciÃŗn de usuarios se ejecuta a medianoche para comprobar si hay usuarios que estÊn listos para su eliminaciÃŗn. Los cambios a esta configuraciÃŗn se evaluarÃĄn en la prÃŗxima ejecuciÃŗn.", "user_delete_immediately": "La cuenta {user} y los archivos se pondrÃĄn en cola para su eliminaciÃŗn permanente inmediatamente.", "user_delete_immediately_checkbox": "Poner en cola la eliminaciÃŗn inmediata de usuarios y elementos", + "user_details": "Detalles de Usuario", "user_management": "GestiÃŗn de usuarios", "user_password_has_been_reset": "La contraseÃąa del usuario ha sido restablecida:", "user_password_reset_description": "Proporcione una contraseÃąa temporal al usuario e infÃŗrmele que deberÃĄ cambiar la contraseÃąa en su prÃŗximo inicio de sesiÃŗn.", @@ -371,13 +370,17 @@ "admin_password": "ContraseÃąa del Administrador", "administration": "AdministraciÃŗn", "advanced": "Avanzada", - "advanced_settings_log_level_title": "Nivel de registro: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opciÃŗn para filtrar medios durante la sincronizaciÃŗn segÃēn criterios alternativos. Intenta esto solo si tienes problemas con que la aplicaciÃŗn detecte todos los ÃĄlbumes.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronizaciÃŗn de ÃĄlbumes del dispositivo", + "advanced_settings_log_level_title": "Nivel de registro: {level}", "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de los elementos encontrados en el dispositivo. Activa esta opciÃŗn para cargar imÃĄgenes remotas en su lugar.", "advanced_settings_prefer_remote_title": "Preferir imÃĄgenes remotas", "advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirÃĄ en cada peticiÃŗn de red", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Omitir verificaciÃŗn del certificado SSL del servidor. Requerido para certificados autofirmados", + "advanced_settings_proxy_headers_title": "Cabeceras Proxy", + "advanced_settings_self_signed_ssl_subtitle": "Omitir verificaciÃŗn del certificado SSL del servidor. Requerido para certificados autofirmados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados autofirmados", + "advanced_settings_sync_remote_deletions_subtitle": "Eliminar o restaurar automÃĄticamente un recurso en este dispositivo cuando se realice esa acciÃŗn en la web", + "advanced_settings_sync_remote_deletions_title": "Sincronizar eliminaciones remotas [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "Configuraciones avanzadas del usuario", "advanced_settings_troubleshooting_subtitle": "Habilitar funciones adicionales para soluciÃŗn de problemas", "advanced_settings_troubleshooting_title": "SoluciÃŗn de problemas", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "ÂŋEstÃĄs seguro de que quieres eliminar a {user}?", "album_share_no_users": "Parece que has compartido este ÃĄlbum con todos los usuarios o no tienes ningÃēn usuario con quien compartirlo.", "album_thumbnail_card_item": "1 elemento", - "album_thumbnail_card_items": "{} elementos", - "album_thumbnail_card_shared": "Compartido", - "album_thumbnail_shared_by": "Compartido por {}", + "album_thumbnail_card_items": "{count} elementos", + "album_thumbnail_card_shared": " ¡ Compartido", + "album_thumbnail_shared_by": "Compartido por {user}", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificaciÃŗn por correo electrÃŗnico cuando un ÃĄlbum compartido tenga nuevos archivos", "album_user_left": "Salida {album}", @@ -411,8 +414,8 @@ "album_viewer_appbar_share_err_delete": "No ha podido eliminar el ÃĄlbum", "album_viewer_appbar_share_err_leave": "No se ha podido abandonar el ÃĄlbum", "album_viewer_appbar_share_err_remove": "Hay problemas para eliminar los elementos del ÃĄlbum", - "album_viewer_appbar_share_err_title": "Error al cambiar el título del ÃĄlbum ", - "album_viewer_appbar_share_leave": "Abandonar ÃĄlbum ", + "album_viewer_appbar_share_err_title": "Error al cambiar el título del ÃĄlbum", + "album_viewer_appbar_share_leave": "Abandonar ÃĄlbum", "album_viewer_appbar_share_to": "Compartir Con", "album_viewer_page_share_add_users": "Agregar usuarios", "album_with_link_access": "Permita que cualquier persona con el enlace vea fotos y personas en este ÃĄlbum.", @@ -425,7 +428,7 @@ "allow_dark_mode": "Permitir modo oscuro", "allow_edits": "Permitir ediciÃŗn", "allow_public_user_to_download": "Permitir descargar al usuario pÃēblico", - "allow_public_user_to_upload": "Permitir cargar al usuario publico", + "allow_public_user_to_upload": "Permitir subir al usuario publico", "alt_text_qr_code": "CÃŗdigo QR", "anti_clockwise": "En sentido antihorario", "api_key": "Clave API", @@ -440,7 +443,7 @@ "archive": "Archivo", "archive_or_unarchive_photo": "Archivar o restaurar foto", "archive_page_no_archived_assets": "No se encontraron elementos archivados", - "archive_page_title": "Archivo ({})", + "archive_page_title": "Archivo ({count})", "archive_size": "TamaÃąo del archivo", "archive_size_description": "Configure el tamaÃąo del archivo para descargas (en GB)", "archived": "Archivado", @@ -477,40 +480,40 @@ "assets_added_to_album_count": "AÃąadido {count, plural, one {# asset} other {# assets}} al ÃĄlbum", "assets_added_to_name_count": "AÃąadido {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", - "assets_deleted_permanently": "\n{} elementos(s) eliminado(s) permanentemente", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich", "assets_moved_to_trash_count": "{count, plural, one {# elemento movido} other {# elementos movidos}} a la papelera", "assets_permanently_deleted_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", "assets_removed_count": "Eliminado {count, plural, one {# elemento} other {# elementos}}", - "assets_removed_permanently_from_device": "{} elemento(s) eliminado(s) permanentemente de su dispositivo", + "assets_removed_permanently_from_device": "{count} elemento(s) eliminado(s) permanentemente de su dispositivo", "assets_restore_confirmation": "ÂŋEstÃĄs seguro de que quieres restaurar todos tus activos eliminados? ÂĄNo puede deshacer esta acciÃŗn! Tenga en cuenta que los archivos sin conexiÃŗn no se pueden restaurar de esta manera.", "assets_restored_count": "Restaurado {count, plural, one {# elemento} other {# elementos}}", - "assets_restored_successfully": "{} elemento(s) restaurado(s) exitosamente", - "assets_trashed": "{} elemento(s) eliminado(s)", + "assets_restored_successfully": "{count} elemento(s) restaurado(s) exitosamente", + "assets_trashed": "{count} elemento(s) eliminado(s)", "assets_trashed_count": "Borrado {count, plural, one {# elemento} other {# elementos}}", - "assets_trashed_from_server": "{} elemento(s) movido a la papelera en Immich", + "assets_trashed_from_server": "{count} recurso(s) enviado(s) a la papelera desde el servidor de Immich", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} ya forma parte del ÃĄlbum", "authorized_devices": "Dispositivos Autorizados", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "Conectarse localmente a travÊs de la Wi-Fi designada cuando estÊ disponible y usar conexiones alternativas en otros lugares", + "automatic_endpoint_switching_title": "Cambio automÃĄtico de URL", "back": "AtrÃĄs", "back_close_deselect": "AtrÃĄs, cerrar o anular la selecciÃŗn", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})", + "background_location_permission": "Permiso de ubicaciÃŗn en segundo plano", + "background_location_permission_content": "Para poder cambiar de red mientras se ejecuta en segundo plano, Immich debe tener *siempre* acceso a la ubicaciÃŗn precisa para que la aplicaciÃŗn pueda leer el nombre de la red Wi-Fi", + "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir", "backup_album_selection_page_assets_scatter": "Los elementos pueden dispersarse en varios ÃĄlbumes. De este modo, los ÃĄlbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.", "backup_album_selection_page_select_albums": "Seleccionar Álbumes", "backup_album_selection_page_selection_info": "InformaciÃŗn sobre la SelecciÃŗn", "backup_album_selection_page_total_assets": "Total de elementos Ãēnicos", "backup_all": "Todos", - "backup_background_service_backup_failed_message": "Error al copiar elementos. Reintentando...", - "backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentando...", - "backup_background_service_current_upload_notification": "Cargando {}", - "backup_background_service_default_notification": "Verificando si hay nuevos elementos", + "backup_background_service_backup_failed_message": "Error al copiar elementos. Reintentandoâ€Ļ", + "backup_background_service_connection_failed_message": "Error al conectar con el servidor. Reintentandoâ€Ļ", + "backup_background_service_current_upload_notification": "Subiendo {filename}", + "backup_background_service_default_notification": "Comprobando nuevos elementosâ€Ļ", "backup_background_service_error_title": "Error de copia de seguridad", - "backup_background_service_in_progress_notification": "Creando copia de seguridad de tus elementos...", - "backup_background_service_upload_failure_notification": "Error al cargar {}", + "backup_background_service_in_progress_notification": "Creando copia de seguridad de tus elementosâ€Ļ", + "backup_background_service_upload_failure_notification": "Error al subir {filename}", "backup_controller_page_albums": "Álbumes de copia de seguridad", "backup_controller_page_background_app_refresh_disabled_content": "Activa la actualizaciÃŗn en segundo plano de la aplicaciÃŗn en ConfiguraciÃŗn > General > ActualizaciÃŗn en segundo plano para usar la copia de seguridad en segundo plano.", "backup_controller_page_background_app_refresh_disabled_title": "ActualizaciÃŗn en segundo plano desactivada", @@ -521,22 +524,21 @@ "backup_controller_page_background_battery_info_title": "Optimizaciones de batería", "backup_controller_page_background_charging": "Solo mientras se carga", "backup_controller_page_background_configure_error": "Error al configurar el servicio en segundo plano", - "backup_controller_page_background_delay": "Retraso en la copia de seguridad de nuevos elementos: {}", - "backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automÃĄticamente cualquier nuevos elementos sin necesidad de abrir la aplicaciÃŗn.", + "backup_controller_page_background_delay": "Retrasar la copia de seguridad de los nuevos elementos: {duration}", + "backup_controller_page_background_description": "Activa el servicio en segundo plano para copiar automÃĄticamente cualquier nuevos elementos sin necesidad de abrir la aplicaciÃŗn", "backup_controller_page_background_is_off": "La copia de seguridad en segundo plano automÃĄtica estÃĄ desactivada", "backup_controller_page_background_is_on": "La copia de seguridad en segundo plano automÃĄtica estÃĄ activada", "backup_controller_page_background_turn_off": "Desactivar el servicio en segundo plano", "backup_controller_page_background_turn_on": "Activar el servicio en segundo plano", - "backup_controller_page_background_wifi": "Solo en WiFi", + "backup_controller_page_background_wifi": "Solo en Wi-Fi", "backup_controller_page_backup": "Copia de Seguridad", - "backup_controller_page_backup_selected": "Seleccionado:", + "backup_controller_page_backup_selected": "Seleccionado: ", "backup_controller_page_backup_sub": "Fotos y videos respaldados", - "backup_controller_page_created": "Creado el: {}", - "backup_controller_page_desc_backup": "Active la copia de seguridad para cargar automÃĄticamente los nuevos elementos al servidor.", - "backup_controller_page_excluded": "Excluido:", - "backup_controller_page_failed": "Fallidos ({})", - "backup_controller_page_filename": "Nombre del archivo: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_created": "Creado el: {date}", + "backup_controller_page_desc_backup": "Active la copia de seguridad para subir automÃĄticamente los nuevos elementos al servidor cuando se abre la aplicaciÃŗn.", + "backup_controller_page_excluded": "Excluido: ", + "backup_controller_page_failed": "Fallidos ({count})", + "backup_controller_page_filename": "Nombre del archivo: {filename} [{size}]", "backup_controller_page_info": "InformaciÃŗn de la Copia de Seguridad", "backup_controller_page_none_selected": "Ninguno seleccionado", "backup_controller_page_remainder": "Restante", @@ -545,21 +547,25 @@ "backup_controller_page_start_backup": "Iniciar copia de seguridad", "backup_controller_page_status_off": "La copia de seguridad estÃĄ desactivada", "backup_controller_page_status_on": "La copia de seguridad estÃĄ activada", - "backup_controller_page_storage_format": "{} de {} usadas", + "backup_controller_page_storage_format": "{used} de {total} usadas", "backup_controller_page_to_backup": "Álbumes a respaldar", "backup_controller_page_total_sub": "Todas las fotos y vídeos Ãēnicos de los ÃĄlbumes seleccionados", "backup_controller_page_turn_off": "Apagar la copia de seguridad", "backup_controller_page_turn_on": "Activar la copia de seguridad", - "backup_controller_page_uploading_file_info": "Cargando informaciÃŗn del archivo", + "backup_controller_page_uploading_file_info": "Subiendo informaciÃŗn del archivo", "backup_err_only_album": "No se puede eliminar el Ãēnico ÃĄlbum", "backup_info_card_assets": "elementos", "backup_manual_cancelled": "Cancelado", - "backup_manual_in_progress": "Subida en progreso. Espere", + "backup_manual_in_progress": "Subida ya en progreso. Vuelve a intentarlo mÃĄs tarde", "backup_manual_success": "Éxito", "backup_manual_title": "Estado de la subida", "backup_options_page_title": "Opciones de Copia de Seguridad", "backup_setting_subtitle": "Administra las configuraciones de respaldo en segundo y primer plano", "backward": "Retroceder", + "biometric_auth_enabled": "AutentificaciÃŗn biomÊtrica habilitada", + "biometric_locked_out": "EstÃĄs bloqueado de la autentificaciÃŗn biomÊtrica", + "biometric_no_options": "Sin opciones biomÊtricas disponibles", + "biometric_not_available": "AutentificaciÃŗn biomÊtrica no disponible en este dispositivo", "birthdate_saved": "Fecha de nacimiento guardada con Êxito", "birthdate_set_description": "La fecha de nacimiento se utiliza para calcular la edad de esta persona en el momento de la fotografía.", "blurred_background": "Fondo borroso", @@ -570,21 +576,21 @@ "bulk_keep_duplicates_confirmation": "ÂŋEstas seguro de que desea mantener {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto resolverÃĄ todos los grupos duplicados sin borrar nada.", "bulk_trash_duplicates_confirmation": "ÂŋEstas seguro de que desea eliminar masivamente {count, plural, one {# duplicate asset} other {# duplicate assets}} archivos duplicados? Esto mantendrÃĄ el archivo mÃĄs grande de cada grupo y eliminarÃĄ todos los demÃĄs duplicados.", "buy": "Comprar Immich", - "cache_settings_album_thumbnails": "Miniaturas de la pÃĄgina de la biblioteca ({} elementos)", + "cache_settings_album_thumbnails": "Miniaturas de la pÃĄgina de la biblioteca ({count} elementos)", "cache_settings_clear_cache_button": "Borrar cachÊ", "cache_settings_clear_cache_button_title": "Borra la cachÊ de la aplicaciÃŗn. Esto afectarÃĄ significativamente el rendimiento de la aplicaciÃŗn hasta que se reconstruya la cachÊ.", "cache_settings_duplicated_assets_clear_button": "LIMPIAR", "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos en la lista negra de la app", - "cache_settings_duplicated_assets_title": "Elementos duplicados ({})", - "cache_settings_image_cache_size": "TamaÃąo de la cachÊ de imÃĄgenes ({} elementos)", + "cache_settings_duplicated_assets_title": "Elementos duplicados ({count})", + "cache_settings_image_cache_size": "TamaÃąo de la cachÊ de imÃĄgenes ({count} elementos)", "cache_settings_statistics_album": "Miniaturas de la biblioteca", - "cache_settings_statistics_assets": "{} elementos ({})", + "cache_settings_statistics_assets": "{count} elementos ({size})", "cache_settings_statistics_full": "ImÃĄgenes completas", "cache_settings_statistics_shared": "Miniaturas de ÃĄlbumes compartidos", "cache_settings_statistics_thumbnail": "Miniaturas", "cache_settings_statistics_title": "Uso de cachÊ", "cache_settings_subtitle": "Controla el comportamiento del almacenamiento en cachÊ de la aplicaciÃŗn mÃŗvil Immich", - "cache_settings_thumbnail_size": "TamaÃąo de la cachÊ de miniaturas ({} elementos)", + "cache_settings_thumbnail_size": "TamaÃąo de la cachÊ de miniaturas ({count} elementos)", "cache_settings_tile_subtitle": "Controla el comportamiento del almacenamiento local", "cache_settings_tile_title": "Almacenamiento local", "cache_settings_title": "ConfiguraciÃŗn de la cachÊ", @@ -593,12 +599,14 @@ "camera_model": "Modelo de cÃĄmara", "cancel": "Cancelar", "cancel_search": "Cancelar bÃēsqueda", - "canceled": "Canceled", + "canceled": "Cancelado", "cannot_merge_people": "No se pueden fusionar personas", "cannot_undo_this_action": "ÂĄNo puedes deshacer esta acciÃŗn!", "cannot_update_the_description": "No se puede actualizar la descripciÃŗn", + "cast": "Convertir", "change_date": "Cambiar fecha", - "change_display_order": "Change display order", + "change_description": "Cambiar descripciÃŗn", + "change_display_order": "Cambiar orden de visualizaciÃŗn", "change_expiration_time": "Cambiar fecha de caducidad", "change_location": "Cambiar ubicaciÃŗn", "change_name": "Cambiar nombre", @@ -610,12 +618,13 @@ "change_password_form_new_password": "Nueva ContraseÃąa", "change_password_form_password_mismatch": "Las contraseÃąas no coinciden", "change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseÃąa", + "change_pin_code": "Cambiar PIN", "change_your_password": "Cambia tu contraseÃąa", "changed_visibility_successfully": "Visibilidad cambiada correctamente", "check_all": "Comprobar todo", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "Comprobar copias de seguridad de archivos corruptos", + "check_corrupt_asset_backup_button": "Realizar comprobaciÃŗn", + "check_corrupt_asset_backup_description": "Ejecutar esta comprobaciÃŗn solo por Wi-Fi y una vez que todos los archivos hayan sido respaldados. El procedimiento puede tardar unos minutos.", "check_logs": "Comprobar Registros", "choose_matching_people_to_merge": "Elija personas similares para fusionar", "city": "Ciudad", @@ -624,19 +633,17 @@ "clear_all_recent_searches": "Borrar bÃēsquedas recientes", "clear_message": "Limpiar mensaje", "clear_value": "Limpiar valor", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Introduzca contraseÃąa", "client_cert_import": "Importar", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "El certificado de cliente estÃĄ importado", + "client_cert_invalid_msg": "Archivo de certificado no vÃĄlido o contraseÃąa incorrecta", + "client_cert_remove_msg": "El certificado de cliente se ha eliminado", + "client_cert_subtitle": "Solo se admite el formato PKCS12 (.p12, .pfx). La importaciÃŗn/eliminaciÃŗn de certificados solo estÃĄ disponible antes de iniciar sesiÃŗn", + "client_cert_title": "Certificado de cliente SSL", "clockwise": "En el sentido de las agujas del reloj", "close": "Cerrar", "collapse": "Agrupar", "collapse_all": "Desplegar todo", - "color": "Color", "color_theme": "Color del tema", "comment_deleted": "Comentario borrado", "comment_options": "Opciones de comentarios", @@ -644,23 +651,25 @@ "comments_are_disabled": "Los comentarios estÃĄn deshabilitados", "common_create_new_album": "Crear nuevo ÃĄlbum", "common_server_error": "Por favor, verifica tu conexiÃŗn de red, asegÃērate de que el servidor estÊ accesible y las versiones de la aplicaciÃŗn y del servidor sean compatibles.", - "completed": "Completed", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar ContraseÃąa de Administrador", "confirm_delete_face": "ÂŋEstÃĄs seguro que deseas eliminar la cara de {name} del archivo?", "confirm_delete_shared_link": "ÂŋEstÃĄs seguro de que deseas eliminar este enlace compartido?", "confirm_keep_this_delete_others": "Todos los demÃĄs activos de la pila se eliminarÃĄn excepto este activo. ÂŋEstÃĄ seguro de que quiere continuar?", + "confirm_new_pin_code": "Confirmar nuevo pin", "confirm_password": "Confirmar contraseÃąa", + "connected_to": "Conectado a", "contain": "Incluido", "context": "Contexto", "continue": "Continuar", - "control_bottom_app_bar_album_info_shared": "{} elementos ¡ Compartidos", + "control_bottom_app_bar_album_info_shared": "{count} elementos ¡ Compartidos", "control_bottom_app_bar_create_new_album": "Crear nuevo ÃĄlbum", "control_bottom_app_bar_delete_from_immich": "Borrar de Immich", "control_bottom_app_bar_delete_from_local": "Borrar del dispositivo", "control_bottom_app_bar_edit_location": "Editar ubicaciÃŗn", "control_bottom_app_bar_edit_time": "Editar fecha y hora", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Enlace para compartir", "control_bottom_app_bar_share_to": "Enviar", "control_bottom_app_bar_trash_from_immich": "Mover a la papelera", "copied_image_to_clipboard": "Imagen copiada al portapapeles.", @@ -692,10 +701,12 @@ "create_tag_description": "Crear una nueva etiqueta. Para las etiquetas anidadas, ingresa la ruta completa de la etiqueta, incluidas las barras diagonales.", "create_user": "Crear usuario", "created": "Creado", + "created_at": "Creado", "crop": "Recortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo actual", - "current_server_address": "Current server address", + "current_pin_code": "PIN actual", + "current_server_address": "DirecciÃŗn actual del servidor", "custom_locale": "ConfiguraciÃŗn regional personalizada", "custom_locale_description": "Formatear fechas y nÃēmeros segÃēn el idioma y la regiÃŗn", "daily_title_text_date": "E dd, MMM", @@ -742,11 +753,10 @@ "description": "DescripciÃŗn", "description_input_hint_text": "Agregar descripciÃŗn...", "description_input_submit_error": "Error al actualizar la descripciÃŗn, verifica el registro para obtener mÃĄs detalles", - "details": "DETALLES", + "details": "Detalles", "direction": "DirecciÃŗn", "disabled": "Deshabilitado", "disallow_edits": "Bloquear ediciÃŗn", - "discord": "", "discover": "Descubrir", "dismiss_all_errors": "Descartar todos los errores", "dismiss_error": "Descartar error", @@ -763,7 +773,7 @@ "download_enqueue": "Descarga en cola", "download_error": "Error al descargar", "download_failed": "Descarga fallida", - "download_filename": "Archivo: {}", + "download_filename": "archivo: {filename}", "download_finished": "Descarga completada", "download_include_embedded_motion_videos": "Vídeos incrustados", "download_include_embedded_motion_videos_description": "Incluir vídeos incrustados en fotografías en movimiento como un archivo separado", @@ -787,6 +797,8 @@ "edit_avatar": "Editar avatar", "edit_date": "Editar fecha", "edit_date_and_time": "Editar fecha y hora", + "edit_description": "Editar descripciÃŗn", + "edit_description_prompt": "Por favor selecciona una nueva descripciÃŗn:", "edit_exclusion_pattern": "Editar patrÃŗn de exclusiÃŗn", "edit_faces": "Editar rostros", "edit_import_path": "Editar ruta de importaciÃŗn", @@ -801,25 +813,26 @@ "edit_title": "Editar Titulo", "edit_user": "Editar usuario", "edited": "Editado", - "editor": "Editor", "editor_close_without_save_prompt": "No se guardarÃĄn los cambios", "editor_close_without_save_title": "ÂŋCerrar el editor?", "editor_crop_tool_h2_aspect_ratios": "Proporciones del aspecto", "editor_crop_tool_h2_rotation": "RotaciÃŗn", "email": "Correo", - "empty_folder": "This folder is empty", + "email_notifications": "Notificaciones por correo electrÃŗnico", + "empty_folder": "Esta carpeta estÃĄ vacía", "empty_trash": "Vaciar papelera", "empty_trash_confirmation": "ÂŋEstÃĄs seguro de que quieres vaciar la papelera? Esto eliminarÃĄ permanentemente todos los archivos de la basura de Immich.\nÂĄNo puedes deshacer esta acciÃŗn!", "enable": "Habilitar", + "enable_biometric_auth_description": "Introduce tu cÃŗdigo PIN para habilitar la autentificaciÃŗn biomÊtrica", "enabled": "Habilitado", "end_date": "Fecha final", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", - "error": "Error", - "error_change_sort_album": "Failed to change album sort order", + "enqueued": "AÃąadido a la cola", + "enter_wifi_name": "Introduce el nombre Wi-Fi", + "enter_your_pin_code": "Introduce tu cÃŗdigo PIN", + "enter_your_pin_code_subtitle": "Introduce tu cÃŗdigo PIN para acceder a la carpeta bloqueada", + "error_change_sort_album": "No se pudo cambiar el orden de visualizaciÃŗn del ÃĄlbum", "error_delete_face": "Error al eliminar la cara del archivo", "error_loading_image": "Error al cargar la imagen", - "error_saving_image": "Error: {}", "error_title": "Error: algo saliÃŗ mal", "errors": { "cannot_navigate_next_asset": "No puedes navegar al siguiente archivo", @@ -849,10 +862,12 @@ "failed_to_keep_this_delete_others": "No se pudo conservar este activo y eliminar los demÃĄs", "failed_to_load_asset": "Error al cargar el elemento", "failed_to_load_assets": "Error al cargar los elementos", + "failed_to_load_notifications": "Error al cargar las notificaciones", "failed_to_load_people": "Error al cargar a los usuarios", "failed_to_remove_product_key": "No se pudo eliminar la clave del producto", "failed_to_stack_assets": "No se pudieron agrupar los archivos", "failed_to_unstack_assets": "Error al desagrupar los archivos", + "failed_to_update_notification_status": "Error al actualizar el estado de la notificaciÃŗn", "import_path_already_exists": "Esta ruta de importaciÃŗn ya existe.", "incorrect_email_or_password": "ContraseÃąa o email incorrecto", "paths_validation_failed": "FallÃŗ la validaciÃŗn en {paths, plural, one {# carpeta} other {# carpetas}}", @@ -870,6 +885,7 @@ "unable_to_archive_unarchive": "AÃąade a {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "No se puede cambiar la funciÃŗn del usuario del ÃĄlbum", "unable_to_change_date": "No se puede cambiar la fecha", + "unable_to_change_description": "Imposible cambiar la descripciÃŗn", "unable_to_change_favorite": "Imposible cambiar el archivo favorito", "unable_to_change_location": "No se puede cambiar de ubicaciÃŗn", "unable_to_change_password": "No se puede cambiar la contraseÃąa", @@ -907,6 +923,7 @@ "unable_to_log_out_all_devices": "No se pueden cerrar las sesiones en todos los dispositivos", "unable_to_log_out_device": "No se puede cerrar la sesiÃŗn en el dispositivo", "unable_to_login_with_oauth": "No se puede iniciar sesiÃŗn con OAuth", + "unable_to_move_to_locked_folder": "Imposible mover a la carpeta bloqueada", "unable_to_play_video": "No se puede reproducir el vídeo", "unable_to_reassign_assets_existing_person": "No se pueden reasignar a {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "No se pueden reasignar archivos a una nueva persona", @@ -920,6 +937,7 @@ "unable_to_remove_reaction": "No se puede eliminar la reacciÃŗn", "unable_to_repair_items": "No se pueden reparar los items", "unable_to_reset_password": "No se puede restablecer la contraseÃąa", + "unable_to_reset_pin_code": "No se ha podido restablecer el PIN", "unable_to_resolve_duplicate": "No se resolver duplicado", "unable_to_restore_assets": "No se pueden restaurar los archivos", "unable_to_restore_trash": "No se puede restaurar la papelera", @@ -953,16 +971,15 @@ "exif_bottom_sheet_location": "UBICACIÓN", "exif_bottom_sheet_people": "PERSONAS", "exif_bottom_sheet_person_add_person": "AÃąadir nombre", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Edad {age}", + "exif_bottom_sheet_person_age_months": "Edad {months} meses", + "exif_bottom_sheet_person_age_year_months": "Edad 1 aÃąo, {months} meses", + "exif_bottom_sheet_person_age_years": "Edad {years}", "exit_slideshow": "Salir de la presentaciÃŗn", "expand_all": "Expandir todo", "experimental_settings_new_asset_list_subtitle": "Trabajo en progreso", "experimental_settings_new_asset_list_title": "Habilitar cuadrícula fotogrÃĄfica experimental", - "experimental_settings_subtitle": "Úsalo bajo tu responsabilidad", - "experimental_settings_title": "Experimental", + "experimental_settings_subtitle": "ÂĄÃšsalo bajo tu propia responsabilidad!", "expire_after": "Expirar despuÊs de", "expired": "Caducado", "expires_date": "Expira el {date}", @@ -970,15 +987,15 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", - "extension": "Extension", "external": "Externo", "external_libraries": "Bibliotecas Externas", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Red externa", + "external_network_sheet_info": "Cuando no estÊs conectado a la red Wi-Fi preferida, la aplicaciÃŗn se conectarÃĄ al servidor utilizando la primera de las siguientes URLs a la que pueda acceder, comenzando desde la parte superior de la lista hacia abajo", "face_unassigned": "Sin asignar", - "failed": "Failed", + "failed": "Fallido", + "failed_to_authenticate": "Fallo al autentificar", "failed_to_load_assets": "Error al cargar los activos", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", @@ -992,21 +1009,21 @@ "filetype": "Tipo de archivo", "filter": "Filtrar", "filter_people": "Filtrar personas", + "filter_places": "Filtrar lugares", "find_them_fast": "EncuÊntrelos rÃĄpidamente por nombre con la bÃēsqueda", "fix_incorrect_match": "Corregir coincidencia incorrecta", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Carpeta", + "folder_not_found": "Carpeta no encontrada", "folders": "Carpetas", "folders_feature_description": "Explorar la vista de carpetas para las fotos y los videos en el sistema de archivos", "forward": "Reenviar", - "general": "General", "get_help": "Solicitar ayuda", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "No se pudo obtener el nombre de la red Wi-Fi. AsegÃērate de haber concedido los permisos necesarios y de estar conectado a una red Wi-Fi", "getting_started": "Comenzamos", "go_back": "Volver atrÃĄs", "go_to_folder": "Ir al directorio", "go_to_search": "Ir a bÃēsqueda", - "grant_permission": "Grant permission", + "grant_permission": "Conceder permiso", "group_albums_by": "Agrupar albums por...", "group_country": "Agrupar por país", "group_no": "Sin agrupaciÃŗn", @@ -1031,7 +1048,7 @@ "hide_unnamed_people": "Ocultar personas anÃŗnimas", "home_page_add_to_album_conflicts": "{added} elementos agregados al ÃĄlbum {album}.{failed} elementos ya existen en el ÃĄlbum.", "home_page_add_to_album_err_local": "AÃēn no se pueden agregar elementos locales a ÃĄlbumes, omitiendo", - "home_page_add_to_album_success": "{added} elementos agregados al ÃĄlbum {album}. ", + "home_page_add_to_album_success": "Se aÃąadieron {added} elementos al ÃĄlbum {album}.", "home_page_album_err_partner": "AÃēn no se pueden agregar elementos a un ÃĄlbum de un compaÃąero, omitiendo", "home_page_archive_err_local": "Los elementos locales no pueden ser archivados, omitiendo", "home_page_archive_err_partner": "No se pueden archivar elementos de un compaÃąero, omitiendo", @@ -1040,10 +1057,11 @@ "home_page_delete_remote_err_local": "Elementos locales en la selecciÃŗn de eliminaciÃŗn remota, omitiendo", "home_page_favorite_err_local": "AÃēn no se pueden archivar elementos locales, omitiendo", "home_page_favorite_err_partner": "AÃēn no se pueden marcar elementos de compaÃąeros como favoritos, omitiendo", - "home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegÃērate de elegir un ÃĄlbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los ÃĄlbumes.", + "home_page_first_time_notice": "Si es la primera vez que usas la aplicaciÃŗn, asegÃērate de elegir un ÃĄlbum de copia de seguridad para que la línea de tiempo pueda mostrar fotos y vídeos en Êl", + "home_page_locked_error_local": "Imposible mover archivos locales a carpeta bloqueada, saltando", + "home_page_locked_error_partner": "Imposible mover los archivos del compaÃąero a carpeta bloqueada, obviando", "home_page_share_err_local": "No se pueden compartir elementos locales a travÊs de un enlace, omitiendo", "home_page_upload_err_limit": "Solo se pueden subir 30 elementos simultÃĄneamente, omitiendo", - "host": "Host", "hour": "Hora", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirÃĄn a Immich", @@ -1117,15 +1135,16 @@ "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la bÃēsqueda", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "La aplicaciÃŗn se conectarÃĄ al servidor a travÊs de esta URL cuando utilice la red Wi-Fi especificada", + "location_permission": "Permiso de ubicaciÃŗn", + "location_permission_content": "Para usar la funciÃŗn de cambio automÃĄtico, Immich necesita permiso de ubicaciÃŗn precisa para poder leer el nombre de la red Wi-Fi actual", "location_picker_choose_on_map": "Elegir en el mapa", "location_picker_latitude_error": "Introduce una latitud vÃĄlida", "location_picker_latitude_hint": "Introduce tu latitud aquí", "location_picker_longitude_error": "Introduce una longitud vÃĄlida", "location_picker_longitude_hint": "Introduce tu longitud aquí", + "lock": "Bloquear", + "locked_folder": "Bloquear carpeta", "log_out": "Cerrar sesiÃŗn", "log_out_all_devices": "Cerrar sesiÃŗn en todos los dispositivos", "logged_out_all_devices": "Cierre la sesiÃŗn en todos los dispositivos", @@ -1145,7 +1164,7 @@ "login_form_failed_get_oauth_server_config": "Error al iniciar sesiÃŗn con OAuth, verifica la URL del servidor", "login_form_failed_get_oauth_server_disable": "La funciÃŗn de OAuth no estÃĄ disponible en este servidor", "login_form_failed_login": "Error al iniciar sesiÃŗn, comprueba la URL del servidor, el correo electrÃŗnico y la contraseÃąa", - "login_form_handshake_exception": "Hubo un error de verificaciÃŗn del certificado del servidor. Activa el soporte para certificados autofirmados en las preferencias si estÃĄs usando un certificado autofirmado", + "login_form_handshake_exception": "Hubo una excepciÃŗn de handshake con el servidor. Activa la compatibilidad con certificados autofirmados en la configuraciÃŗn si estÃĄs utilizando un certificado autofirmado.", "login_form_password_hint": "contraseÃąa", "login_form_save_login": "Mantener la sesiÃŗn iniciada", "login_form_server_empty": "Agrega la URL del servidor.", @@ -1170,8 +1189,8 @@ "manage_your_devices": "Administre sus dispositivos conectados", "manage_your_oauth_connection": "Administra tu conexiÃŗn OAuth", "map": "Mapa", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} fotos", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "No se pudo obtener la posiciÃŗn del usuario", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Usar esta ubicaciÃŗn", @@ -1185,15 +1204,18 @@ "map_settings": "Ajustes mapa", "map_settings_dark_mode": "Modo oscuro", "map_settings_date_range_option_day": "Últimas 24 horas", - "map_settings_date_range_option_days": "Últimos {} días", + "map_settings_date_range_option_days": "Últimos {days} días", "map_settings_date_range_option_year": "Último aÃąo", - "map_settings_date_range_option_years": "Últimos {} aÃąos", + "map_settings_date_range_option_years": "Últimos {years} aÃąos", "map_settings_dialog_title": "Ajustes mapa", "map_settings_include_show_archived": "Incluir archivados", "map_settings_include_show_partners": "Incluir Parejas", "map_settings_only_show_favorites": "Mostrar solo favoritas", "map_settings_theme_settings": "Apariencia del Mapa", "map_zoom_to_see_photos": "Alejar para ver fotos", + "mark_all_as_read": "Marcar todos como leídos", + "mark_as_read": "Marcar como leído", + "marked_all_as_read": "Todos marcados como leídos", "matches": "Coincidencias", "media_type": "Tipo de medio", "memories": "Recuerdos", @@ -1202,8 +1224,6 @@ "memories_setting_description": "Gestiona lo que ves en tus recuerdos", "memories_start_over": "Empezar de nuevo", "memories_swipe_to_close": "Desliza para cerrar", - "memories_year_ago": "Hace un aÃąo", - "memories_years_ago": "Hace {} aÃąos", "memory": "Recuerdo", "memory_lane_title": "BaÃēl de los recuerdos {title}", "menu": "MenÃē", @@ -1218,28 +1238,34 @@ "missing": "Perdido", "model": "Modelo", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", "more": "Mas", + "move": "Mover", + "move_off_locked_folder": "Mover fuera de carpeta protegida", + "move_to_locked_folder": "Mover a carpeta protegida", + "move_to_locked_folder_confirmation": "Estas fotos y vídeos serÃĄn eliminados de todos los ÃĄlbumes y sÃŗlo podrÃĄn ser vistos desde la carpeta protegida", + "moved_to_archive": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a archivo", + "moved_to_library": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a biblioteca", "moved_to_trash": "Movido a la papelera", "multiselect_grid_edit_date_time_err_read_only": "No se puede cambiar la fecha del archivo(s) de solo lectura, omitiendo", - "multiselect_grid_edit_gps_err_read_only": "No se puede cambiar la localizaciÃŗn de archivos de solo lectura. Saltando.", + "multiselect_grid_edit_gps_err_read_only": "No se puede editar la ubicaciÃŗn de activos de solo lectura, omitiendo", "mute_memories": "Silenciar Recuerdos", "my_albums": "Mis albums", "name": "Nombre", "name_or_nickname": "Nombre o apodo", "networking_settings": "Red", "networking_subtitle": "Configuraciones de acceso por URL al servidor", - "never": "nunca", + "never": "Nunca", "new_album": "Nuevo ÃĄlbum", "new_api_key": "Nueva clave API", "new_password": "Nueva contraseÃąa", "new_person": "Nueva persona", + "new_pin_code": "Nuevo PIN", + "new_pin_code_subtitle": "Esta es tu primera vez accediendo a la carpeta protegida. Crea un PIN seguro para acceder a esta pÃĄgina", "new_user_created": "Nuevo usuario creado", "new_version_available": "NUEVA VERSIÓN DISPONIBLE", "newest_first": "El mÃĄs reciente primero", "next": "Siguiente", "next_memory": "Siguiente recuerdo", - "no": "No", "no_albums_message": "Crea un ÃĄlbum para organizar tus fotos y vídeos", "no_albums_with_name_yet": "Parece que todavía no tienes ningÃēn ÃĄlbum con este nombre.", "no_albums_yet": "Parece que aÃēn no tienes ningÃēn ÃĄlbum.", @@ -1251,15 +1277,19 @@ "no_explore_results_message": "Sube mÃĄs fotos para explorar tu colecciÃŗn.", "no_favorites_message": "Agregue favoritos para encontrar rÃĄpidamente sus mejores fotos y videos", "no_libraries_message": "Crea una biblioteca externa para ver tus fotos y vídeos", + "no_locked_photos_message": "Fotos y vídeos en la carpeta protegida estÃĄn ocultos y no se verÃĄn en tus bÃēsquedas de tu librería.", "no_name": "Sin nombre", + "no_notifications": "Ninguna notificaciÃŗn", + "no_people_found": "No se encontraron personas coincidentes", "no_places": "Sin lugares", "no_results": "Sin resultados", "no_results_description": "Pruebe con un sinÃŗnimo o una palabra clave mÃĄs general", "no_shared_albums_message": "Crea un ÃĄlbum para compartir fotos y vídeos con personas de tu red", "not_in_any_album": "Sin ÃĄlbum", - "not_selected": "Not selected", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos cargados previamente, ejecute el", + "not_selected": "No seleccionado", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos subidos previamente, ejecute el", "notes": "Notas", + "nothing_here_yet": "Sin nada aÃēn", "notification_permission_dialog_content": "Para activar las notificaciones, ve a ConfiguraciÃŗn y selecciona permitir.", "notification_permission_list_tile_content": "Concede permiso para habilitar las notificaciones.", "notification_permission_list_tile_enable_button": "Permitir notificaciones", @@ -1267,7 +1297,6 @@ "notification_toggle_setting_description": "Habilitar notificaciones de correo electrÃŗnico", "notifications": "Notificaciones", "notifications_setting_description": "Administrar notificaciones", - "oauth": "OAuth", "official_immich_resources": "Recursos oficiales de Immich", "offline": "Desconectado", "offline_paths": "Rutas sin conexiÃŗn", @@ -1282,13 +1311,13 @@ "onboarding_welcome_user": "Bienvenido, {user}", "online": "En línea", "only_favorites": "Solo favoritos", + "open": "Abierto", "open_in_map_view": "Abrir en la vista del mapa", "open_in_openstreetmap": "Abrir en OpenStreetMap", "open_the_search_filters": "Abre los filtros de bÃēsqueda", "options": "Opciones", "or": "o", "organize_your_library": "Organiza tu biblioteca", - "original": "original", "other": "Otro", "other_devices": "Otro dispositivo", "other_variables": "Otras variables", @@ -1302,10 +1331,10 @@ "partner_list_view_all": "Ver todas", "partner_page_empty_message": "Tus fotos aÃēn no se han compartido con ningÃēn compaÃąero.", "partner_page_no_more_users": "No hay mÃĄs usuarios para agregar", - "partner_page_partner_add_failed": "CompaÃąero no pudo ser agregado ", + "partner_page_partner_add_failed": "No se pudo aÃąadir el socio", "partner_page_select_partner": "Seleccionar compaÃąero", "partner_page_shared_to_title": "Compartido con", - "partner_page_stop_sharing_content": "{} ya no podrÃĄ acceder a tus fotos", + "partner_page_stop_sharing_content": "{partner} ya no podrÃĄ acceder a tus fotos.", "partner_sharing": "Compartir con invitados", "partners": "Invitados", "password": "ContraseÃąa", @@ -1351,6 +1380,10 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos de aÃąos anteriores", "pick_a_location": "Elige una ubicaciÃŗn", + "pin_code_changed_successfully": "PIN cambiado exitosamente", + "pin_code_reset_successfully": "PIN restablecido exitosamente", + "pin_code_setup_successfully": "PIN establecido exitosamente", + "pin_verification": "VerificaciÃŗn con cÃŗdigo PIN", "place": "Lugar", "places": "Lugares", "places_count": "{count, plural, one {{count, number} Lugar} other {{count, number} Lugares}}", @@ -1358,6 +1391,7 @@ "play_memories": "Reproducir recuerdos", "play_motion_photo": "Reproducir foto en movimiento", "play_or_pause_video": "Reproducir o pausar vídeo", + "please_auth_to_access": "Por favor, autentícate para acceder", "port": "Puerto", "preferences_settings_subtitle": "Configuraciones de la aplicaciÃŗn", "preferences_settings_title": "Preferencias", @@ -1368,11 +1402,11 @@ "previous_or_next_photo": "Foto anterior o siguiente", "primary": "BÃĄsico", "privacy": "Privacidad", + "profile": "Perfil", "profile_drawer_app_logs": "Registros", "profile_drawer_client_out_of_date_major": "La app estÃĄ desactualizada. Por favor actualiza a la Ãēltima versiÃŗn principal.", "profile_drawer_client_out_of_date_minor": "La app estÃĄ desactualizada. Por favor actualiza a la Ãēltima versiÃŗn menor.", "profile_drawer_client_server_up_to_date": "El Cliente y el Servidor estÃĄn actualizados", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "El servidor estÃĄ desactualizado. Por favor actualiza a la Ãēltima versiÃŗn principal.", "profile_drawer_server_out_of_date_minor": "El servidor estÃĄ desactualizado. Por favor actualiza a la Ãēltima versiÃŗn menor.", "profile_image_of_user": "Foto de perfil de {user}", @@ -1381,7 +1415,7 @@ "public_share": "Compartir pÃēblicamente", "purchase_account_info": "Seguidor", "purchase_activated_subtitle": "Gracias por apoyar a Immich y al software de cÃŗdigo abierto", - "purchase_activated_time": "Activado el {date, date}", + "purchase_activated_time": "Activado el {date}", "purchase_activated_title": "Su clave ha sido activada correctamente", "purchase_button_activate": "Activar", "purchase_button_buy": "Comprar", @@ -1393,7 +1427,6 @@ "purchase_failed_activation": "ÂĄError al activar! ÂĄPor favor, revisa tu correo electrÃŗnico para obtener la clave del producto correcta!", "purchase_individual_description_1": "Para un usuario", "purchase_individual_description_2": "Estado de soporte", - "purchase_individual_title": "Individual", "purchase_input_suggestion": "ÂŋTiene una clave de producto? IntrodÃēzcala a continuaciÃŗn", "purchase_license_subtitle": "Compre Immich para apoyar el desarrollo continuo del servicio", "purchase_lifetime_description": "Compra de por vida", @@ -1426,6 +1459,8 @@ "recent_searches": "BÃēsquedas recientes", "recently_added": "AÃąadidos recientemente", "recently_added_page_title": "ReciÊn Agregadas", + "recently_taken": "Recientemente tomado", + "recently_taken_page_title": "Recientemente Tomado", "refresh": "Actualizar", "refresh_encoded_videos": "Recargar los vídeos codificados", "refresh_faces": "Actualizar caras", @@ -1445,6 +1480,8 @@ "remove_deleted_assets": "Eliminar archivos sin conexiÃŗn", "remove_from_album": "Eliminar del ÃĄlbum", "remove_from_favorites": "Quitar de favoritos", + "remove_from_locked_folder": "Eliminar de carpeta protegida", + "remove_from_locked_folder_confirmation": "ÂŋEstÃĄs seguro de que quieres mover estas fotos y vídeos de la carpeta protegida? SerÃĄn visibles en tu biblioteca", "remove_from_shared_link": "Eliminar desde enlace compartido", "remove_memory": "Quitar memoria", "remove_photo_from_memory": "Quitar foto de esta memoria", @@ -1468,6 +1505,7 @@ "reset": "Reiniciar", "reset_password": "Restablecer la contraseÃąa", "reset_people_visibility": "Restablecer la visibilidad de las personas", + "reset_pin_code": "Restablecer PIN", "reset_to_default": "Restablecer los valores predeterminados", "resolve_duplicates": "Resolver duplicados", "resolved_all_duplicates": "Todos los duplicados resueltos", @@ -1479,7 +1517,6 @@ "retry_upload": "Reintentar subida", "review_duplicates": "Revisar duplicados", "role": "Rol", - "role_editor": "Editor", "role_viewer": "Visor", "save": "Guardar", "save_to_gallery": "Guardado en la galería", @@ -1509,8 +1546,8 @@ "search_filter_date_interval": "{start} al {end}", "search_filter_date_title": "Selecciona un intervalo de fechas", "search_filter_display_option_not_in_album": "No en ÃĄlbum", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", + "search_filter_display_options": "Opciones de visualizaciÃŗn", + "search_filter_filename": "Buscar por nombre de archivo", "search_filter_location": "UbicaciÃŗn", "search_filter_location_title": "Seleccionar una ubicaciÃŗn", "search_filter_media_type": "Tipo de archivo", @@ -1518,10 +1555,10 @@ "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", - "search_no_more_result": "No more results", + "search_no_more_result": "No hay mÃĄs resultados", "search_no_people": "Ninguna persona", "search_no_people_named": "Ninguna persona llamada \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "No se encontraron resultados, prueba con un tÊrmino o combinaciÃŗn de bÃēsqueda diferente", "search_options": "Opciones de bÃēsqueda", "search_page_categories": "Categorías", "search_page_motion_photos": "Foto en Movimiento", @@ -1529,7 +1566,6 @@ "search_page_no_places": "No hay informaciÃŗn de lugares disponibles", "search_page_screenshots": "Capturas de pantalla", "search_page_search_photos_videos": "Busca tus fotos y videos", - "search_page_selfies": "Selfies", "search_page_things": "Cosas", "search_page_view_all_button": "Ver todo", "search_page_your_activity": "Tu actividad", @@ -1560,6 +1596,7 @@ "select_keep_all": "Conservar todo", "select_library_owner": "Seleccionar propietario de la biblioteca", "select_new_face": "Seleccionar nueva cara", + "select_person_to_tag": "Elija una persona a etiquetar", "select_photos": "Seleccionar Fotos", "select_trash_all": "Seleccionar eliminar todo", "select_user_for_sharing_page_err_album": "Fallo al crear el ÃĄlbum", @@ -1567,7 +1604,7 @@ "selected_count": "{count, plural, one {# seleccionado} other {# seleccionados}}", "send_message": "Enviar mensaje", "send_welcome_email": "Enviar correo de bienvenida", - "server_endpoint": "Server Endpoint", + "server_endpoint": "Punto final del servidor", "server_info_box_app_version": "VersiÃŗn de la AplicaciÃŗn", "server_info_box_server_url": "URL del servidor", "server_offline": "Servidor desconectado", @@ -1590,27 +1627,29 @@ "setting_languages_apply": "Aplicar", "setting_languages_subtitle": "Cambia el idioma de la aplicaciÃŗn", "setting_languages_title": "Idiomas", - "setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {}", - "setting_notifications_notify_hours": "{} horas", + "setting_notifications_notify_failures_grace_period": "Notificar fallos de copia de seguridad en segundo plano: {duration}", + "setting_notifications_notify_hours": "{count} horas", "setting_notifications_notify_immediately": "inmediatamente", - "setting_notifications_notify_minutes": "{} minutos", + "setting_notifications_notify_minutes": "{count} minutos", "setting_notifications_notify_never": "nunca", - "setting_notifications_notify_seconds": "{} segundos", + "setting_notifications_notify_seconds": "{count} segundos", "setting_notifications_single_progress_subtitle": "InformaciÃŗn detallada del progreso de subida de cada archivo", "setting_notifications_single_progress_title": "Mostrar progreso detallado de copia de seguridad en segundo plano", "setting_notifications_subtitle": "Ajusta tus preferencias de notificaciÃŗn", "setting_notifications_total_progress_subtitle": "Progreso general de subida (elementos completados/total)", "setting_notifications_total_progress_title": "Mostrar progreso total de copia de seguridad en segundo plano", "setting_video_viewer_looping_title": "Bucle", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_subtitle": "Al reproducir un video en streaming desde el servidor, reproducir el original incluso cuando haya una transcodificaciÃŗn disponible. Puede causar buffering. Los videos disponibles localmente se reproducen en calidad original independientemente de esta configuraciÃŗn.", + "setting_video_viewer_original_video_title": "Forzar vídeo original", "settings": "Ajustes", "settings_require_restart": "Por favor, reinicia Immich para aplicar este ajuste", "settings_saved": "Ajustes guardados", + "setup_pin_code": "Establecer un PIN", "share": "Compartir", "share_add_photos": "Agregar fotos", - "share_assets_selected": "{} seleccionados", + "share_assets_selected": "{count} seleccionado(s)", "share_dialog_preparing": "Preparando...", + "share_link": "Compartir Enlace", "shared": "Compartido", "shared_album_activities_input_disable": "Los comentarios estÃĄn deshabilitados", "shared_album_activity_remove_content": "ÂŋDeseas eliminar esta actividad?", @@ -1623,34 +1662,33 @@ "shared_by_user": "Compartido por {user}", "shared_by_you": "Compartido por ti", "shared_from_partner": "Fotos de {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Cargado(s)", "shared_link_app_bar_title": "Enlaces compartidos", "shared_link_clipboard_copied_massage": "Copiado al portapapeles", - "shared_link_clipboard_text": "Enlace: {}\nContraseÃąa: {}", + "shared_link_clipboard_text": "Enlace: {link}\nContraseÃąa: {password}", "shared_link_create_error": "Error creando el enlace compartido", "shared_link_edit_description_hint": "Introduce la descripciÃŗn del enlace", "shared_link_edit_expire_after_option_day": "1 día", - "shared_link_edit_expire_after_option_days": "{} días", + "shared_link_edit_expire_after_option_days": "{count} días", "shared_link_edit_expire_after_option_hour": "1 hora", - "shared_link_edit_expire_after_option_hours": "{} horas", + "shared_link_edit_expire_after_option_hours": "{count} horas", "shared_link_edit_expire_after_option_minute": "1 minuto", - "shared_link_edit_expire_after_option_minutes": "{} minutos", - "shared_link_edit_expire_after_option_months": "{} meses", - "shared_link_edit_expire_after_option_year": "{} aÃąo", + "shared_link_edit_expire_after_option_minutes": "{count} minutos", + "shared_link_edit_expire_after_option_months": "{count} meses", + "shared_link_edit_expire_after_option_year": "{count} aÃąo", "shared_link_edit_password_hint": "Introduce la contraseÃąa del enlace", "shared_link_edit_submit_button": "Actualizar enlace", "shared_link_error_server_url_fetch": "No se puede adquirir la URL del servidor", - "shared_link_expires_day": "Caduca en {} día", - "shared_link_expires_days": "Caduca en {} días", - "shared_link_expires_hour": "Caduca en {} hora", - "shared_link_expires_hours": "Caduca en {} horas", - "shared_link_expires_minute": "Caduca en {} minuto", - "shared_link_expires_minutes": "Caduca en {} minutos", + "shared_link_expires_day": "Caduca en {count} día", + "shared_link_expires_days": "Caduca en {count} días", + "shared_link_expires_hour": "Caduca en {count} hora", + "shared_link_expires_hours": "Caduca en {count} horas", + "shared_link_expires_minute": "Caduca en {count} minuto", + "shared_link_expires_minutes": "Caduca en {count} minutos", "shared_link_expires_never": "Caduca ∞", - "shared_link_expires_second": "Caduca en {} segundo", - "shared_link_expires_seconds": "Caduca en {} segundos", + "shared_link_expires_second": "Caduca en {count} segundo", + "shared_link_expires_seconds": "Caduca en {count} segundos", "shared_link_individual_shared": "Compartido individualmente", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrar enlaces compartidos", "shared_link_options": "Opciones de enlaces compartidos", "shared_links": "Enlaces compartidos", @@ -1723,6 +1761,7 @@ "stop_sharing_photos_with_user": "Deja de compartir tus fotos con este usuario", "storage": "Espacio de almacenamiento", "storage_label": "Etiqueta de almacenamiento", + "storage_quota": "Cuota de Almacenamiento", "storage_usage": "{used} de {available} en uso", "submit": "Enviar", "suggestions": "Sugerencias", @@ -1734,7 +1773,7 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar ÃĄlbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los ÃĄlbumes seleccionados a respaldar", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los ÃĄlbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", "tag_created": "Etiqueta creada: {tag}", @@ -1749,13 +1788,13 @@ "theme_selection": "SelecciÃŗn de tema", "theme_selection_description": "Establece el tema automÃĄticamente como \"claro\" u \"oscuro\" segÃēn las preferencias del sistema/navegador", "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamiento en las miniaturas de los archivos", - "theme_setting_asset_list_tiles_per_row_title": "NÃēmero de elementos por fila ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "NÃēmero de elementos por fila ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicar el color primario a las superficies de fondo.", + "theme_setting_colorful_interface_title": "Color de Interfaz", "theme_setting_image_viewer_quality_subtitle": "Ajustar la calidad del visor de detalles de imÃĄgenes", "theme_setting_image_viewer_quality_title": "Calidad del visor de imÃĄgenes", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", + "theme_setting_primary_color_subtitle": "Elige un color para las acciones principales y los acentos.", + "theme_setting_primary_color_title": "Color primario", "theme_setting_system_primary_color_title": "Usar color del sistema", "theme_setting_system_theme_switch": "AutomÃĄtico (seguir ajuste del sistema)", "theme_setting_theme_subtitle": "Elige la configuraciÃŗn del tema de la aplicaciÃŗn", @@ -1774,7 +1813,6 @@ "to_trash": "Descartar", "toggle_settings": "Alternar ajustes", "toggle_theme": "Alternar tema oscuro", - "total": "Total", "total_usage": "Uso total", "trash": "Papelera", "trash_all": "Descartar todo", @@ -1784,13 +1822,15 @@ "trash_no_results_message": "Las fotos y videos que se envíen a la papelera aparecerÃĄn aquí.", "trash_page_delete_all": "Eliminar todos", "trash_page_empty_trash_dialog_content": "ÂŋEstÃĄ seguro que quiere eliminar los elementos? Estos elementos serÃĄn eliminados de Immich permanentemente", - "trash_page_info": "Los archivos en la papelera serÃĄn eliminados automÃĄticamente despuÊs de {} días", + "trash_page_info": "Los archivos en la papelera serÃĄn eliminados automÃĄticamente de forma permanente despuÊs de {days} días", "trash_page_no_assets": "No hay elementos en la papelera", "trash_page_restore_all": "Restaurar todos", "trash_page_select_assets_btn": "Seleccionar elementos", - "trash_page_title": "Papelera ({})", + "trash_page_title": "Papelera ({count})", "trashed_items_will_be_permanently_deleted_after": "Los elementos en la papelera serÃĄn eliminados permanentemente tras {days, plural, one {# día} other {# días}}.", "type": "Tipo", + "unable_to_change_pin_code": "No se ha podido cambiar el PIN", + "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", "unfavorite": "Retirar favorito", @@ -1812,32 +1852,36 @@ "unstack": "Desapilar", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", "untracked_files": "Archivos no monitorizados", - "untracked_files_decription": "Estos archivos no estÃĄn siendo monitorizados por la aplicaciÃŗn. Es posible que sean resultado de errores al moverlos, cargas interrumpidas o por un fallo de la aplicaciÃŗn", + "untracked_files_decription": "Estos archivos no estÃĄn siendo monitorizados por la aplicaciÃŗn. Es posible que sean resultado de errores al moverlos, subidas interrumpidas o por un fallo de la aplicaciÃŗn", "up_next": "A continuaciÃŗn", + "updated_at": "Actualizado", "updated_password": "ContraseÃąa actualizada", "upload": "Subir", - "upload_concurrency": "Cargas simultÃĄneas", + "upload_concurrency": "Subidas simultÃĄneas", "upload_dialog_info": "ÂŋQuieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", - "upload_errors": "Carga completada con {count, plural, one {# error} other {# errores}}, actualice la pÃĄgina para ver los nuevos recursos de carga.", + "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la pÃĄgina para ver los nuevos recursos de la subida.", "upload_progress": "Restante {remaining, number} - Procesado {processed, number}/{total, number}", "upload_skipped_duplicates": "Saltado {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Errores", "upload_status_uploaded": "Subido", - "upload_success": "Carga realizada correctamente, actualice la pÃĄgina para ver los nuevos recursos de carga.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_success": "Subida realizada correctamente, actualice la pÃĄgina para ver los nuevos recursos de subida.", + "upload_to_immich": "Subir a Immich ({count})", + "uploading": "Subiendo", "usage": "Uso", - "use_current_connection": "use current connection", + "use_biometric": "Uso biomÊtrico", + "use_current_connection": "Usar conexiÃŗn actual", "use_custom_date_range": "Usa un intervalo de fechas personalizado", "user": "Usuario", + "user_has_been_deleted": "Este usuario ha sido eliminado.", "user_id": "ID de usuario", "user_liked": "{user} le gustÃŗ {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_pin_code_settings": "PIN", + "user_pin_code_settings_description": "Gestione su PIN", "user_purchase_settings": "Compra", "user_purchase_settings_description": "Gestiona tu compra", - "user_role_set": "Carbiar {user} a {role}", + "user_role_set": "Establecer {user} como {role}", "user_usage_detail": "Detalle del uso del usuario", "user_usage_stats": "Estadísticas de uso de la cuenta", "user_usage_stats_description": "Ver estadísticas de uso de la cuenta", @@ -1845,8 +1889,7 @@ "users": "Usuarios", "utilities": "Utilidades", "validate": "Validar", - "validate_endpoint_error": "Please enter a valid URL", - "variables": "Variables", + "validate_endpoint_error": "Por favor, introduce una URL vÃĄlida", "version": "VersiÃŗn", "version_announcement_closing": "Tu amigo, Alex", "version_announcement_message": "ÂĄHola! Hay una nueva versiÃŗn de Immich disponible. TÃŗmese un tiempo para leer las notas de la versiÃŗn para asegurarse de que su configuraciÃŗn estÊ actualizada y evitar errores de configuraciÃŗn, especialmente si utiliza WatchTower o cualquier mecanismo que se encargue de actualizar su instancia de Immich automÃĄticamente.", @@ -1883,11 +1926,12 @@ "week": "Semana", "welcome": "Bienvenido", "welcome_to_immich": "Bienvenido a Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nombre Wi-Fi", + "wrong_pin_code": "CÃŗdigo PIN incorrecto", "year": "AÃąo", "years_ago": "Hace {years, plural, one {# aÃąo} other {# aÃąos}}", "yes": "Sí", "you_dont_have_any_shared_links": "No tienes ningÃēn enlace compartido", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "El nombre de tu Wi-Fi", "zoom_image": "Acercar Imagen" } diff --git a/i18n/et.json b/i18n/et.json index 673d2ea63a..fa621118d1 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -4,6 +4,7 @@ "account_settings": "Konto seaded", "acknowledge": "Sain aru", "action": "Tegevus", + "action_common_update": "Uuenda", "actions": "Tegevused", "active": "Aktiivne", "activity": "Aktiivsus", @@ -13,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_endpoint": "Lisa lÃĩpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", "add_location": "Lisa asukoht", @@ -22,6 +24,9 @@ "add_photos": "Lisa fotosid", "add_to": "Lisa kohtaâ€Ļ", "add_to_album": "Lisa albumisse", + "add_to_album_bottom_sheet_added": "Lisatud albumisse {album}", + "add_to_album_bottom_sheet_already_exists": "On juba albumis {album}", + "add_to_locked_folder": "Lisa lukustatud kausta", "add_to_shared_album": "Lisa jagatud albumisse", "add_url": "Lisa URL", "added_to_archive": "Lisatud arhiivi", @@ -35,11 +40,11 @@ "authentication_settings_disable_all": "Kas oled kindel, et soovid kÃĩik sisselogimismeetodid välja lÃŧlitada? Sisselogimine lÃŧlitatakse täielikult välja.", "authentication_settings_reenable": "Et taas lubada, kasuta serveri käsku.", "background_task_job": "Tausttegumid", - "backup_database": "Varunda andmebaas", - "backup_database_enable_description": "Luba andmebaasi varundamine", - "backup_keep_last_amount": "Varukoopiate arv, mida alles hoida", - "backup_settings": "Varundamise seaded", - "backup_settings_description": "Halda andmebaasi varundamise seadeid", + "backup_database": "Loo andmebaasi tÃĩmmis", + "backup_database_enable_description": "Luba andmebaasi tÃĩmmised", + "backup_keep_last_amount": "Eelmiste tÃĩmmiste arv, mida alles hoida", + "backup_settings": "Andmebaasi tÃĩmmiste seaded", + "backup_settings_description": "Halda andmebaasi tÃĩmmiste seadeid. Märkus: Neid tÃļÃļteid ei jälgita ning ebaÃĩnnestumisest ei hoiatata.", "check_all": "Märgi kÃĩik", "cleanup": "Koristus", "cleared_jobs": "TÃļÃļted eemaldatud: {job}", @@ -49,6 +54,7 @@ "confirm_email_below": "Kinnitamiseks sisesta allpool \"{email}\"", "confirm_reprocess_all_faces": "Kas oled kindel, et soovid kÃĩik näod uuesti tÃļÃļdelda? See eemaldab kÃĩik nimega isikud.", "confirm_user_password_reset": "Kas oled kindel, et soovid kasutaja {user} parooli lähtestada?", + "confirm_user_pin_code_reset": "Kas oled kindel, et soovid kasutaja {user} PIN-koodi lähtestada?", "create_job": "Lisa tÃļÃļde", "cron_expression": "Cron avaldis", "cron_expression_description": "Sea skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", @@ -188,26 +194,22 @@ "oauth_auto_register": "Automaatne registreerimine", "oauth_auto_register_description": "Registreeri uued kasutajad automaatselt OAuth abil sisselogimisel", "oauth_button_text": "Nupu tekst", - "oauth_client_id": "Kliendi ID", - "oauth_client_secret": "Kliendi saladus", + "oauth_client_secret_description": "NÃĩutud, kui PKCE (Proof Key for Code Exchange) ei ole OAuth pakkuja poolt toetatud", "oauth_enable_description": "Sisene OAuth abil", - "oauth_issuer_url": "Väljastaja URL", "oauth_mobile_redirect_uri": "Mobiilne Ãŧmbersuunamise URI", "oauth_mobile_redirect_uri_override": "Mobiilse Ãŧmbersuunamise URI Ãŧlekirjutamine", "oauth_mobile_redirect_uri_override_description": "LÃŧlita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks '{callback}'", - "oauth_profile_signing_algorithm": "Profiili allkirjastamise algoritm", - "oauth_profile_signing_algorithm_description": "Algoritm, mida kasutatakse kasutajaprofiili allkirjastamiseks.", - "oauth_scope": "Skoop", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", - "oauth_signing_algorithm": "Allkirjastamise algoritm", "oauth_storage_label_claim": "Talletussildi väide", "oauth_storage_label_claim_description": "Sea kasutaja talletussildiks automaatselt selle väite väärtus.", "oauth_storage_quota_claim": "Talletuskvoodi väide", "oauth_storage_quota_claim_description": "Sea kasutaja talletuskvoodiks automaatselt selle väite väärtus.", "oauth_storage_quota_default": "Vaikimisi talletuskvoot (GiB)", "oauth_storage_quota_default_description": "Kvoot (GiB), mida kasutada, kui Ãŧhtegi väidet pole esitatud (piiramatu kvoodi jaoks sisesta 0).", + "oauth_timeout": "Päringu ajalÃĩpp", + "oauth_timeout_description": "Päringute ajalÃĩpp millisekundites", "offline_paths": "Ühenduseta failiteed", "offline_paths_description": "Need tulemused vÃĩivad olla pÃĩhjustatud manuaalselt kustutatud failidest, mis ei ole osa välisest kogust.", "password_enable_description": "Logi sisse e-posti aadressi ja parooliga", @@ -266,7 +268,7 @@ "template_email_update_album": "Albumi muutmise mall", "template_email_welcome": "Tervituskirja mall", "template_settings": "Teavituse mallid", - "template_settings_description": "Teavituste mallide haldamine.", + "template_settings_description": "Teavituste mallide haldamine", "theme_custom_css_settings": "Kohandatud CSS", "theme_custom_css_settings_description": "Cascading Style Sheets lubab Immich'i kujunduse kohandamist.", "theme_settings": "Teema seaded", @@ -348,6 +350,7 @@ "user_delete_delay_settings_description": "Päevade arv, pärast mida kustutatakse eemaldatud kasutaja konto ja Ãŧksused jäädavalt. Kasutajate kustutamise tÃļÃļde käivitub keskÃļÃļl, et otsida kustutamiseks valmis kasutajaid. Selle seadistuse muudatused rakenduvad järgmisel käivitumisel.", "user_delete_immediately": "Kasutaja {user} konto ja Ãŧksused suunatakse koheselt jäädavale kustutamisele.", "user_delete_immediately_checkbox": "Suuna kasutaja ja Ãŧksused jäädavale kustutamisele", + "user_details": "Kasutaja detailid", "user_management": "Kasutajate haldus", "user_password_has_been_reset": "Kasutaja parool on lähtestatud:", "user_password_reset_description": "Sisesta kasutajale ajutine parool ja teavita teda, et järgmisel sisselogimisel tuleb parool ära muuta.", @@ -367,6 +370,20 @@ "admin_password": "Administraatori parool", "administration": "Administratsioon", "advanced": "Täpsemad valikud", + "advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sÃŧnkroonimise ajal Ãŧksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kÃĩigi albumite tuvastamisega.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sÃŧnkroonimise filtrit", + "advanced_settings_log_level_title": "Logimistase: {level}", + "advanced_settings_prefer_remote_subtitle": "MÃĩned seadmed laadivad seadmes olevate Ãŧksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", + "advanced_settings_prefer_remote_title": "Eelista kaugpilte", + "advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma", + "advanced_settings_proxy_headers_title": "Vaheserveri päised", + "advanced_settings_self_signed_ssl_subtitle": "Jätab serveri lÃĩpp-punkti SSL-sertifikaadi kontrolli vahele. NÃĩutud endasigneeritud sertifikaatide jaoks.", + "advanced_settings_self_signed_ssl_title": "Luba endasigneeritud SSL-sertifikaadid", + "advanced_settings_sync_remote_deletions_subtitle": "Kustuta vÃĩi taasta Ãŧksus selles seadmes automaatself, kui sama tegevus toimub veebis", + "advanced_settings_sync_remote_deletions_title": "SÃŧnkrooni kaugkustutamised [EKSPERIMENTAALNE]", + "advanced_settings_tile_subtitle": "EdasijÃĩudnud kasutajate seaded", + "advanced_settings_troubleshooting_subtitle": "Luba lisafunktsioonid tÃĩrkeotsinguks", + "advanced_settings_troubleshooting_title": "TÃĩrkeotsing", "age_months": "Vanus {months, plural, one {# kuu} other {# kuud}}", "age_year_months": "Vanus 1 aasta, {months, plural, one {# kuu} other {# kuud}}", "age_years": "{years, plural, other {Vanus #}}", @@ -375,6 +392,8 @@ "album_cover_updated": "Albumi kaanepilt muudetud", "album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?", "album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.", + "album_info_card_backup_album_excluded": "VÄLJA JÄETUD", + "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", "album_leave": "Lahku albumist?", "album_leave_confirmation": "Kas oled kindel, et soovid albumist {album} lahkuda?", @@ -383,10 +402,22 @@ "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", "album_share_no_users": "Paistab, et oled seda albumit kÃĩikide kasutajatega jaganud, vÃĩi pole Ãŧhtegi kasutajat, kellega jagada.", + "album_thumbnail_card_item": "1 Ãŧksus", + "album_thumbnail_card_items": "{count} Ãŧksust", + "album_thumbnail_card_shared": " ¡ Jagatud", + "album_thumbnail_shared_by": "Jagas {user}", "album_updated": "Album muudetud", "album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi Ãŧksuseid", "album_user_left": "Lahkutud albumist {album}", "album_user_removed": "Kasutaja {user} eemaldatud", + "album_viewer_appbar_delete_confirm": "Kas oled kindel, et soovid selle albumi oma kontolt kustutada?", + "album_viewer_appbar_share_err_delete": "Albumi kustutamine ebaÃĩnnestus", + "album_viewer_appbar_share_err_leave": "Albumist lahkumine ebaÃĩnnestus", + "album_viewer_appbar_share_err_remove": "Üksuste albumist eemaldamisel tekkis probleeme", + "album_viewer_appbar_share_err_title": "Albumi pealkirja muutmine ebaÃĩnnestus", + "album_viewer_appbar_share_leave": "Lahku albumist", + "album_viewer_appbar_share_to": "Jaga", + "album_viewer_page_share_add_users": "Lisa kasutajaid", "album_with_link_access": "Luba kÃĩigil, kellel on link, näha selle albumi fotosid ja isikuid.", "albums": "Albumid", "albums_count": "{count, plural, one {{count, number} album} other {{count, number} albumit}}", @@ -404,43 +435,136 @@ "api_key_description": "Seda väärtust kuvatakse ainult Ãŧks kord. Kopeeri see enne akna sulgemist.", "api_key_empty": "Su API vÃĩtme nimi ei tohiks olla tÃŧhi", "api_keys": "API vÃĩtmed", + "app_bar_signout_dialog_content": "Kas oled kindel, et soovid välja logida?", + "app_bar_signout_dialog_ok": "Jah", + "app_bar_signout_dialog_title": "Logi välja", "app_settings": "Rakenduse seaded", "appears_in": "Albumid", "archive": "Arhiiv", "archive_or_unarchive_photo": "Arhiveeri vÃĩi taasta foto", + "archive_page_no_archived_assets": "Arhiveeritud Ãŧksuseid ei leitud", + "archive_page_title": "Arhiveeri ({count})", "archive_size": "Arhiivi suurus", "archive_size_description": "Seadista arhiivi suurus allalaadimiseks (GiB)", + "archived": "Arhiveeritud", "archived_count": "{count, plural, other {# arhiveeritud}}", "are_these_the_same_person": "Kas need on sama isik?", "are_you_sure_to_do_this": "Kas oled kindel, et soovid seda teha?", + "asset_action_delete_err_read_only": "Kirjutuskaitstud Ãŧksuseid ei saa kustutada, jäetakse vahele", + "asset_action_share_err_offline": "Ühenduseta Ãŧksuseid ei saa pärida, jäetakse vahele", "asset_added_to_album": "Lisatud albumisse", "asset_adding_to_album": "Albumisse lisamineâ€Ļ", "asset_description_updated": "Üksuse kirjeldus on muudetud", "asset_filename_is_offline": "Üksus {filename} ei ole kättesaadav", "asset_has_unassigned_faces": "Üksusel on seostamata nägusid", "asset_hashing": "Räsimineâ€Ļ", + "asset_list_group_by_sub_title": "Grupeeri", + "asset_list_layout_settings_dynamic_layout_title": "DÃŧnaamiline asetus", + "asset_list_layout_settings_group_automatically": "Automaatne", + "asset_list_layout_settings_group_by": "Grupeeri Ãŧksused", + "asset_list_layout_settings_group_by_month_day": "Kuu + päev", + "asset_list_layout_sub_title": "Asetus", + "asset_list_settings_subtitle": "Fotoruudustiku asetuse sätted", + "asset_list_settings_title": "Fotoruudustik", "asset_offline": "Üksus pole kättesaadav", "asset_offline_description": "Seda välise kogu Ãŧksust ei leitud kettalt. Abi saamiseks palun vÃĩta Ãŧhendust oma Immich'i administraatoriga.", + "asset_restored_successfully": "Üksus edukalt taastatud", "asset_skipped": "Vahele jäetud", "asset_skipped_in_trash": "PrÃŧgikastis", "asset_uploaded": "Üleslaaditud", "asset_uploading": "Üleslaadimineâ€Ļ", + "asset_viewer_settings_subtitle": "Halda galeriivaaturi seadeid", + "asset_viewer_settings_title": "Üksuste vaatur", "assets": "Üksused", "assets_added_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} lisatud", "assets_added_to_album_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} albumisse lisatud", "assets_added_to_name_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} lisatud {hasName, select, true {albumisse {name}} other {uude albumisse}}", "assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", + "assets_deleted_permanently": "{count} Ãŧksus(t) jäädavalt kustutatud", + "assets_deleted_permanently_from_server": "{count} Ãŧksus(t) Immich'i serverist jäädavalt kustutatud", "assets_moved_to_trash_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud prÃŧgikasti", "assets_permanently_deleted_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} jäädavalt kustutatud", "assets_removed_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} eemaldatud", + "assets_removed_permanently_from_device": "{count} Ãŧksus(t) seadmest jäädavalt eemaldatud", "assets_restore_confirmation": "Kas oled kindel, et soovid oma prÃŧgikasti liigutatud Ãŧksused taastada? Seda ei saa tagasi vÃĩtta! Pane tähele, et sel meetodil ei saa taastada Ãŧhenduseta Ãŧksuseid.", "assets_restored_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} taastatud", + "assets_restored_successfully": "{count} Ãŧksus(t) edukalt taastatud", + "assets_trashed": "{count} Ãŧksus(t) liigutatud prÃŧgikasti", "assets_trashed_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud prÃŧgikasti", + "assets_trashed_from_server": "{count} Ãŧksus(t) liigutatud Immich'i serveris prÃŧgikasti", "assets_were_part_of_album_count": "{count, plural, one {Üksus oli} other {Üksused olid}} juba osa albumist", "authorized_devices": "Autoriseeritud seadmed", + "automatic_endpoint_switching_subtitle": "Ühendu lokaalselt Ãŧle valitud WiFi-vÃĩrgu, kui see on saadaval, ja kasuta mujal alternatiivseid Ãŧhendusi", + "automatic_endpoint_switching_title": "Automaatne URL-i ÃŧmberlÃŧlitamine", "back": "Tagasi", "back_close_deselect": "Tagasi, sulge vÃĩi tÃŧhista valik", + "background_location_permission": "Taustal asukoha luba", + "background_location_permission_content": "Et taustal tÃļÃļtades vÃĩrguÃŧhendust vahetada, peab Immich'il *alati* olema täpse asukoha luba, et rakendus saaks WiFi-vÃĩrgu nime lugeda", + "backup_album_selection_page_albums_device": "Albumid seadmel ({count})", + "backup_album_selection_page_albums_tap": "Puuduta kaasamiseks, topeltpuuduta välistamiseks", + "backup_album_selection_page_assets_scatter": "Üksused vÃĩivad olla jaotatud mitme albumi vahel. Seega saab albumeid varundamise protsessi kaasata vÃĩi välistada.", + "backup_album_selection_page_select_albums": "Vali albumid", + "backup_album_selection_page_selection_info": "Valiku info", + "backup_album_selection_page_total_assets": "Unikaalseid Ãŧksuseid kokku", + "backup_all": "KÃĩik", + "backup_background_service_backup_failed_message": "Üksuste varundamine ebaÃĩnnestus. Uuesti proovimineâ€Ļ", + "backup_background_service_connection_failed_message": "Serveriga Ãŧhendumine ebaÃĩnnestus. Uuesti proovimineâ€Ļ", + "backup_background_service_current_upload_notification": "{filename} Ãŧleslaadimine", + "backup_background_service_default_notification": "Uute Ãŧksuste kontrollimineâ€Ļ", + "backup_background_service_error_title": "Varundamise viga", + "backup_background_service_in_progress_notification": "Sinu Ãŧksuste varundamineâ€Ļ", + "backup_background_service_upload_failure_notification": "Faili {filename} Ãŧleslaadimine ebaÃĩnnestus", + "backup_controller_page_albums": "Varunduse albumid", + "backup_controller_page_background_app_refresh_disabled_content": "Taustal varundamise kasutamiseks luba rakenduse taustal värskendamine: Seaded > Üldine > Rakenduse taustal värskendamine.", + "backup_controller_page_background_app_refresh_disabled_title": "Rakenduse taustal värskendamine keelatud", + "backup_controller_page_background_app_refresh_enable_button_text": "Mine seadetesse", + "backup_controller_page_background_battery_info_link": "Näita mulle, kuidas", + "backup_controller_page_background_battery_info_message": "Parima taustal varundamise kogemuse jaoks palun keela Immich'i puhul kÃĩik taustategevust piiravad aku optimeerimised.\n\nKuna see on seadmespetsiifiline, otsi vajalikku teavet oma seadme tootja kohta.", + "backup_controller_page_background_battery_info_title": "Aku optimeerimised", + "backup_controller_page_background_charging": "Ainult laadimise ajal", + "backup_controller_page_background_configure_error": "Taustateenuse seadistamine ebaÃĩnnestus", + "backup_controller_page_background_delay": "Oota uute Ãŧksuste varundamisega: {duration}", + "backup_controller_page_background_description": "LÃŧlita taustateenus sisse, et uusi Ãŧksuseid automaatselt varundada, ilma et peaks rakendust avama", + "backup_controller_page_background_is_off": "Automaatne taustal varundamine on välja lÃŧlitatud", + "backup_controller_page_background_is_on": "Automaatne taustal varundamine on sisse lÃŧlitatud", + "backup_controller_page_background_turn_off": "LÃŧlita taustateenus välja", + "backup_controller_page_background_turn_on": "LÃŧlita taustateenus sisse", + "backup_controller_page_background_wifi": "Ainult WiFi-vÃĩrgus", + "backup_controller_page_backup": "Varundamine", + "backup_controller_page_backup_selected": "Valitud: ", + "backup_controller_page_backup_sub": "Varundatud fotod ja videod", + "backup_controller_page_created": "Lisatud: {date}", + "backup_controller_page_desc_backup": "LÃŧlita sisse esiplaanil varundamine, et rakenduse avamisel uued Ãŧksused automaatselt serverisse Ãŧles laadida.", + "backup_controller_page_excluded": "Välistatud: ", + "backup_controller_page_failed": "EbaÃĩnnestunud ({count})", + "backup_controller_page_filename": "Failinimi: {filename} [{size}]", + "backup_controller_page_info": "Varunduse info", + "backup_controller_page_none_selected": "Ühtegi pole valitud", + "backup_controller_page_remainder": "Ootel", + "backup_controller_page_remainder_sub": "Valitud fotod ja videod, mis on veel varundamise ootel", + "backup_controller_page_server_storage": "Serveri talletusruum", + "backup_controller_page_start_backup": "Alusta varundamist", + "backup_controller_page_status_off": "Automaatne esiplaanil varundamine on välja lÃŧlitatud", + "backup_controller_page_status_on": "Automaatne esiplaanil varundamine on sisse lÃŧlitatud", + "backup_controller_page_storage_format": "{used}/{total} kasutusel", + "backup_controller_page_to_backup": "Albumid, mida varundada", + "backup_controller_page_total_sub": "KÃĩik unikaalsed fotod ja videod valitud albumitest", + "backup_controller_page_turn_off": "LÃŧlita esiplaanil varundus välja", + "backup_controller_page_turn_on": "LÃŧlita esiplaanil varundus sisse", + "backup_controller_page_uploading_file_info": "Faili info Ãŧleslaadimine", + "backup_err_only_album": "Ei saa ainsat albumit eemaldada", + "backup_info_card_assets": "Ãŧksused", + "backup_manual_cancelled": "TÃŧhistatud", + "backup_manual_in_progress": "Üleslaadimine juba käib. Proovi hiljem uuesti", + "backup_manual_success": "Õnnestus", + "backup_manual_title": "Üleslaadimise staatus", + "backup_options_page_title": "Varundamise valikud", + "backup_setting_subtitle": "Halda taustal ja esiplaanil Ãŧleslaadimise seadeid", "backward": "Tagasi", + "biometric_auth_enabled": "Biomeetriline autentimine lubatud", + "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", + "biometric_no_options": "Biomeetrilisi valikuid ei ole", + "biometric_not_available": "Biomeetriline autentimine ei ole selles seadmes saadaval", "birthdate_saved": "SÃŧnnikuupäev salvestatud", "birthdate_set_description": "SÃŧnnikuupäeva kasutatakse isiku vanuse arvutamiseks foto tegemise hetkel.", "blurred_background": "Udustatud taust", @@ -451,24 +575,55 @@ "bulk_keep_duplicates_confirmation": "Kas oled kindel, et soovid {count, plural, one {# dubleeritud Ãŧksuse} other {# dubleeritud Ãŧksust}} alles jätta? Sellega märgitakse kÃĩik duplikaadigrupid lahendatuks ilma midagi kustutamata.", "bulk_trash_duplicates_confirmation": "Kas oled kindel, et soovid {count, plural, one {# dubleeritud Ãŧksuse} other {# dubleeritud Ãŧksust}} masskustutada? Sellega jäetakse alles iga grupi suurim Ãŧksus ning duplikaadid liigutatakse prÃŧgikasti.", "buy": "Osta Immich", + "cache_settings_album_thumbnails": "Kogu lehtede pisipildid ({count} Ãŧksust)", + "cache_settings_clear_cache_button": "TÃŧhjenda puhver", + "cache_settings_clear_cache_button_title": "TÃŧhjendab rakenduse puhvri. See mÃĩjutab oluliselt rakenduse jÃĩudlust, kuni puhver uuesti täidetakse.", + "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_title": "Dubleeritud Ãŧksused ({count})", + "cache_settings_image_cache_size": "Piltide puhvri suurus ({count} Ãŧksust)", + "cache_settings_statistics_album": "Kogu pisipildid", + "cache_settings_statistics_assets": "{count} Ãŧksust ({size})", + "cache_settings_statistics_full": "TäismÃĩÃĩdus pildid", + "cache_settings_statistics_shared": "Jagatud albumite pisipildid", + "cache_settings_statistics_thumbnail": "Pisipildid", + "cache_settings_statistics_title": "Puhvri kasutus", + "cache_settings_subtitle": "Juhi Immich'i rakenduse puhverdamist", + "cache_settings_thumbnail_size": "Pisipiltide puhvri suurus ({count} Ãŧksust)", + "cache_settings_tile_subtitle": "Juhi lokaalse talletuse käitumist", + "cache_settings_tile_title": "Lokaalne talletus", + "cache_settings_title": "Puhverdamise seaded", "camera": "Kaamera", "camera_brand": "Kaamera mark", "camera_model": "Kaamera mudel", "cancel": "Katkesta", "cancel_search": "Katkesta otsing", + "canceled": "TÃŧhistatud", "cannot_merge_people": "Ei saa isikuid Ãŧhendada", "cannot_undo_this_action": "Sa ei saa seda tagasi vÃĩtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaÃĩnnestus", + "cast": "Edasta", "change_date": "Muuda kuupäeva", + "change_description": "Muuda kirjeldust", + "change_display_order": "Muuda kuva järjekorda", "change_expiration_time": "Muuda aegumisaega", "change_location": "Muuda asukohta", "change_name": "Muuda nime", "change_name_successfully": "Nimi edukalt muudetud", "change_password": "Parooli muutmine", "change_password_description": "See on su esimene kord sÃŧsteemi siseneda, vÃĩi on tehtud taotlus parooli muutmiseks. Palun sisesta allpool uus parool.", + "change_password_form_confirm_password": "Kinnita parool", + "change_password_form_description": "Hei {name},\n\nSa kas logid sÃŧsteemi esimest korda sisse, vÃĩi on esitatud taotlus sinu parooli muutmiseks. Palun sisesta allpool uus parool.", + "change_password_form_new_password": "Uus parool", + "change_password_form_password_mismatch": "Paroolid ei klapi", + "change_password_form_reenter_new_password": "Korda uut parooli", + "change_pin_code": "Muuda PIN-koodi", "change_your_password": "Muuda oma parooli", "changed_visibility_successfully": "Nähtavus muudetud", "check_all": "Märgi kÃĩik", + "check_corrupt_asset_backup": "Otsi riknenud Ãŧksuste varukoopiaid", + "check_corrupt_asset_backup_button": "Teosta kontroll", + "check_corrupt_asset_backup_description": "Käivita see kontroll ainult WiFi-vÃĩrgus ja siis, kui kÃĩik Ãŧksused on varundatud. See protseduur vÃĩib kesta mÃĩne minuti.", "check_logs": "Vaata logisid", "choose_matching_people_to_merge": "Vali kattuvad isikud, mida Ãŧhendada", "city": "Linn", @@ -477,6 +632,13 @@ "clear_all_recent_searches": "TÃŧhjenda hiljutised otsingud", "clear_message": "TÃŧhjenda sÃĩnum", "clear_value": "TÃŧhjenda väärtus", + "client_cert_enter_password": "Sisesta parool", + "client_cert_import": "Impordi", + "client_cert_import_success_msg": "Klientsertifikaat on imporditud", + "client_cert_invalid_msg": "Vigane sertifikaadi fail vÃĩi vale parool", + "client_cert_remove_msg": "Klientsertifikaat on eemaldatud", + "client_cert_subtitle": "Toetab ainult PKCS12 (.p12, .pfx) formaati. Sertifikaadi importimine/eemaldamine on saadaval ainult enne sisselogimist", + "client_cert_title": "SSL klientsertifikaat", "clockwise": "Päripäeva", "close": "Sulge", "collapse": "Peida", @@ -487,15 +649,29 @@ "comment_options": "Kommentaari valikud", "comments_and_likes": "Kommentaarid ja meeldimised", "comments_are_disabled": "Kommentaarid on keelatud", + "common_create_new_album": "Lisa uus album", + "common_server_error": "Kontrolli oma vÃĩrguÃŧhendust ja veendu, et server on kättesaadav ning rakenduse ja serveri versioonid on Ãŧhilduvad.", + "completed": "LÃĩpetatud", "confirm": "Kinnita", "confirm_admin_password": "Kinnita administraatori parool", "confirm_delete_face": "Kas oled kindel, et soovid isiku {name} näo Ãŧksuselt kustutada?", "confirm_delete_shared_link": "Kas oled kindel, et soovid selle jagatud lingi kustutada?", "confirm_keep_this_delete_others": "KÃĩik muud Ãŧksused selles virnas kustutatakse. Kas oled kindel, et soovid jätkata?", + "confirm_new_pin_code": "Kinnita uus PIN-kood", "confirm_password": "Kinnita parool", + "connected_to": "Ühendatud seadmega", "contain": "Mahuta ära", "context": "Kontekst", "continue": "Jätka", + "control_bottom_app_bar_album_info_shared": "{count} Ãŧksust ¡ Jagatud", + "control_bottom_app_bar_create_new_album": "Lisa uus album", + "control_bottom_app_bar_delete_from_immich": "Kustuta Immich'ist", + "control_bottom_app_bar_delete_from_local": "Kustuta seadmest", + "control_bottom_app_bar_edit_location": "Muuda asukohta", + "control_bottom_app_bar_edit_time": "Muuda kuupäeva ja aega", + "control_bottom_app_bar_share_link": "Jaga linki", + "control_bottom_app_bar_share_to": "Jaga", + "control_bottom_app_bar_trash_from_immich": "Liiguta prÃŧgikasti", "copied_image_to_clipboard": "Pilt kopeeritud lÃĩikelauale.", "copied_to_clipboard": "Kopeeritud lÃĩikelauale!", "copy_error": "Kopeeri viga", @@ -510,24 +686,36 @@ "covers": "Kaanepildid", "create": "Lisa", "create_album": "Lisa album", + "create_album_page_untitled": "Pealkirjata", "create_library": "Lisa kogu", "create_link": "Lisa link", "create_link_to_share": "Lisa jagamiseks link", "create_link_to_share_description": "Luba kÃĩigil, kellel on link, valitud pilte näha", + "create_new": "LISA UUS", "create_new_person": "Lisa uus isik", "create_new_person_hint": "Seosta valitud Ãŧksused uue isikuga", "create_new_user": "Lisa uus kasutaja", + "create_shared_album_page_share_add_assets": "LISA ÜKSUSEID", + "create_shared_album_page_share_select_photos": "Vali fotod", "create_tag": "Lisa silt", "create_tag_description": "Lisa uus silt. Pesastatud siltide jaoks sisesta täielik tee koos kaldkriipsudega.", "create_user": "Lisa kasutaja", "created": "Lisatud", + "created_at": "Lisatud", + "crop": "Kärpimine", + "curated_object_page_title": "Asjad", "current_device": "Praegune seade", + "current_pin_code": "Praegune PIN-kood", + "current_server_address": "Praegune serveri aadress", "custom_locale": "Kohandatud lokaat", "custom_locale_description": "Vorminda kuupäevad ja arvud vastavalt keelele ja regioonile", + "daily_title_text_date": "d. MMMM", + "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", + "date_format": "d. MMMM y â€ĸ HH:mm", "date_of_birth_saved": "SÃŧnnikuupäev salvestatud", "date_range": "Kuupäevavahemik", "day": "Päev", @@ -541,24 +729,34 @@ "delete": "Kustuta", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API vÃĩtme kustutada?", + "delete_dialog_alert": "Need Ãŧksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", + "delete_dialog_alert_local": "Need Ãŧksused kustutatakse jäädavalt sinu seadmest, aga jäävad Immich'i serverisse alles", + "delete_dialog_alert_local_non_backed_up": "MÃĩned Ãŧksustest ei ole Immich'isse varundatud ning kustutatakse su seadmest jäädavalt", + "delete_dialog_alert_remote": "Need Ãŧksused kustutatakse jäädavalt Immich'i serverist", + "delete_dialog_ok_force": "Kustuta sellegipoolest", + "delete_dialog_title": "Kustuta jäädavalt", "delete_duplicates_confirmation": "Kas oled kindel, et soovid need duplikaadid jäädavalt kustutada?", "delete_face": "Kustuta nägu", "delete_key": "Kustuta vÃĩti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", + "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", "delete_shared_link": "Kustuta jagatud link", + "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", "delete_tag_confirmation_prompt": "Kas oled kindel, et soovid sildi {tagName} kustutada?", "delete_user": "Kustuta kasutaja", "deleted_shared_link": "Jagatud link kustutatud", "deletes_missing_assets": "Kustutab Ãŧksused, mis on kettalt puudu", "description": "Kirjeldus", + "description_input_hint_text": "Lisa kirjeldus...", + "description_input_submit_error": "Viga kirjelduse muutmisel, rohkem infot leiad logist", "details": "Üksikasjad", "direction": "Suund", "disabled": "Välja lÃŧlitatud", "disallow_edits": "Keela muutmine", - "discord": "Discord", "discover": "Avasta", "dismiss_all_errors": "Peida kÃĩik veateated", "dismiss_error": "Peida veateade", @@ -570,12 +768,26 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_canceled": "Allalaadimine katkestatud", + "download_complete": "Allalaadimine lÃĩpetatud", + "download_enqueue": "Allalaadimine ootel", + "download_error": "Allalaadimise viga", + "download_failed": "Allalaadimine ebaÃĩnnestus", + "download_filename": "fail: {filename}", + "download_finished": "Allalaadimine lÃĩpetatud", "download_include_embedded_motion_videos": "Manustatud videod", "download_include_embedded_motion_videos_description": "Lisa liikuvatesse fotodesse manustatud videod eraldi failidena", + "download_notfound": "Allalaadimist ei leitud", + "download_paused": "Allalaadimine peatatud", "download_settings": "Allalaadimine", "download_settings_description": "Halda Ãŧksuste allalaadimise seadeid", + "download_started": "Allalaadimine alustatud", + "download_sucess": "Allalaadimine Ãĩnnestus", + "download_sucess_android": "Meediumid laaditi alla kataloogi DCIM/Immich", + "download_waiting_to_retry": "Uuesti proovimise ootel", "downloading": "Allalaadimine", "downloading_asset_filename": "Üksuse {filename} allalaadimine", + "downloading_media": "Meediumi allalaadimine", "drop_files_to_upload": "Failide Ãŧleslaadimiseks sikuta need ÃŧkskÃĩik kuhu", "duplicates": "Duplikaadid", "duplicates_description": "Lahenda iga grupp, valides duplikaadid, kui neid on", @@ -585,6 +797,8 @@ "edit_avatar": "Muuda avatari", "edit_date": "Muuda kuupäeva", "edit_date_and_time": "Muuda kuupäeva ja kellaaega", + "edit_description": "Muuda kirjeldust", + "edit_description_prompt": "Palun vali uus kirjeldus:", "edit_exclusion_pattern": "Muuda välistamismustrit", "edit_faces": "Muuda nägusid", "edit_import_path": "Muuda imporditeed", @@ -592,6 +806,7 @@ "edit_key": "Muuda vÃĩtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", "edit_tag": "Muuda silti", @@ -604,14 +819,23 @@ "editor_crop_tool_h2_aspect_ratios": "Kuvasuhted", "editor_crop_tool_h2_rotation": "PÃļÃļre", "email": "E-post", + "email_notifications": "E-posti teavitused", + "empty_folder": "See kaust on tÃŧhi", "empty_trash": "TÃŧhjenda prÃŧgikast", "empty_trash_confirmation": "Kas oled kindel, et soovid prÃŧgikasti tÃŧhjendada? See eemaldab kÃĩik seal olevad Ãŧksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi vÃĩtta!", "enable": "Luba", + "enable_biometric_auth_description": "Biomeetrilise autentimise lubamiseks sisesta oma PIN-kood", "enabled": "Lubatud", "end_date": "LÃĩppkuupäev", + "enqueued": "Järjekorras", + "enter_wifi_name": "Sisesta WiFi-vÃĩrgu nimi", + "enter_your_pin_code": "Sisesta oma PIN-kood", + "enter_your_pin_code_subtitle": "Sisesta oma PIN-kood, et lukustatud kaustale ligi pääseda", "error": "Viga", + "error_change_sort_album": "Albumi sorteerimisjärjestuse muutmine ebaÃĩnnestus", "error_delete_face": "Viga näo kustutamisel", "error_loading_image": "Viga pildi laadimisel", + "error_saving_image": "Viga: {error}", "error_title": "Viga - midagi läks valesti", "errors": { "cannot_navigate_next_asset": "Järgmise Ãŧksuse juurde liikumine ebaÃĩnnestus", @@ -641,10 +865,12 @@ "failed_to_keep_this_delete_others": "Selle Ãŧksuse säilitamine ja Ãŧlejäänute kustutamine ebaÃĩnnestus", "failed_to_load_asset": "Üksuse laadimine ebaÃĩnnestus", "failed_to_load_assets": "Üksuste laadimine ebaÃĩnnestus", + "failed_to_load_notifications": "Teavituste laadimine ebaÃĩnnestus", "failed_to_load_people": "Isikute laadimine ebaÃĩnnestus", "failed_to_remove_product_key": "TootevÃĩtme eemaldamine ebaÃĩnnestus", "failed_to_stack_assets": "Üksuste virnastamine ebaÃĩnnestus", "failed_to_unstack_assets": "Üksuste eraldamine ebaÃĩnnestus", + "failed_to_update_notification_status": "Teavituste seisundi uuendamine ebaÃĩnnestus", "import_path_already_exists": "See imporditee on juba olemas.", "incorrect_email_or_password": "Vale e-posti aadress vÃĩi parool", "paths_validation_failed": "{paths, plural, one {# tee} other {# teed}} ei valideerunud", @@ -662,6 +888,7 @@ "unable_to_archive_unarchive": "{archived, select, true {Arhiveerimine} other {Arhiivist taastamine}} ebaÃĩnnestus", "unable_to_change_album_user_role": "Kasutaja rolli albumis muutmine ebaÃĩnnestus", "unable_to_change_date": "Kuupäeva muutmine ebaÃĩnnestus", + "unable_to_change_description": "Kirjelduse muutmine ebaÃĩnnestus", "unable_to_change_favorite": "Üksuse lemmiku staatuse muutmine ebaÃĩnnestus", "unable_to_change_location": "Asukoha muutmine ebaÃĩnnestus", "unable_to_change_password": "Parooli muutmine ebaÃĩnnestus", @@ -699,6 +926,7 @@ "unable_to_log_out_all_devices": "KÃĩigist seadmetest väljalogimine ebaÃĩnnestus", "unable_to_log_out_device": "Seadmest väljalogimine ebaÃĩnnestus", "unable_to_login_with_oauth": "OAuth abil sisselogimine ebaÃĩnnestus", + "unable_to_move_to_locked_folder": "Lukustatud kausta liigutamine ebaÃĩnnestus", "unable_to_play_video": "Video esitamine ebaÃĩnnestus", "unable_to_reassign_assets_existing_person": "Üksuste {name, select, null {olemasoleva isikuga} other {isikuga {name}}} seostamine ebaÃĩnnestus", "unable_to_reassign_assets_new_person": "Üksuste uue isikuga seostamine ebaÃĩnnestus", @@ -712,6 +940,7 @@ "unable_to_remove_reaction": "Reaktsiooni eemaldamine ebaÃĩnnestus", "unable_to_repair_items": "Üksuste parandamine ebaÃĩnnestus", "unable_to_reset_password": "Parooli lähtestamine ebaÃĩnnestus", + "unable_to_reset_pin_code": "PIN-koodi lähtestamine ebaÃĩnnestus", "unable_to_resolve_duplicate": "Duplikaadi lahendamine ebaÃĩnnestus", "unable_to_restore_assets": "Üksuste taastamine ebaÃĩnnestus", "unable_to_restore_trash": "PrÃŧgikastist taastamine ebaÃĩnnestus", @@ -739,9 +968,21 @@ "unable_to_update_user": "Kasutaja muutmine ebaÃĩnnestus", "unable_to_upload_file": "Faili Ãŧleslaadimine ebaÃĩnnestus" }, - "exif": "Exif", + "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_details": "ÜKSIKASJAD", + "exif_bottom_sheet_location": "ASUKOHT", + "exif_bottom_sheet_people": "ISIKUD", + "exif_bottom_sheet_person_add_person": "Lisa nimi", + "exif_bottom_sheet_person_age": "Vanus {age}", + "exif_bottom_sheet_person_age_months": "Vanus {months} kuud", + "exif_bottom_sheet_person_age_year_months": "Vanus 1 aasta, {months} kuud", + "exif_bottom_sheet_person_age_years": "Vanus {years}", "exit_slideshow": "Sulge slaidiesitlus", "expand_all": "Näita kÃĩik", + "experimental_settings_new_asset_list_subtitle": "TÃļÃļs", + "experimental_settings_new_asset_list_title": "Luba eksperimentaalne fotoruudistik", + "experimental_settings_subtitle": "Kasuta omal vastutusel!", + "experimental_settings_title": "Eksperimentaalne", "expire_after": "Aegub", "expired": "Aegunud", "expires_date": "Aegub {date}", @@ -752,11 +993,17 @@ "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", + "external_network": "Väline vÃĩrk", + "external_network_sheet_info": "Kui seade ei ole eelistatud WiFi-vÃĩrgus, Ãŧhendub rakendus serveriga allolevatest URL-idest esimese kättesaadava kaudu, alustades Ãŧlevalt", "face_unassigned": "Seostamata", + "failed": "EbaÃĩnnestus", + "failed_to_authenticate": "Autentimine ebaÃĩnnestus", "failed_to_load_assets": "Üksuste laadimine ebaÃĩnnestus", + "failed_to_load_folder": "Kausta laadimine ebaÃĩnnestus", "favorite": "Lemmik", "favorite_or_unfavorite_photo": "Lisa foto lemmikutesse vÃĩi eemalda lemmikutest", "favorites": "Lemmikud", + "favorites_page_no_favorites": "Lemmikuid Ãŧksuseid ei leitud", "feature_photo_updated": "EsiletÃĩstetud foto muudetud", "features": "Funktsioonid", "features_setting_description": "Halda rakenduse funktsioone", @@ -765,24 +1012,37 @@ "filename": "Failinimi", "filetype": "FailitÃŧÃŧp", "filter_people": "Filtreeri isikuid", + "filter_places": "Filtreeri kohti", "find_them_fast": "Leia teda kiiresti nime järgi otsides", "fix_incorrect_match": "Paranda ebaÃĩige vaste", + "folder": "Kaust", + "folder_not_found": "Kausta ei leitud", "folders": "Kaustad", "folders_feature_description": "Kaustavaate abil failisÃŧsteemis olevate fotode ja videote sirvimine", "forward": "Edasi", "general": "Üldine", "get_help": "KÃŧsi abi", + "get_wifiname_error": "WiFi-vÃĩrgu nime ei Ãĩnnestunud lugeda. Veendu, et oled andnud vajalikud load ja oled WiFi-vÃĩrguga Ãŧhendatud", "getting_started": "Alustamine", "go_back": "Tagasi", "go_to_folder": "Mine kausta", "go_to_search": "Otsingusse", + "grant_permission": "Anna luba", "group_albums_by": "Grupeeri albumid...", "group_country": "Grupeeri riigi kaupa", "group_no": "Ära grupeeri", "group_owner": "Grupeeri omaniku kaupa", "group_places_by": "Grupeeri kohad...", "group_year": "Grupeeri aasta kaupa", + "haptic_feedback_switch": "Luba haptiline tagasiside", + "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "header_settings_add_header_tip": "Lisa päis", + "header_settings_field_validator_msg": "Väärtus ei saa olla tÃŧhi", + "header_settings_header_name_input": "Päise nimi", + "header_settings_header_value_input": "Päise väärtus", + "headers_settings_tile_subtitle": "Määra vaheserveri päised, mida rakendus peaks iga päringuga saatma", + "headers_settings_tile_title": "Kohandatud vaheserveri päised", "hi_user": "Tere {name} ({email})", "hide_all_people": "Peida kÃĩik isikud", "hide_gallery": "Peida galerii", @@ -790,8 +1050,25 @@ "hide_password": "Peida parool", "hide_person": "Peida isik", "hide_unnamed_people": "Peida nimetud isikud", - "host": "Host", + "home_page_add_to_album_conflicts": "{added} Ãŧksust lisati albumisse {album}. {failed} Ãŧksust oli juba albumis.", + "home_page_add_to_album_err_local": "Lokaalseid Ãŧksuseid ei saa veel albumisse lisada, jäetakse vahele", + "home_page_add_to_album_success": "{added} Ãŧksust lisati albumisse {album}.", + "home_page_album_err_partner": "Partneri Ãŧksuseid ei saa veel albumisse lisada, jäetakse vahele", + "home_page_archive_err_local": "Lokaalseid Ãŧksuseid ei saa veel arhiveerida, jäetakse vahele", + "home_page_archive_err_partner": "Partneri Ãŧksuseid ei saa arhiveerida, jäetakse vahele", + "home_page_building_timeline": "Ajajoone koostamine", + "home_page_delete_err_partner": "Partneri Ãŧksuseid ei saa kustutada, jäetakse vahele", + "home_page_delete_remote_err_local": "Kaugkustutamise valikus on lokaalsed Ãŧksused, jäetakse vahele", + "home_page_favorite_err_local": "Lokaalseid Ãŧksuseid ei saa lemmikuks märkida, jäetakse vahele", + "home_page_favorite_err_partner": "Partneri Ãŧksuseid ei saa lemmikuks märkida, jäetakse vahele", + "home_page_first_time_notice": "Kui see on su esimene kord rakendust kasutada, vali varunduse album, et ajajoon saaks sellest fotosid ja videosid kuvada", + "home_page_locked_error_local": "Lokaalseid Ãŧksuseid ei saa lukustatud kausta liigutada, jäetakse vahele", + "home_page_locked_error_partner": "Partneri Ãŧksuseid ei saa lukustatud kausta lisada, jäetakse vahele", + "home_page_share_err_local": "Lokaalseid Ãŧksuseid ei saa lingiga jagada, jäetakse vahele", + "home_page_upload_err_limit": "Korraga saab Ãŧles laadida ainult 30 Ãŧksust, jäetakse vahele", "hour": "Tund", + "ignore_icloud_photos": "Ignoreeri iCloud fotosid", + "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita Ãŧles Immich'i serverisse", "image": "Pilt", "image_alt_text_date": "{isVideo, select, true {Video} other {Pilt}} tehtud {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} koos isikuga {person1}", @@ -803,6 +1080,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1} ja {person2}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos isikutega {person1}, {person2} ja {person3}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Pilt}} tehtud {date} kohas {city}, {country} koos {person1}, {person2} ja veel {additionalCount, number} isikuga", + "image_saved_successfully": "Pilt salvestatud", + "image_viewer_page_state_provider_download_started": "Allalaadimine alustatud", + "image_viewer_page_state_provider_download_success": "Allalaadimine Ãĩnnestus", + "image_viewer_page_state_provider_share_error": "Jagamise viga", "immich_logo": "Immich'i logo", "immich_web_interface": "Immich'i veebiliides", "import_from_json": "Impordi JSON-formaadist", @@ -814,13 +1095,14 @@ "include_shared_partner_assets": "Kaasa partneri jagatud Ãŧksused", "individual_share": "Jagatud Ãŧksus", "individual_shares": "Jagatud Ãŧksused", - "info": "Info", "interval": { "day_at_onepm": "Iga päev kell 13", "hours": "Iga {hours, plural, one {tunni} other {{hours, number} tunni}} tagant", "night_at_midnight": "Iga päev keskÃļÃļl", "night_at_twoam": "Iga ÃļÃļ kell 2" }, + "invalid_date": "Vigane kuupäev", + "invalid_date_format": "Vigane kuupäevaformaat", "invite_people": "Kutsu inimesi", "invite_to_album": "Kutsu albumisse", "items_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", @@ -841,6 +1123,12 @@ "level": "Tase", "library": "Kogu", "library_options": "Kogu seaded", + "library_page_device_albums": "Albumid seadmes", + "library_page_new_album": "Uus album", + "library_page_sort_asset_count": "Üksuste arv", + "library_page_sort_created": "Loomise aeg", + "library_page_sort_last_modified": "Viimase muutmise aeg", + "library_page_sort_title": "Albumi pealkiri", "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", @@ -850,12 +1138,44 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaÃĩnnestus", + "local_network": "Kohalik vÃĩrk", + "local_network_sheet_info": "Rakendus Ãŧhendub valitud Wi-Fi vÃĩrgus olles serveriga selle URL-i kaudu", + "location_permission": "Asukoha luba", + "location_permission_content": "Automaatseks ÃŧmberlÃŧlitumiseks vajab Immich täpse asukoha luba, et saaks lugeda aktiivse WiFi-vÃĩrgu nime", + "location_picker_choose_on_map": "Vali kaardil", + "location_picker_latitude_error": "Sisesta korrektne laiuskraad", + "location_picker_latitude_hint": "Sisesta laiuskraad siia", + "location_picker_longitude_error": "Sisesta korrektne pikkuskraad", + "location_picker_longitude_hint": "Sisesta pikkuskraad siia", + "lock": "Lukusta", + "locked_folder": "Lukustatud kaust", "log_out": "Logi välja", "log_out_all_devices": "Logi kÃĩigist seadmetest välja", "logged_out_all_devices": "KÃĩigist seadmetest välja logitud", "logged_out_device": "Seadmest välja logitud", "login": "Logi sisse", + "login_disabled": "Sisselogimine on keelatud", + "login_form_api_exception": "API viga. Kontrolli serveri URL-i ja proovi uuesti.", + "login_form_back_button_text": "Tagasi", + "login_form_email_hint": "sinunimi@email.com", + "login_form_endpoint_hint": "http://serveri-ip:port", + "login_form_endpoint_url": "Serveri lÃĩpp-punkti URL", + "login_form_err_http": "Palun täpsusta http:// vÃĩi https://", + "login_form_err_invalid_email": "Vigane e-posti aadress", + "login_form_err_invalid_url": "Vigane URL", + "login_form_err_leading_whitespace": "Eelnevad tÃŧhikud", + "login_form_err_trailing_whitespace": "Järgnevad tÃŧhikud", + "login_form_failed_get_oauth_server_config": "Viga OAuth abil sisenemisel, kontrolli serveri URL-i", + "login_form_failed_get_oauth_server_disable": "OAuth funktsionaalsus ei ole selles serveris saadaval", + "login_form_failed_login": "Viga sisselogimisel, kontrolli serveri URL-i, e-posti aadressi ja parooli", + "login_form_handshake_exception": "Serveriga suhtlemisel tekkis kätlemise viga. Kui kasutad endasigneeritud sertifikaati, luba seadetes endasigneeritud sertifikaatide tugi.", + "login_form_password_hint": "parool", + "login_form_save_login": "Jää sisselogituks", + "login_form_server_empty": "Sisesta serveri URL.", + "login_form_server_error": "Serveriga Ãŧhendumine ebaÃĩnnestus.", "login_has_been_disabled": "Sisselogimine on keelatud.", + "login_password_changed_error": "Parooli muutmisel tekkis viga", + "login_password_changed_success": "Parool edukalt uuendatud", "logout_all_device_confirmation": "Kas oled kindel, et soovid kÃĩigist seadmetest välja logida?", "logout_this_device_confirmation": "Kas oled kindel, et soovid sellest seadmest välja logida?", "longitude": "Pikkuskraad", @@ -873,13 +1193,41 @@ "manage_your_devices": "Halda oma autenditud seadmeid", "manage_your_oauth_connection": "Halda oma OAuth Ãŧhendust", "map": "Kaart", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotot", + "map_cannot_get_user_location": "Ei saa kasutaja asukohta tuvastada", + "map_location_dialog_yes": "Jah", + "map_location_picker_page_use_location": "Kasuta seda asukohta", + "map_location_service_disabled_content": "Praeguse asukoha Ãŧksuste kuvamiseks tuleb lubada asukoha teenus. Kas soovid seda praegu lubada?", + "map_location_service_disabled_title": "Asukoha teenus keelatud", "map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks", "map_marker_with_image": "Kaardimarker pildiga", + "map_no_assets_in_bounds": "Selles piirkonnas ei ole fotosid", + "map_no_location_permission_content": "Praeguse asukoha Ãŧksuste kuvamiseks on vaja asukoha luba. Kas soovid seda praegu lubada?", + "map_no_location_permission_title": "Asukoha luba keelatud", "map_settings": "Kaardi seaded", + "map_settings_dark_mode": "Tume reÅžiim", + "map_settings_date_range_option_day": "Viimased 24 tundi", + "map_settings_date_range_option_days": "Viimased {days} päeva", + "map_settings_date_range_option_year": "Viimane aasta", + "map_settings_date_range_option_years": "Viimased {years} aastat", + "map_settings_dialog_title": "Kaardi seaded", + "map_settings_include_show_archived": "Kaasa arhiveeritud", + "map_settings_include_show_partners": "Kaasa partnerid", + "map_settings_only_show_favorites": "Kuva ainult lemmikud", + "map_settings_theme_settings": "Kaardi teema", + "map_zoom_to_see_photos": "Fotode nägemiseks suumi välja", + "mark_all_as_read": "Märgi kÃĩik loetuks", + "mark_as_read": "Märgi loetuks", + "marked_all_as_read": "KÃĩik märgiti loetuks", "matches": "Ühtivad failid", - "media_type": "Meedia tÃŧÃŧp", + "media_type": "Meediumi tÃŧÃŧp", "memories": "Mälestused", + "memories_all_caught_up": "Ongi kÃĩik", + "memories_check_back_tomorrow": "Vaata homme juba uusi mälestusi", "memories_setting_description": "Halda, mida sa oma mälestustes näed", + "memories_start_over": "Alusta uuesti", + "memories_swipe_to_close": "Sulgemiseks pÃŧhi Ãŧles", "memory": "Mälestus", "memory_lane_title": "Mälestus {title}", "menu": "MenÃŧÃŧ", @@ -895,16 +1243,28 @@ "model": "Mudel", "month": "Kuu", "more": "Rohkem", + "move": "Liiguta", + "move_off_locked_folder": "Liiguta lukustatud kaustast välja", + "move_to_locked_folder": "Liiguta lukustatud kausta", + "move_to_locked_folder_confirmation": "Need fotod ja videod eemaldatakse kÃĩigist albumitest ning nad on nähtavad ainult lukustatud kaustas", + "moved_to_archive": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud arhiivi", + "moved_to_library": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud kogusse", "moved_to_trash": "Liigutatud prÃŧgikasti", + "multiselect_grid_edit_date_time_err_read_only": "Kirjutuskaitsega Ãŧksus(t)e kuupäeva ei saa muuta, jäetakse vahele", + "multiselect_grid_edit_gps_err_read_only": "Kirjutuskaitsega Ãŧksus(t)e asukohta ei saa muuta, jäetakse vahele", "mute_memories": "Vaigista mälestused", "my_albums": "Minu albumid", "name": "Nimi", "name_or_nickname": "Nimi vÃĩi hÃŧÃŧdnimi", + "networking_settings": "VÃĩrguÃŧhendus", + "networking_subtitle": "Halda serveri lÃĩpp-punkti seadeid", "never": "Mitte kunagi", "new_album": "Uus album", "new_api_key": "Uus API vÃĩti", "new_password": "Uus parool", "new_person": "Uus isik", + "new_pin_code": "Uus PIN-kood", + "new_pin_code_subtitle": "See on sul esimene kord lukustatud kausta kasutada. Turvaliseks ligipääsuks loo PIN-kood", "new_user_created": "Uus kasutaja lisatud", "new_version_available": "UUS VERSIOON SAADAVAL", "newest_first": "Uuemad eespool", @@ -916,29 +1276,38 @@ "no_albums_yet": "Paistab, et sul pole veel Ãŧhtegi albumit.", "no_archived_assets_message": "Arhiveeri fotod ja videod, et neid Fotod vaatest peita", "no_assets_message": "KLIKI ESIMESE FOTO ÜLESLAADIMISEKS", + "no_assets_to_show": "Pole Ãŧksuseid, mida kuvada", "no_duplicates_found": "Ühtegi duplikaati ei leitud.", "no_exif_info_available": "Exif info pole saadaval", "no_explore_results_message": "Oma kogu avastamiseks laadi Ãŧles rohkem fotosid.", "no_favorites_message": "Lisa lemmikud, et oma parimaid fotosid ja videosid kiiresti leida", "no_libraries_message": "Lisa väline kogu oma fotode ja videote vaatamiseks", + "no_locked_photos_message": "Lukustatud kaustas olevad fotod ja videod on peidetud ning need pole kogu sirvimisel ja otsimisel nähtavad.", "no_name": "Nimetu", + "no_notifications": "Teavitusi pole", + "no_people_found": "Kattuvaid isikuid ei leitud", "no_places": "Kohti ei ole", "no_results": "Vasteid pole", "no_results_description": "Proovi sÃŧnonÃŧÃŧmi vÃĩi Ãŧldisemat märksÃĩna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", "not_in_any_album": "Pole Ãŧheski albumis", + "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem Ãŧleslaaditud Ãŧksustele, käivita", "notes": "Märkused", + "nothing_here_yet": "Siin pole veel midagi", + "notification_permission_dialog_content": "Teavituste lubamiseks mine Seadetesse ja vali lubamine.", + "notification_permission_list_tile_content": "Anna luba teavituste saatmiseks.", + "notification_permission_list_tile_enable_button": "Luba teavitused", + "notification_permission_list_tile_title": "Teavituste luba", "notification_toggle_setting_description": "Luba e-posti teel teavitused", "notifications": "Teavitused", "notifications_setting_description": "Halda teavitusi", - "oauth": "OAuth", "official_immich_resources": "Ametlikud Immich'i ressursid", "offline": "Ühendus puudub", "offline_paths": "Ühenduseta failiteed", "offline_paths_description": "Need tulemused vÃĩivad olla pÃĩhjustatud manuaalselt kustutatud failidest, mis ei ole osa välisest kogust.", - "ok": "Ok", "oldest_first": "Vanemad eespool", + "on_this_device": "Sellel seadmel", "onboarding": "KasutuselevÃĩtt", "onboarding_privacy_description": "Järgnevad (valikulised) funktsioonid sÃĩltuvad välistest teenustest ning neid saab igal ajal administraatori seadetes välja lÃŧlitada.", "onboarding_theme_description": "Vali oma serverile värviteema. Saad seda hiljem seadetes muuta.", @@ -946,6 +1315,7 @@ "onboarding_welcome_user": "Tere tulemast, {user}", "online": "Ühendatud", "only_favorites": "Ainult lemmikud", + "open": "Ava", "open_in_map_view": "Ava kaardi vaates", "open_in_openstreetmap": "Ava OpenStreetMap", "open_the_search_filters": "Ava otsingufiltrid", @@ -958,10 +1328,17 @@ "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", - "partner": "Partner", "partner_can_access": "{partner} pääseb ligi", "partner_can_access_assets": "KÃĩik su fotod ja videod, välja arvatud arhiveeritud ja kustutatud", "partner_can_access_location": "Asukohad, kus su fotod tehti", + "partner_list_user_photos": "Kasutaja {user} fotod", + "partner_list_view_all": "Vaata kÃĩiki", + "partner_page_empty_message": "Su fotod pole veel Ãŧhegi partneriga jagatud.", + "partner_page_no_more_users": "Pole rohkem kasutajaid, keda lisada", + "partner_page_partner_add_failed": "Partneri lisamine ebaÃĩnnestus", + "partner_page_select_partner": "Vali partner", + "partner_page_shared_to_title": "Jagatud", + "partner_page_stop_sharing_content": "{partner} ei pääse rohkem su fotodele ligi.", "partner_sharing": "Partneriga jagamine", "partners": "Partnerid", "password": "Parool", @@ -990,6 +1367,14 @@ "permanently_delete_assets_prompt": "Kas oled kindel, et soovid {count, plural, one {selle Ãŧksuse} other {need # Ãŧksust}} jäädavalt kustutada? Sellega eemaldatakse {count, plural, one {see} other {need}} ka oma albumi(te)st.", "permanently_deleted_asset": "Üksus jäädavalt kustutatud", "permanently_deleted_assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} jäädavalt kustutatud", + "permission_onboarding_back": "Tagasi", + "permission_onboarding_continue_anyway": "Jätka sellegipoolest", + "permission_onboarding_get_started": "Alusta", + "permission_onboarding_go_to_settings": "Mine seadetesse", + "permission_onboarding_permission_denied": "Luba keelatud. Immich'i kasutamiseks anna Seadetes fotode ja videote load.", + "permission_onboarding_permission_granted": "Luba antud! Oled valmis.", + "permission_onboarding_permission_limited": "Piiratud luba. Et Immich saaks tervet su galeriid varundada ja hallata, anna Seadetes luba fotodele ja videotele.", + "permission_onboarding_request": "Immich'il on vaja luba su fotode ja videote vaatamiseks.", "person": "Isik", "person_birthdate": "SÃŧndinud {date}", "person_hidden": "{name}{hidden, select, true { (peidetud)} other {}}", @@ -999,6 +1384,10 @@ "photos_count": "{count, plural, one {{count, number} foto} other {{count, number} fotot}}", "photos_from_previous_years": "Fotod varasematest aastatest", "pick_a_location": "Vali asukoht", + "pin_code_changed_successfully": "PIN-kood edukalt muudetud", + "pin_code_reset_successfully": "PIN-kood edukalt lähtestatud", + "pin_code_setup_successfully": "PIN-kood edukalt seadistatud", + "pin_verification": "PIN-koodi kinnitus", "place": "Asukoht", "places": "Kohad", "places_count": "{count, plural, one {{count, number} koht} other {{count, number} kohta}}", @@ -1006,7 +1395,9 @@ "play_memories": "Esita mälestused", "play_motion_photo": "Esita liikuv foto", "play_or_pause_video": "Esita vÃĩi peata video", - "port": "Port", + "please_auth_to_access": "Ligipääsemiseks palun autendi", + "preferences_settings_subtitle": "Halda rakenduse eelistusi", + "preferences_settings_title": "Eelistused", "preset": "Eelseadistus", "preview": "Eelvaade", "previous": "Eelmine", @@ -1014,13 +1405,20 @@ "previous_or_next_photo": "Eelmine vÃĩi järgmine foto", "primary": "Peamine", "privacy": "Privaatsus", + "profile": "Profiil", + "profile_drawer_app_logs": "Logid", + "profile_drawer_client_out_of_date_major": "Mobiilirakendus on aegunud. Palun uuenda uusimale suurele versioonile.", + "profile_drawer_client_out_of_date_minor": "Mobiilirakendus on aegunud. Palun uuenda uusimale väikesele versioonile.", + "profile_drawer_client_server_up_to_date": "Klient ja server on uuendatud", + "profile_drawer_server_out_of_date_major": "Server on aegunud. Palun uuenda uusimale suurele versioonile.", + "profile_drawer_server_out_of_date_minor": "Server on aegunud. Palun uuenda uusimale väikesele versioonile.", "profile_image_of_user": "Kasutaja {user} profiilipilt", "profile_picture_set": "Profiilipilt määratud.", "public_album": "Avalik album", "public_share": "Avalik jagamine", "purchase_account_info": "Toetaja", "purchase_activated_subtitle": "Aitäh, et toetad Immich'it ja avatud lähtekoodiga tarkvara", - "purchase_activated_time": "Aktiveeritud {date, date}", + "purchase_activated_time": "Aktiveeritud {date}", "purchase_activated_title": "Sinu vÃĩtme aktiveerimine Ãĩnnestus", "purchase_button_activate": "Aktiveeri", "purchase_button_buy": "Osta", @@ -1048,7 +1446,6 @@ "purchase_remove_server_product_key_prompt": "Kas oled kindel, et soovid serveri tootevÃĩtme eemaldada?", "purchase_server_description_1": "Kogu serveri jaoks", "purchase_server_description_2": "Toetaja staatus", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevÃĩtit haldab administraator", "rating": "Hinnang", "rating_clear": "TÃŧhjenda hinnang", @@ -1063,6 +1460,10 @@ "recent": "Hiljutine", "recent-albums": "Hiljutised albumid", "recent_searches": "Hiljutised otsingud", + "recently_added": "Hiljuti lisatud", + "recently_added_page_title": "Hiljuti lisatud", + "recently_taken": "Hiljuti tehtud", + "recently_taken_page_title": "Hiljuti tehtud", "refresh": "Värskenda", "refresh_encoded_videos": "Värskenda kodeeritud videod", "refresh_faces": "Värskenda näod", @@ -1082,6 +1483,8 @@ "remove_deleted_assets": "Eemalda kustutatud Ãŧksused", "remove_from_album": "Eemalda albumist", "remove_from_favorites": "Eemalda lemmikutest", + "remove_from_locked_folder": "Eemalda lukustatud kaustast", + "remove_from_locked_folder_confirmation": "Kas oled kindel, et soovid need fotod ja videod lukustatud kaustast välja liigutada? Need muutuvad su kogus nähtavaks.", "remove_from_shared_link": "Eemalda jagatud lingist", "remove_memory": "Eemalda mälestus", "remove_photo_from_memory": "Eemalda foto sellest mälestusest", @@ -1105,6 +1508,7 @@ "reset": "Lähtesta", "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", + "reset_pin_code": "Lähtesta PIN-kood", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "KÃĩik duplikaadid lahendatud", @@ -1119,10 +1523,12 @@ "role_editor": "Muutja", "role_viewer": "Vaataja", "save": "Salvesta", + "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API vÃĩti salvestatud", "saved_profile": "Profiil salvestatud", "saved_settings": "Seaded salvestatud", "say_something": "Ütle midagi", + "scaffold_body_error_occurred": "Tekkis viga", "scan_all_libraries": "Skaneeri kÃĩik kogud", "scan_library": "Skaneeri", "scan_settings": "Skaneerimise seaded", @@ -1138,16 +1544,45 @@ "search_camera_model": "Otsi kaamera mudelit...", "search_city": "Otsi linna...", "search_country": "Otsi riiki...", + "search_filter_apply": "Rakenda filter", + "search_filter_camera_title": "Vali kaamera tÃŧÃŧp", + "search_filter_date": "Kuupäev", + "search_filter_date_interval": "{start} kuni {end}", + "search_filter_date_title": "Vali kuupäevavahemik", + "search_filter_display_option_not_in_album": "Pole albumis", + "search_filter_display_options": "Kuva valikud", + "search_filter_filename": "Otsi failinime alusel", + "search_filter_location": "Asukoht", + "search_filter_location_title": "Vali asukoht", + "search_filter_media_type": "Meediumi tÃŧÃŧp", + "search_filter_media_type_title": "Vali meediumi tÃŧÃŧp", + "search_filter_people_title": "Vali isikud", "search_for": "Otsi", "search_for_existing_person": "Otsi olemasolevat isikut", + "search_no_more_result": "Rohkem vasteid pole", "search_no_people": "Isikuid ei ole", "search_no_people_named": "Ei ole isikuid nimega \"{name}\"", + "search_no_result": "Vasteid ei leitud, proovi muud otsinguterminit vÃĩi kombinatsiooni", "search_options": "Otsingu valikud", + "search_page_categories": "Kategooriad", + "search_page_motion_photos": "Liikuvad fotod", + "search_page_no_objects": "Objektide info pole saadaval", + "search_page_no_places": "Kohtade info pole saadaval", + "search_page_screenshots": "Ekraanipildid", + "search_page_search_photos_videos": "Otsi oma fotosid ja videosid", + "search_page_selfies": "Selfid", + "search_page_things": "Asjad", + "search_page_view_all_button": "Vaata kÃĩiki", + "search_page_your_activity": "Sinu aktiivsus", + "search_page_your_map": "Sinu kaart", "search_people": "Otsi inimesi", "search_places": "Otsi kohti", "search_rating": "Otsi hinnangu järgi...", + "search_result_page_new_search_hint": "Uus otsing", "search_settings": "Otsi seadeid", "search_state": "Otsi osariiki...", + "search_suggestion_list_smart_search_hint_1": "Nutiotsing on vaikimisi lubatud, metaandmete otsimiseks kasuta sÃŧntaksit ", + "search_suggestion_list_smart_search_hint_2": "m:sinu-otsingu-termin", "search_tags": "Otsi silte...", "search_timezone": "Otsi ajavÃļÃļndit...", "search_type": "Otsingu tÃŧÃŧp", @@ -1166,12 +1601,17 @@ "select_keep_all": "Vali jäta kÃĩik alles", "select_library_owner": "Vali kogu omanik", "select_new_face": "Vali uus nägu", + "select_person_to_tag": "Vali sildistamiseks isik", "select_photos": "Vali fotod", "select_trash_all": "Vali kÃĩik prÃŧgikasti", + "select_user_for_sharing_page_err_album": "Albumi lisamine ebaÃĩnnestus", "selected": "Valitud", "selected_count": "{count, plural, other {# valitud}}", "send_message": "Saada sÃĩnum", "send_welcome_email": "Saada tervituskiri", + "server_endpoint": "Serveri lÃĩpp-punkt", + "server_info_box_app_version": "Rakenduse versioon", + "server_info_box_server_url": "Serveri URL", "server_offline": "Serveriga Ãŧhendus puudub", "server_online": "Server Ãŧhendatud", "server_stats": "Serveri statistika", @@ -1183,22 +1623,92 @@ "set_date_of_birth": "Määra sÃŧnnikuupäev", "set_profile_picture": "Sea profiilipilt", "set_slideshow_to_fullscreen": "Kuva slaidiesitlus täisekraanil", + "setting_image_viewer_help": "Detailivaatur laadib kÃĩigepealt väikese pisipildi, seejärel keskmises mÃĩÃĩdus eelvaate (kui lubatud) ja lÃĩpuks originaalpildi (kui lubatud).", + "setting_image_viewer_original_subtitle": "LÃŧlita sisse, et laadida algne täisresolutsiooniga pilt (suur!). LÃŧlita välja, et vähendada andmekasutust (nii vÃĩrgu kui seadme puhvri).", + "setting_image_viewer_original_title": "Laadi algne pilt", + "setting_image_viewer_preview_subtitle": "Luba keskmise resolutsiooniga pildi laadimine. Keela, et laadida kohe originaalpilt vÃĩi kasutada ainult pisipilti.", + "setting_image_viewer_preview_title": "Laadi pildi eelvaade", + "setting_image_viewer_title": "Pildid", + "setting_languages_apply": "Rakenda", + "setting_languages_subtitle": "Muuda rakenduse keelt", + "setting_languages_title": "Keeled", + "setting_notifications_notify_failures_grace_period": "Teavita taustal varundamise vigadest: {duration}", + "setting_notifications_notify_hours": "{count} tundi", + "setting_notifications_notify_immediately": "kohe", + "setting_notifications_notify_minutes": "{count} minutit", + "setting_notifications_notify_never": "mitte kunagi", + "setting_notifications_notify_seconds": "{count} sekundit", + "setting_notifications_single_progress_subtitle": "Detailne Ãŧleslaadimise edenemise info Ãŧksuse kohta", + "setting_notifications_single_progress_title": "Kuva taustal varundamise detailset edenemist", + "setting_notifications_subtitle": "Halda oma teavituste eelistusi", + "setting_notifications_total_progress_subtitle": "Üldine Ãŧleslaadimise edenemine (Ãŧksuseid tehtud/kokku)", + "setting_notifications_total_progress_title": "Kuva taustal varundamise Ãŧldist edenemist", + "setting_video_viewer_looping_title": "Taasesitus", + "setting_video_viewer_original_video_subtitle": "Esita serverist video voogedastamisel originaal, isegi kui transkodeeritud video on saadaval. VÃĩib pÃĩhjustada puhverdamist. Lokaalselt saadaolevad videod mängitakse originaalkvaliteediga sÃĩltumata sellest seadest.", + "setting_video_viewer_original_video_title": "Sunni originaalvideo", "settings": "Seaded", + "settings_require_restart": "Selle seade rakendamiseks palun taaskäivita Immich", "settings_saved": "Seaded salvestatud", + "setup_pin_code": "Seadista PIN-kood", "share": "Jaga", + "share_add_photos": "Lisa fotosid", + "share_assets_selected": "{count} valitud", + "share_dialog_preparing": "Ettevalmistamine...", + "share_link": "Jaga linki", "shared": "Jagatud", + "shared_album_activities_input_disable": "Kommentaarid on keelatud", + "shared_album_activity_remove_content": "Kas soovid selle tegevuse kustutada?", + "shared_album_activity_remove_title": "Kustuta tegevus", + "shared_album_section_people_action_error": "Viga albumist eemaldamisel/lahkumisel", + "shared_album_section_people_action_leave": "Eemalda kasutaja albumist", + "shared_album_section_people_action_remove_user": "Eemalda kasutaja albumist", + "shared_album_section_people_title": "ISIKUD", "shared_by": "Jagas", "shared_by_user": "Jagas {user}", "shared_by_you": "Jagasid sina", "shared_from_partner": "Fotod partnerilt {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} Ãŧles laaditud", + "shared_link_app_bar_title": "Jagatud lingid", + "shared_link_clipboard_copied_massage": "Kopeeritud lÃĩikelauale", + "shared_link_clipboard_text": "Link: {link}\nParool: {password}", + "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_edit_description_hint": "Sisesta jagatud lingi kirjeldus", + "shared_link_edit_expire_after_option_day": "1 päev", + "shared_link_edit_expire_after_option_days": "{count} päeva", + "shared_link_edit_expire_after_option_hour": "1 tund", + "shared_link_edit_expire_after_option_hours": "{count} tundi", + "shared_link_edit_expire_after_option_minute": "1 minut", + "shared_link_edit_expire_after_option_minutes": "{count} minutit", + "shared_link_edit_expire_after_option_months": "{count} kuud", + "shared_link_edit_expire_after_option_year": "{count} aasta", + "shared_link_edit_password_hint": "Sisesta jagatud lingi parool", + "shared_link_edit_submit_button": "Muuda link", + "shared_link_error_server_url_fetch": "Serveri URL-i ei leitud", + "shared_link_expires_day": "Aegub {count} päeva pärast", + "shared_link_expires_days": "Aegub {count} päeva pärast", + "shared_link_expires_hour": "Aegub {count} tunni pärast", + "shared_link_expires_hours": "Aegub {count} tunni pärast", + "shared_link_expires_minute": "Aegub {count} minuti pärast", + "shared_link_expires_minutes": "Aegub {count} minuti pärast", + "shared_link_expires_never": "Ei aegu", + "shared_link_expires_second": "Aegub {count} sekundi pärast", + "shared_link_expires_seconds": "Aegub {count} sekundi pärast", + "shared_link_individual_shared": "Individuaalselt jagatud", + "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", + "shared_with_me": "Minuga jagatud", "shared_with_partner": "Jagatud partneriga {partner}", "sharing": "Jagamine", "sharing_enter_password": "Palun sisesta selle lehe vaatamiseks salasÃĩna.", + "sharing_page_album": "Jagatud albumid", + "sharing_page_description": "Loo jagatud albumeid, et jagada fotosid ja videosid inimestega oma vÃĩrgustikus.", + "sharing_page_empty_list": "TÜHI LOEND", "sharing_sidebar_description": "Kuva kÃŧlgmenÃŧÃŧs Jagamise linki", + "sharing_silver_appbar_create_shared_album": "Uus jagatud album", + "sharing_silver_appbar_share_partner": "Jaga partneriga", "shift_to_permanent_delete": "vajuta ⇧, et Ãŧksus jäädavalt kustutada", "show_album_options": "Näita albumi valikuid", "show_albums": "Näita albumeid", @@ -1256,6 +1766,7 @@ "stop_sharing_photos_with_user": "LÃĩpeta oma fotode selle kasutajaga jagamine", "storage": "Talletusruum", "storage_label": "Talletussilt", + "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", "suggestions": "Soovitused", @@ -1265,6 +1776,9 @@ "support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, vÃĩivad olla pÃĩhjustatud selle pakendamise poolt, seega vÃĩta esmajärjekorras nendega Ãŧhendust, kasutades allolevaid linke.", "swap_merge_direction": "Muuda Ãŧhendamise suunda", "sync": "SÃŧnkrooni", + "sync_albums": "SÃŧnkrooni albumid", + "sync_albums_manual_subtitle": "SÃŧnkrooni kÃĩik Ãŧleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod Ãŧles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista Ãŧksuseid", "tag_created": "Lisatud silt: {tag}", @@ -1278,6 +1792,19 @@ "theme": "Teema", "theme_selection": "Teema valik", "theme_selection_description": "Sea automaatselt hele vÃĩi tume teema vastavalt veebilehitseja eelistustele", + "theme_setting_asset_list_storage_indicator_title": "Kuva Ãŧksuste ruutudel talletusindikaatorit", + "theme_setting_asset_list_tiles_per_row_title": "Üksuste arv reas ({count})", + "theme_setting_colorful_interface_subtitle": "Rakenda taustapindadele pÃĩhivärv.", + "theme_setting_colorful_interface_title": "Värviline kasutajaliides", + "theme_setting_image_viewer_quality_subtitle": "Kohanda detailvaaturi kvaliteeti", + "theme_setting_image_viewer_quality_title": "Pildivaaturi kvaliteet", + "theme_setting_primary_color_subtitle": "Vali värv pÃĩhitegevuste ja aktsentide jaoks.", + "theme_setting_primary_color_title": "PÃĩhivärv", + "theme_setting_system_primary_color_title": "Kasuta sÃŧsteemset värvi", + "theme_setting_system_theme_switch": "Automaatne (järgi sÃŧsteemi seadet)", + "theme_setting_theme_subtitle": "Vali rakenduse teema seade", + "theme_setting_three_stage_loading_subtitle": "Kolmeastmeline laadimine vÃĩib parandada laadimise jÃĩudlust, aga pÃĩhjustab oluliselt suuremat vÃĩrgukoormust", + "theme_setting_three_stage_loading_title": "Luba kolmeastmeline laadimine", "they_will_be_merged_together": "Nad Ãŧhendatakse kokku", "third_party_resources": "Kolmanda osapoole ressursid", "time_based_memories": "AjapÃĩhised mälestused", @@ -1297,9 +1824,19 @@ "trash_all": "KÃĩik prÃŧgikasti", "trash_count": "Liiguta {count, number} prÃŧgikasti", "trash_delete_asset": "Kustuta Ãŧksus", + "trash_emptied": "PrÃŧgikast tÃŧhjendatud", "trash_no_results_message": "Siia ilmuvad prÃŧgikasti liigutatud fotod ja videod.", + "trash_page_delete_all": "Kustuta kÃĩik", + "trash_page_empty_trash_dialog_content": "Kas soovid prÃŧgikasti liigutatud Ãŧksused kustutada? Need eemaldatakse Immich'ist jäädavalt", + "trash_page_info": "PrÃŧgikasti liigutatud Ãŧksused kustutatakse jäädavalt {days} päeva pärast", + "trash_page_no_assets": "PrÃŧgikastis Ãŧksuseid pole", + "trash_page_restore_all": "Taasta kÃĩik", + "trash_page_select_assets_btn": "Vali Ãŧksused", + "trash_page_title": "PrÃŧgikast ({count})", "trashed_items_will_be_permanently_deleted_after": "PrÃŧgikasti tÃĩstetud Ãŧksused kustutatakse jäädavalt {days, plural, one {# päeva} other {# päeva}} pärast.", "type": "TÃŧÃŧp", + "unable_to_change_pin_code": "PIN-koodi muutmine ebaÃĩnnestus", + "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaÃĩnnestus", "unarchive": "Taasta arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", "unfavorite": "Eemalda lemmikutest", @@ -1323,9 +1860,12 @@ "untracked_files": "Mittejälgitavad failid", "untracked_files_decription": "Rakendus ei jälgi neid faile. Need vÃĩivad olla pÃĩhjustatud ebaÃĩnnestunud liigutamisest, katkestatud Ãŧleslaadimisest vÃĩi rakenduse veast", "up_next": "Järgmine", + "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi Ãŧles", "upload_concurrency": "Üleslaadimise samaaegsus", + "upload_dialog_info": "Kas soovid valitud Ãŧksuse(d) serverisse varundada?", + "upload_dialog_title": "Üksuse Ãŧleslaadimine", "upload_errors": "Üleslaadimine lÃĩpetatud {count, plural, one {# veaga} other {# veaga}}, uute Ãŧksuste nägemiseks värskenda lehte.", "upload_progress": "Ootel {remaining, number} - TÃļÃļdeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud Ãŧksus} other {# dubleeritud Ãŧksust}} vahele jäetud", @@ -1333,12 +1873,18 @@ "upload_status_errors": "Vead", "upload_status_uploaded": "Üleslaaditud", "upload_success": "Üleslaadimine Ãĩnnestus, uute Ãŧksuste nägemiseks värskenda lehte.", - "url": "URL", + "upload_to_immich": "Laadi Immich'isse ({count})", + "uploading": "Üleslaadimine", "usage": "Kasutus", + "use_biometric": "Kasuta biomeetriat", + "use_current_connection": "kasuta praegust Ãŧhendust", "use_custom_date_range": "Kasuta kohandatud kuupäevavahemikku", "user": "Kasutaja", + "user_has_been_deleted": "See kasutaja on kustutatud.", "user_id": "Kasutaja ID", "user_liked": "Kasutajale {user} meeldis {type, select, photo {see foto} video {see video} asset {see Ãŧksus} other {see}}", + "user_pin_code_settings": "PIN-kood", + "user_pin_code_settings_description": "Halda oma PIN-koodi", "user_purchase_settings": "Ost", "user_purchase_settings_description": "Halda oma ostu", "user_role_set": "Määra kasutajale {user} roll {role}", @@ -1349,13 +1895,18 @@ "users": "Kasutajad", "utilities": "TÃļÃļriistad", "validate": "Valideeri", + "validate_endpoint_error": "Sisesta korrektne URL", "variables": "Muutujad", "version": "Versioon", - "version_announcement_closing": "Sinu sÃĩber, Alex", - "version_announcement_message": "Hei! Saadaval on uus Immich'i versioon. Palun vÃĩta aega, et lugeda väljalasketeadet ning veendu, et su seadistus on ajakohane, et vältida konfiguratsiooniprobleeme, eriti kui kasutad WatchTower'it vÃĩi muud mehhanismi, mis Immich'it automaatselt uuendab.", + "version_announcement_closing": "Sinu sÃĩber Alex", + "version_announcement_message": "Hei! Saadaval on uus Immich'i versioon. Palun vÃĩta aega, et lugeda väljalasketeadet ja veenduda, et su seadistus on ajakohane, vältimaks konfiguratsiooniprobleeme, eriti kui kasutad WatchTower'it vÃĩi muud mehhanismi, mis Immich'it automaatselt uuendab.", + "version_announcement_overlay_release_notes": "väljalasketeadet", + "version_announcement_overlay_text_1": "Hei sÃĩber, on saadaval uus versioon rakendusest", + "version_announcement_overlay_text_2": "palun vÃĩta aega, et lugeda ", + "version_announcement_overlay_text_3": " ning veendu, et su docker-compose ja .env seadistus on ajakohane, et vältida konfiguratsiooniprobleeme, eriti kui kasutad WatchTower'it vÃĩi muud mehhanismi, mis serveripoolset rakendust automaatselt uuendab.", + "version_announcement_overlay_title": "Uus serveri versioon saadaval 🎉", "version_history": "Versiooniajalugu", "version_history_item": "Versioon {version} paigaldatud {date}", - "video": "Video", "video_hover_setting": "Esita hÃĩljutamisel video eelvaade", "video_hover_setting_description": "Esita video eelvaade, kui hiirt selle kohal hÃĩljutada. Isegi kui keelatud, saab taasesituse alustada taasesitusnupu kohal hÃĩljutades.", "videos": "Videod", @@ -1372,15 +1923,21 @@ "view_previous_asset": "Vaata eelmist Ãŧksust", "view_qr_code": "Vaata QR-koodi", "view_stack": "Vaata virna", + "viewer_remove_from_stack": "Eemalda virnast", + "viewer_stack_use_as_main_asset": "Kasuta peamise Ãŧksusena", + "viewer_unstack": "Eralda", "visibility_changed": "{count, plural, one {# isiku} other {# isiku}} nähtavus muudetud", "waiting": "Ootel", "warning": "Hoiatus", "week": "Nädal", "welcome": "Tere tulemast", "welcome_to_immich": "Tere tulemast Immich'isse", + "wifi_name": "WiFi-vÃĩrgu nimi", + "wrong_pin_code": "Vale PIN-kood", "year": "Aasta", "years_ago": "{years, plural, one {# aasta} other {# aastat}} tagasi", "yes": "Jah", "you_dont_have_any_shared_links": "Sul pole Ãŧhtegi jagatud linki", + "your_wifi_name": "Sinu WiFi-vÃĩrgu nimi", "zoom_image": "Suumi pilti" } diff --git a/i18n/eu.json b/i18n/eu.json index 0967ef424b..f2840bfebd 100644 --- a/i18n/eu.json +++ b/i18n/eu.json @@ -1 +1,18 @@ -{} +{ + "active": "Martxan", + "add": "Gehitu", + "add_a_description": "Azalpena gehitu", + "add_a_name": "Izena gehitu", + "add_a_title": "Izenburua gehitu", + "add_more_users": "Erabiltzaile gehiago gehitu", + "add_photos": "Argazkiak gehitu", + "add_to_album": "Albumera gehitu", + "add_to_album_bottom_sheet_already_exists": "Dagoeneko {album} albumenean", + "add_to_shared_album": "Gehitu partekatutako albumera", + "add_url": "URL-a gehitu", + "added_to_favorites": "Faboritoetara gehituta", + "admin": { + "cleanup": "Garbiketa", + "image_quality": "Kalitatea" + } +} diff --git a/i18n/fa.json b/i18n/fa.json index a6cca739b4..f0b99a81ca 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -4,6 +4,7 @@ "account_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ", "acknowledge": "Ų…ØĒ؈ØŦŲ‡ Ø´Ø¯Ų…", "action": "ØšŲ…Ų„ÚŠØąØ¯", + "action_common_update": "Ø¨Ų‡â€Œ ØąŲˆØ˛â€ŒØąØŗØ§Ų†ÛŒ", "actions": "ØšŲ…Ų„ÚŠØąØ¯", "active": "ŲØšØ§Ų„", "activity": "ŲØšØ§Ų„ÛŒØĒ", @@ -64,8 +65,6 @@ "job_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ ÚŠØ§Øą", "job_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ Ų‡Ų…Ø˛Ų…Ø§Ų†ÛŒ ÚŠØ§Øą", "job_status": "؈ØļØšÛŒØĒ ÚŠØ§Øą", - "jobs_delayed": "", - "jobs_failed": "", "library_created": "ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ ایØŦاد Ø´Ø¯Ų‡: {library}", "library_deleted": "ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡ Ø­Ø°Ų شد", "library_import_path_description": "یڊ ŲžŲˆØ´Ų‡ Ø¨ØąØ§ÛŒ ŲˆØ§ØąØ¯ ÚŠØąØ¯Ų† Ų…Ø´ØŽØĩ ÚŠŲ†ÛŒØ¯. Ø§ÛŒŲ† ŲžŲˆØ´Ų‡ØŒ Ø¨Ų‡ Ų‡Ų…ØąØ§Ų‡ Ø˛ÛŒØąŲžŲˆØ´Ų‡â€ŒŲ‡Ø§ØŒ Ø¨ØąØ§ÛŒ ÛŒØ§ŲØĒŲ† ØĒØĩØ§ŲˆÛŒØą ؈ ŲˆÛŒØ¯ÛŒŲˆŲ‡Ø§ Ø§ØŗÚŠŲ† ØŽŲˆØ§Ų‡Ø¯ شد.", @@ -127,7 +126,6 @@ "metadata_extraction_job": "Ø§ØŗØĒØŽØąØ§ØŦ ŲØąØ§ Ø¯Ø§Ø¯Ų‡", "metadata_extraction_job_description": "Ø§ØŗØĒØŽØąØ§ØŦ Ø§ØˇŲ„Ø§ØšØ§ØĒ Ø§Ø¨ØąØ¯Ø§Ø¯Ų‡ØŒ Ų…Ø§Ų†Ų†Ø¯ Ų…ŲˆŲ‚ØšÛŒØĒ ØŦØēØąØ§ŲÛŒØ§ÛŒÛŒ ؈ ÚŠÛŒŲÛŒØĒ Ø§Ø˛ Ų‡Øą ŲØ§ÛŒŲ„", "migration_job": "Ų…Ų‡Ø§ØŦØąØĒ", - "migration_job_description": "", "no_paths_added": "Ų‡ÛŒÚ† Ų…ØŗÛŒØąÛŒ اØļØ§ŲŲ‡ Ų†Ø´Ø¯Ų‡", "no_pattern_added": "Ų‡ÛŒÚ† Ø§Ų„Ú¯ŲˆÛŒ اØļØ§ŲŲ‡ Ų†Ø´Ø¯Ų‡", "note_apply_storage_label_previous_assets": "ØĒ؈ØŦŲ‡: Ø¨ØąØ§ÛŒ Ø§ØšŲ…Ø§Ų„ Ø¨ØąÚ†ØŗØ¨ Ø°ØŽÛŒØąŲ‡ ØŗØ§Ø˛ÛŒ Ø¨Ų‡ Ø¯Ø§ØąØ§ÛŒÛŒ Ų‡Ø§ÛŒÛŒ ÚŠŲ‡ Ų‚Ø¨Ų„Ø§Ų‹ Ø¨Ø§ØąÚ¯Ø°Ø§ØąÛŒ Ø´Ø¯Ų‡ Ø§Ų†Ø¯ØŒ Ø¯ØŗØĒŲˆØą Ø˛ÛŒØą ØąØ§ اØŦØąØ§ ÚŠŲ†ÛŒØ¯", @@ -153,20 +151,13 @@ "oauth_auto_register": "ØĢبØĒ ØŽŲˆØ¯ÚŠØ§Øą", "oauth_auto_register_description": "ÚŠØ§ØąØ¨ØąØ§Ų† ØŦدید ØąØ§ ŲžØŗ Ø§Ø˛ ŲˆØąŲˆØ¯ با OAuth Ø¨Ų‡ ØˇŲˆØą ØŽŲˆØ¯ÚŠØ§Øą ØĢبØĒ Ų†Ø§Ų… ÚŠŲ†", "oauth_button_text": "Ų…ØĒŲ† Ø¯ÚŠŲ…Ų‡", - "oauth_client_id": "Ø´Ų†Ø§ØŗŲ‡ ÚŠØ§ØąØ¨Øą", - "oauth_client_secret": "Ø´Ų†Ø§ØŗŲ‡ Ų…Ø­ØąŲ…Ø§Ų†Ų‡ ÚŠØ§ØąØ¨Øą", "oauth_enable_description": "ŲˆØąŲˆØ¯ ØĒŲˆØŗØˇ OAuth", - "oauth_issuer_url": "Ų†Ø´Ø§Ų†ÛŒ ŲˆØ¨ ØĩØ§Ø¯Øą ÚŠŲ†Ų†Ø¯Ų‡", "oauth_mobile_redirect_uri": "ØĒØēÛŒÛŒØą Ų…ØŗÛŒØą URI Ų…ŲˆØ¨Ø§ÛŒŲ„", "oauth_mobile_redirect_uri_override": "ØĒØēÛŒÛŒØą Ų…ØŗÛŒØą URI ØĒ؄؁؆ Ų‡Ų…ØąØ§Ų‡", "oauth_mobile_redirect_uri_override_description": "Ø˛Ų…Ø§Ų†ÛŒ ÚŠŲ‡ 'app.immich:/' یڊ URI ŲžØąØ´ Ų†Ø§Ų…ØšØĒØ¨Øą Ø§ØŗØĒ، ŲØšØ§Ų„ ÚŠŲ†ÛŒØ¯.", - "oauth_profile_signing_algorithm": "Ø§Ų„Ú¯ŲˆØąÛŒØĒŲ… Ø§Ų…Øļای ŲžØąŲˆŲØ§ÛŒŲ„", - "oauth_profile_signing_algorithm_description": "Ø§Ų„Ú¯ŲˆØąÛŒØĒŲ… Ų…ŲˆØąØ¯ Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø¨ØąØ§ÛŒ Ø§Ų…Øļای ŲžØąŲˆŲØ§ÛŒŲ„ ÚŠØ§ØąØ¨Øą.", - "oauth_scope": "Ų…Ø­Ø¯ŲˆØ¯Ų‡", "oauth_settings": "OAuth", "oauth_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ ŲˆØąŲˆØ¯ Ø¨Ų‡ ØŗÛŒØŗØĒŲ… OAuth", "oauth_settings_more_details": "Ø¨ØąØ§ÛŒ ØŦØ˛ØĻیاØĒ بیشØĒØą Ø¯Øą Ų…ŲˆØąØ¯ Ø§ÛŒŲ† ŲˆÛŒÚ˜Ú¯ÛŒØŒ Ø¨Ų‡ Ų…ØŗØĒŲ†Ø¯Ø§ØĒ Ų…ØąØ§ØŦØšŲ‡ ÚŠŲ†ÛŒØ¯.", - "oauth_signing_algorithm": "Ø§Ų„Ú¯ŲˆØąÛŒØĒŲ… Ø§Ų…Øļا", "oauth_storage_label_claim": "Ø¯ØąØŽŲˆØ§ØŗØĒ Ø¨ØąÚ†ØŗØ¨ ؁Øļای Ø°ØŽÛŒØąŲ‡ ØŗØ§Ø˛ÛŒ", "oauth_storage_label_claim_description": "ØĒŲ†Ø¸ÛŒŲ… ØŽŲˆØ¯ÚŠØ§Øą Ø¨ØąÚ†ØŗØ¨ ؁Øļای Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ ÚŠØ§ØąØ¨Øą Ø¨Ų‡ Ų…Ų‚Ø¯Ø§Øą Ø¯ØąØŽŲˆØ§ØŗØĒ Ø´Ø¯Ų‡.", "oauth_storage_quota_claim": "Ø¯ØąØŽŲˆØ§ØŗØĒ ØŗŲ‡Ų…ÛŒŲ‡ ؁Øļای Ø°ØŽÛŒØąŲ‡ ØŗØ§Ø˛ÛŒ", @@ -184,8 +175,6 @@ "registration": "ØĢبØĒ Ų†Ø§Ų… Ų…Ø¯ÛŒØą", "registration_description": "Ø§Ø˛ ØĸŲ†ØŦایی ÚŠŲ‡ Ø´Ų…Ø§ Ø§ŲˆŲ„ÛŒŲ† ÚŠØ§ØąØ¨Øą Ø¯Øą ØŗÛŒØŗØĒŲ… Ų‡ØŗØĒید، Ø¨Ų‡ ØšŲ†ŲˆØ§Ų† Ų…Ø¯ÛŒØą ØĒØšÛŒÛŒŲ† Ø´Ø¯Ų‡â€ŒØ§ÛŒØ¯ ؈ Ų…ØŗØĻŲˆŲ„ÛŒØĒ Ø§Ų†ØŦØ§Ų… ŲˆØ¸Ø§ÛŒŲ Ų…Ø¯ÛŒØąÛŒØĒی Ø¨Øą ØšŲ‡Ø¯Ų‡ Ø´Ų…Ø§ ØŽŲˆØ§Ų‡Ø¯ Ø¨ŲˆØ¯ ؈ ÚŠØ§ØąØ¨ØąØ§Ų† اØļØ§ŲÛŒ ØĒŲˆØŗØˇ Ø´Ų…Ø§ ایØŦاد ØŽŲˆØ§Ų‡Ų†Ø¯ شد.", "repair_all": "Ø¨Ø§Ø˛ØŗØ§Ø˛ÛŒ Ų‡Ų…Ų‡", - "repair_matched_items": "", - "repaired_items": "", "require_password_change_on_login": "Ø§Ų„Ø˛Ø§Ų… ÚŠØ§ØąØ¨Øą Ø¨Ų‡ ØĒØēÛŒÛŒØą Ú¯Ø°ØąŲˆØ§Ú˜Ų‡ Ø¯Øą Ø§ŲˆŲ„ÛŒŲ† ŲˆØąŲˆØ¯", "reset_settings_to_default": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨Ų‡ Ø­Ø§Ų„ØĒ ŲžÛŒØ´â€ŒŲØąØļ", "reset_settings_to_recent_saved": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨Ų‡ ØĸØŽØąÛŒŲ† ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø°ØŽÛŒØąŲ‡ Ø´Ø¯Ų‡", @@ -202,7 +191,6 @@ "smart_search_job_description": "اØŦØąØ§ÛŒ ÛŒØ§Ø¯Ú¯ÛŒØąÛŒ Ų…Ø§Ø´ÛŒŲ†ÛŒ Ø¨Øą ØąŲˆÛŒ Ø¯Ø§ØąØ§ÛŒÛŒâ€ŒŲ‡Ø§ Ø¨ØąØ§ÛŒ ŲžØ´ØĒÛŒØ¨Ø§Ų†ÛŒ Ø§Ø˛ ØŦØŗØĒØŦŲˆÛŒ Ų‡ŲˆØ´Ų…Ų†Ø¯", "storage_template_date_time_description": "Ø˛Ų…Ø§Ų†â€ŒØ¨Ų†Ø¯ÛŒ ایØŦاد Ø¯Ø§ØąØ§ÛŒÛŒ Ø¨ØąØ§ÛŒ Ø§ØˇŲ„Ø§ØšØ§ØĒ ØĒØ§ØąÛŒØŽ ؈ Ø˛Ų…Ø§Ų† Ø§ØŗØĒŲØ§Ø¯Ų‡ Ų…ÛŒâ€ŒØ´ŲˆØ¯", "storage_template_date_time_sample": "Ų†Ų…ŲˆŲ†Ų‡ Ø˛Ų…Ø§Ų† {date}", - "storage_template_enable_description": "", "storage_template_hash_verification_enabled": "ØĒØŖÛŒÛŒØ¯ Ų‡ŲŽØ´ ŲØšØ§Ų„ شد", "storage_template_hash_verification_enabled_description": "ØĒØŖÛŒÛŒØ¯ Ų‡ŲŽØ´ ØąØ§ ŲØšØ§Ų„ Ų…ÛŒâ€ŒÚŠŲ†Ø¯Ø› Ø§ÛŒŲ† Ú¯Ø˛ÛŒŲ†Ų‡ ØąØ§ ØēÛŒØąŲØšØ§Ų„ Ų†ÚŠŲ†ÛŒØ¯ Ų…Ú¯Øą Ø§ÛŒŲ†ÚŠŲ‡ Ø§Ø˛ ØšŲˆØ§Ų‚Ø¨ ØĸŲ† Ų…ØˇŲ…ØĻŲ† باشید", "storage_template_migration": "Ø§Ų†ØĒŲ‚Ø§Ų„ Ø§Ų„Ú¯ŲˆÛŒ Ø°ØŽÛŒØąŲ‡ ØŗØ§Ø˛ÛŒ", @@ -248,7 +236,6 @@ "transcoding_hardware_acceleration": "Ø´ØĒاب Ø¯Ų‡Ų†Ø¯Ų‡ ØŗØŽØĒ Ø§ŲØ˛Ø§ØąÛŒ", "transcoding_hardware_acceleration_description": "ØĸØ˛Ų…Ø§ÛŒØ´ÛŒØ› Ø¨ØŗÛŒØ§Øą ØŗØąÛŒØšâ€ŒØĒØą Ø§ØŗØĒ، Ø§Ų…Ø§ Ø¯Øą Ų‡Ų…Ø§Ų† بیØĒâ€ŒØąÛŒØĒ ÚŠÛŒŲÛŒØĒ ÚŠŲ…ØĒØąÛŒ ØŽŲˆØ§Ų‡Ø¯ داشØĒ", "transcoding_hardware_decoding": "ØąŲ…Ø˛Ú¯Ø´Ø§ÛŒÛŒ ØŗØŽØĒ Ø§ŲØ˛Ø§ØąÛŒ", - "transcoding_hardware_decoding_setting_description": "", "transcoding_hevc_codec": "ڊدڊ HEVC", "transcoding_max_b_frames": "بیشØĒØąÛŒŲ† B-frames", "transcoding_max_b_frames_description": "Ų…Ų‚Ø§Ø¯ÛŒØą Ø¨Ø§Ų„Ø§ØĒØą ÚŠØ§ØąØ§ÛŒÛŒ ŲØ´ØąØ¯Ų‡ ØŗØ§Ø˛ÛŒ ØąØ§ Ø¨Ų‡Ø¨ŲˆØ¯ Ų…ÛŒâ€ŒØ¨ØŽØ´Ų†Ø¯ØŒ Ø§Ų…Ø§ ÚŠØ¯Ú¯Ø°Ø§ØąÛŒ ØąØ§ ÚŠŲ†Ø¯ Ų…ÛŒâ€ŒÚŠŲ†Ų†Ø¯. Ų…Ų…ÚŠŲ† Ø§ØŗØĒ با Ø´ØĒاب Ø¯Ų‡ÛŒ ØŗØŽØĒâ€ŒØ§ŲØ˛Ø§ØąÛŒ Ø¯Øą Ø¯ØŗØĒÚ¯Ø§Ų‡â€ŒŲ‡Ø§ÛŒ Ų‚Ø¯ÛŒŲ…ÛŒ ØŗØ§Ø˛Ú¯Ø§Øą Ų†Ø¨Ø§Ø´Ø¯. Ų…Ų‚Ø¯Ø§Øą( 0 ) B-frames ØąØ§ ØēÛŒØąŲØšØ§Ų„ Ų…ÛŒâ€ŒÚŠŲ†Ø¯ØŒ Ø¯Øą Ø­Ø§Ų„ÛŒ ÚŠŲ‡ Ų…Ų‚Ø¯Ø§Øą ( 1 ) Ø§ÛŒŲ† Ų…Ų‚Ø¯Ø§Øą ØąØ§ Ø¨Ų‡ ØĩŲˆØąØĒ ØŽŲˆØ¯ÚŠØ§Øą ØĒŲ†Ø¸ÛŒŲ… Ų…ÛŒâ€ŒÚŠŲ†Ø¯.", @@ -272,7 +259,6 @@ "transcoding_temporal_aq_description": "Ø§ÛŒŲ† Ų…ŲˆØąØ¯ ŲŲ‚Øˇ Ø¨ØąØ§ÛŒ NVENC Ø§ØšŲ…Ø§Ų„ Ų…ÛŒ Ø´ŲˆØ¯. Ø§ŲØ˛Ø§ÛŒØ´ ÚŠÛŒŲÛŒØĒ Ø¯Øą ØĩØ­Ų†Ų‡ Ų‡Ø§ÛŒ با ØŦØ˛ØĻیاØĒ Ø¨Ø§Ų„Ø§ ؈ Ø­ØąÚŠØĒ ÚŠŲ…. Ų…Ų…ÚŠŲ† Ø§ØŗØĒ با Ø¯ØŗØĒÚ¯Ø§Ų‡ Ų‡Ø§ÛŒ Ų‚Ø¯ÛŒŲ…ÛŒ ØĒØą ØŗØ§Ø˛Ú¯Ø§Øą Ų†Ø¨Ø§Ø´Ø¯.", "transcoding_threads": "ØąØ´ØĒŲ‡ Ų‡Ø§ ( Ų…ŲˆØļŲˆØšØ§ØĒ )", "transcoding_threads_description": "Ų…Ų‚Ø§Ø¯ÛŒØą Ø¨Ø§Ų„Ø§ØĒØą Ų…Ų†ØŦØą Ø¨Ų‡ ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ ØŗØąÛŒØš ØĒØą Ų…ÛŒ Ø´ŲˆØ¯ØŒ Ø§Ų…Ø§ ؁Øļای ÚŠŲ…ØĒØąÛŒ Ø¨ØąØ§ÛŒ ŲžØąØ¯Ø§Ø˛Ø´ ØŗØ§ÛŒØą ŲˆØ¸Ø§ÛŒŲ ØŗØąŲˆØą Ø¯Øą Ø­ÛŒŲ† ŲØšØ§Ų„ÛŒØĒ Ø¨Ø§Ų‚ÛŒ Ų…ÛŒ Ú¯Ø°Ø§ØąØ¯. Ø§ÛŒŲ† Ų…Ų‚Ø¯Ø§Øą Ų†Ø¨Ø§ÛŒØ¯ بیشØĒØą Ø§Ø˛ ØĒؚداد Ų‡ØŗØĒŲ‡ Ų‡Ø§ÛŒ CPU باشد. Ø§Ú¯Øą ØąŲˆÛŒ 0 ØĒŲ†Ø¸ÛŒŲ… Ø´ŲˆØ¯ØŒ بیشØĒØąÛŒŲ† Ø§ØŗØĒŲØ§Ø¯Ų‡ ØąØ§ ØŽŲˆØ§Ų‡Ø¯ داشØĒ.", - "transcoding_tone_mapping": "", "transcoding_tone_mapping_description": "ØĒŲ„Ø§Ø´ Ø¨ØąØ§ÛŒ Ø­ŲØ¸ Ø¸Ø§Ų‡Øą ŲˆÛŒØ¯ÛŒŲˆŲ‡Ø§ÛŒ HDR Ų‡Ų†Ú¯Ø§Ų… ØĒØ¨Ø¯ÛŒŲ„ Ø¨Ų‡ SDR. Ų‡Øą Ø§Ų„Ú¯ŲˆØąÛŒØĒŲ… ØĒØšØ§Ø¯Ų„ Ų‡Ø§ÛŒ Ų…ØĒŲØ§ŲˆØĒی ØąØ§ Ø¨ØąØ§ÛŒ ØąŲ†Ú¯ØŒ ØŦØ˛ØĻیاØĒ ؈ ØąŲˆØ´Ų†Ø§ÛŒÛŒ ایØŦاد Ų…ÛŒ ÚŠŲ†Ø¯. Hable ØŦØ˛ØĻیاØĒ ØąØ§ Ø­ŲØ¸ Ų…ÛŒ ÚŠŲ†Ø¯ØŒ Mobius ØąŲ†Ú¯ ØąØ§ Ø­ŲØ¸ Ų…ÛŒ ÚŠŲ†Ø¯ ؈ Reinhard ØąŲˆØ´Ų†Ø§ÛŒÛŒ ØąØ§ Ø­ŲØ¸ Ų…ÛŒ ÚŠŲ†Ø¯.", "transcoding_transcode_policy": "ØŗÛŒØ§ØŗØĒ ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ", "transcoding_transcode_policy_description": "ØŗÛŒØ§ØŗØĒ Ø¨ØąØ§ÛŒ Ø˛Ų…Ø§Ų†ÛŒ ÚŠŲ‡ ŲˆÛŒØ¯ÛŒŲˆÛŒÛŒ باید Ų…ØŦددا ØĒØ¨Ø¯ÛŒŲ„ (ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ) Ø´ŲˆØ¯. ŲˆÛŒØ¯ÛŒŲˆŲ‡Ø§ÛŒ HDR Ų‡Ų…ÛŒØ´Ų‡ ØĒØ¨Ø¯ÛŒŲ„ (ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ) Ų…ØŦدد ØŽŲˆØ§Ų‡Ų†Ø¯ شد (Ų…Ú¯Øą ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ Ų…ØŦدد ØēÛŒØąŲØšØ§Ų„ باشد).", @@ -312,15 +298,12 @@ "administration": "Ų…Ø¯ÛŒØąÛŒØĒ", "advanced": "ŲžÛŒØ´ØąŲØĒŲ‡", "album_added": "ØĸŲ„Ø¨ŲˆŲ… اØļØ§ŲŲ‡ شد", - "album_added_notification_setting_description": "", "album_cover_updated": "ØŦŲ„Ø¯ ØĸŲ„Ø¨ŲˆŲ… Ø¨Ų‡â€ŒØąŲˆØ˛ØąØŗØ§Ų†ÛŒ شد", "album_info_updated": "Ø§ØˇŲ„Ø§ØšØ§ØĒ ØĸŲ„Ø¨ŲˆŲ… Ø¨Ų‡â€ŒØąŲˆØ˛ØąØŗØ§Ų†ÛŒ شد", "album_name": "Ų†Ø§Ų… ØĸŲ„Ø¨ŲˆŲ…", "album_options": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ØĸŲ„Ø¨ŲˆŲ…", "album_updated": "ØĸŲ„Ø¨ŲˆŲ… Ø¨Ų‡â€ŒØąŲˆØ˛ØąØŗØ§Ų†ÛŒ شد", - "album_updated_setting_description": "", "albums": "ØĸŲ„Ø¨ŲˆŲ…â€ŒŲ‡Ø§", - "albums_count": "", "all": "Ų‡Ų…Ų‡", "all_people": "Ų‡Ų…Ų‡ Ø§ŲØąØ§Ø¯", "allow_dark_mode": "اØŦØ§Ø˛Ų‡ Ø¯Ø§Ø¯Ų† Ø¨Ų‡ Ø­Ø§Ų„ØĒ ØĒØ§ØąÛŒÚŠ", @@ -330,18 +313,13 @@ "app_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨ØąŲ†Ø§Ų…Ų‡", "appears_in": "Ø¸Ø§Ų‡Øą Ų…ÛŒâ€ŒØ´ŲˆØ¯ Ø¯Øą", "archive": "Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ", - "archive_or_unarchive_photo": "", "archive_size": "Ø§Ų†Ø¯Ø§Ø˛Ų‡ Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ", - "archive_size_description": "", "asset_offline": "Ų…Ø­ØĒŲˆØ§ ØĸŲŲ„Ø§ÛŒŲ†", "assets": "Ų…Ø­ØĒŲˆØ§Ų‡Ø§", "authorized_devices": "Ø¯ØŗØĒÚ¯Ø§Ų‡â€ŒŲ‡Ø§ÛŒ Ų…ØŦØ§Ø˛", "back": "Ø¨Ø§Ø˛Ú¯Ø´ØĒ", "backward": "ØšŲ‚Ø¨", "blurred_background": "ŲžØŗâ€ŒØ˛Ų…ÛŒŲ†Ų‡ Ų…Ø­Ųˆ", - "bulk_delete_duplicates_confirmation": "", - "bulk_keep_duplicates_confirmation": "", - "bulk_trash_duplicates_confirmation": "", "camera": "Ø¯ŲˆØąØ¨ÛŒŲ†", "camera_brand": "Ø¨ØąŲ†Ø¯ Ø¯ŲˆØąØ¨ÛŒŲ†", "camera_model": "Ų…Ø¯Ų„ Ø¯ŲˆØąØ¨ÛŒŲ†", @@ -356,10 +334,8 @@ "change_name_successfully": "Ų†Ø§Ų… با Ų…ŲˆŲŲ‚ÛŒØĒ ØĒØēÛŒÛŒØą ÛŒØ§ŲØĒ", "change_password": "ØĒØēÛŒÛŒØą ØąŲ…Ø˛ ØšØ¨ŲˆØą", "change_your_password": "ØąŲ…Ø˛ ØšØ¨ŲˆØą ØŽŲˆØ¯ ØąØ§ ØĒØēÛŒÛŒØą Ø¯Ų‡ÛŒØ¯", - "changed_visibility_successfully": "", "check_all": "Ø§Ų†ØĒ؎اب Ų‡Ų…Ų‡", "check_logs": "Ø¨ØąØąØŗÛŒ Ų„Ø§Ú¯â€ŒŲ‡Ø§", - "choose_matching_people_to_merge": "", "city": "Ø´Ų‡Øą", "clear": "ŲžØ§ÚŠ ÚŠØąØ¯Ų†", "clear_all": "ŲžØ§ÚŠ ÚŠØąØ¯Ų† Ų‡Ų…Ų‡", @@ -372,7 +348,6 @@ "comments_are_disabled": "Ų†Ø¸ØąØ§ØĒ ØēÛŒØąŲØšØ§Ų„ Ų‡ØŗØĒŲ†Ø¯", "confirm": "ØĒØŖÛŒÛŒØ¯", "confirm_admin_password": "ØĒØŖÛŒÛŒØ¯ ØąŲ…Ø˛ ØšØ¨ŲˆØą Ų…Ø¯ÛŒØą", - "confirm_delete_shared_link": "", "confirm_password": "ØĒØŖÛŒÛŒØ¯ ØąŲ…Ø˛ ØšØ¨ŲˆØą", "contain": "Ø´Ø§Ų…Ų„", "context": "Ø˛Ų…ÛŒŲ†Ų‡", @@ -399,8 +374,6 @@ "create_user": "ایØŦاد ÚŠØ§ØąØ¨Øą", "created": "ایØŦاد شد", "current_device": "Ø¯ØŗØĒÚ¯Ø§Ų‡ ŲØšŲ„ÛŒ", - "custom_locale": "", - "custom_locale_description": "", "dark": "ØĒØ§ØąÛŒÚŠ", "date_after": "ØĒØ§ØąÛŒØŽ ŲžØŗ Ø§Ø˛", "date_and_time": "ØĒØ§ØąÛŒØŽ ؈ Ø˛Ų…Ø§Ų†", @@ -408,12 +381,8 @@ "date_range": "Ø¨Ø§Ø˛Ų‡ Ø˛Ų…Ø§Ų†ÛŒ", "day": "ØąŲˆØ˛", "deduplicate_all": "Ø­Ø°Ų ØĒÚŠØąØ§ØąÛŒâ€ŒŲ‡Ø§ Ø¨Ų‡ ØĩŲˆØąØĒ ÚŠØ§Ų…Ų„", - "default_locale": "", - "default_locale_description": "", "delete": "Ø­Ø°Ų", "delete_album": "Ø­Ø°Ų ØĸŲ„Ø¨ŲˆŲ…", - "delete_api_key_prompt": "", - "delete_duplicates_confirmation": "", "delete_key": "Ø­Ø°Ų ÚŠŲ„ÛŒØ¯", "delete_library": "Ø­Ø°Ų ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡", "delete_link": "Ø­Ø°Ų Ų„ÛŒŲ†ÚŠ", @@ -431,14 +400,12 @@ "display_options": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ Ų†Ų…Ø§ÛŒØ´", "display_order": "ØĒØąØĒیب Ų†Ų…Ø§ÛŒØ´", "display_original_photos": "Ų†Ų…Ø§ÛŒØ´ ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ اØĩŲ„ÛŒ", - "display_original_photos_setting_description": "", "done": "Ø§Ų†ØŦØ§Ų… شد", "download": "Ø¯Ø§Ų†Ų„ŲˆØ¯", "download_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¯Ø§Ų†Ų„ŲˆØ¯", "download_settings_description": "Ų…Ø¯ÛŒØąÛŒØĒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ų…ØąØĒØ¨Øˇ با Ø¯Ø§Ų†Ų„ŲˆØ¯ Ų…Ø­ØĒŲˆØ§", "downloading": "Ø¯Øą Ø­Ø§Ų„ Ø¯Ø§Ų†Ų„ŲˆØ¯", "duplicates": "ØĒÚŠØąØ§ØąÛŒâ€ŒŲ‡Ø§", - "duplicates_description": "", "duration": "Ų…Ø¯ØĒ Ø˛Ų…Ø§Ų†", "edit_album": "ŲˆÛŒØąØ§ÛŒØ´ ØĸŲ„Ø¨ŲˆŲ…", "edit_avatar": "ŲˆÛŒØąØ§ÛŒØ´ ØĸŲˆØ§ØĒØ§Øą", @@ -446,8 +413,6 @@ "edit_date_and_time": "ŲˆÛŒØąØ§ÛŒØ´ ØĒØ§ØąÛŒØŽ ؈ Ø˛Ų…Ø§Ų†", "edit_exclusion_pattern": "ŲˆÛŒØąØ§ÛŒØ´ Ø§Ų„Ú¯ŲˆÛŒ Ø§ØŗØĒØĢŲ†Ø§ØĄ", "edit_faces": "ŲˆÛŒØąØ§ÛŒØ´ Ú†Ų‡ØąŲ‡â€ŒŲ‡Ø§", - "edit_import_path": "", - "edit_import_paths": "", "edit_key": "ŲˆÛŒØąØ§ÛŒØ´ ÚŠŲ„ÛŒØ¯", "edit_link": "ŲˆÛŒØąØ§ÛŒØ´ Ų„ÛŒŲ†ÚŠ", "edit_location": "ŲˆÛŒØąØ§ÛŒØ´ Ų…ÚŠØ§Ų†", @@ -462,73 +427,6 @@ "end_date": "ØĒØ§ØąÛŒØŽ ŲžØ§ÛŒØ§Ų†", "error": "ØŽØˇØ§", "error_loading_image": "ØŽØˇØ§ Ø¯Øą Ø¨Ø§ØąÚ¯Ø°Ø§ØąÛŒ ØĒØĩŲˆÛŒØą", - "errors": { - "exclusion_pattern_already_exists": "", - "import_path_already_exists": "", - "paths_validation_failed": "", - "quota_higher_than_disk_size": "", - "repair_unable_to_check_items": "", - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_exclusion_pattern": "", - "unable_to_add_import_path": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_change_password": "", - "unable_to_copy_to_clipboard": "", - "unable_to_create_api_key": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", - "unable_to_delete_exclusion_pattern": "", - "unable_to_delete_import_path": "", - "unable_to_delete_shared_link": "", - "unable_to_delete_user": "", - "unable_to_edit_exclusion_pattern": "", - "unable_to_edit_import_path": "", - "unable_to_empty_trash": "", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", - "unable_to_hide_person": "", - "unable_to_link_oauth_account": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_api_key": "", - "unable_to_remove_deleted_assets": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_api_key": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_timeline_display_status": "", - "unable_to_update_user": "" - }, "exit_slideshow": "ØŽØąŲˆØŦ Ø§Ø˛ Ų†Ų…Ø§ÛŒØ´ Ø§ØŗŲ„Ø§ÛŒØ¯", "expand_all": "Ø¨Ø§Ø˛ ÚŠØąØ¯Ų† Ų‡Ų…Ų‡", "expire_after": "Ų…Ų†Ų‚Øļی Ø´Ø¯Ų† بؚد Ø§Ø˛", @@ -540,15 +438,12 @@ "external": "ØŽØ§ØąØŦی", "external_libraries": "ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡â€ŒŲ‡Ø§ÛŒ ØŽØ§ØąØŦی", "favorite": "ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒ", - "favorite_or_unfavorite_photo": "", "favorites": "ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒâ€ŒŲ‡Ø§", - "feature_photo_updated": "", "file_name": "Ų†Ø§Ų… ŲØ§ÛŒŲ„", "file_name_or_extension": "Ų†Ø§Ų… ŲØ§ÛŒŲ„ یا ŲžØŗŲˆŲ†Ø¯", "filename": "Ų†Ø§Ų… ŲØ§ÛŒŲ„", "filetype": "Ų†ŲˆØš ŲØ§ÛŒŲ„", "filter_people": "ŲÛŒŲ„ØĒØą Ø§ŲØąØ§Ø¯", - "find_them_fast": "", "fix_incorrect_match": "ØąŲØš ØĒØˇØ§Ø¨Ų‚ Ų†Ø§Ø¯ØąØŗØĒ", "forward": "ØŦŲ„Ųˆ", "general": "ØšŲ…ŲˆŲ…ÛŒ", @@ -568,19 +463,11 @@ "immich_web_interface": "ØąØ§Ø¨Øˇ ŲˆØ¨ Immich", "import_from_json": "ŲˆØ§ØąØ¯ ÚŠØąØ¯Ų† Ø§Ø˛ JSON", "import_path": "Ų…ØŗÛŒØą ŲˆØ§ØąØ¯ ÚŠØąØ¯Ų†", - "in_albums": "", "in_archive": "Ø¯Øą Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ", "include_archived": "Ø´Ø§Ų…Ų„ Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ Ø´Ø¯Ų‡â€ŒŲ‡Ø§", "include_shared_albums": "Ø´Ø§Ų…Ų„ ØĸŲ„Ø¨ŲˆŲ…â€ŒŲ‡Ø§ÛŒ اشØĒØąØ§ÚŠÛŒ", - "include_shared_partner_assets": "", "individual_share": "اشØĒØąØ§ÚŠ ŲØąØ¯ÛŒ", "info": "Ø§ØˇŲ„Ø§ØšØ§ØĒ", - "interval": { - "day_at_onepm": "", - "hours": "", - "night_at_midnight": "", - "night_at_twoam": "" - }, "invite_people": "Ø¯ØšŲˆØĒ Ø§ŲØąØ§Ø¯", "invite_to_album": "Ø¯ØšŲˆØĒ Ø¨Ų‡ ØĸŲ„Ø¨ŲˆŲ…", "jobs": "ŲˆØ¸Ø§ÛŒŲ", @@ -607,28 +494,22 @@ "login_has_been_disabled": "ŲˆØąŲˆØ¯ ØēÛŒØąŲØšØ§Ų„ Ø´Ø¯Ų‡ Ø§ØŗØĒ.", "look": "Ų†Ú¯Ø§Ų‡ ÚŠØąØ¯Ų†", "loop_videos": "ŲžØŽØ´ Ų…Ø¯Ø§ŲˆŲ… ŲˆÛŒØ¯ØĻŲˆŲ‡Ø§", - "loop_videos_description": "", "make": "ØŗØ§ØŽØĒŲ†", "manage_shared_links": "Ų…Ø¯ÛŒØąÛŒØĒ Ų„ÛŒŲ†ÚŠâ€ŒŲ‡Ø§ÛŒ اشØĒØąØ§ÚŠÛŒ", - "manage_sharing_with_partners": "", "manage_the_app_settings": "Ų…Ø¯ÛŒØąÛŒØĒ ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø¨ØąŲ†Ø§Ų…Ų‡", "manage_your_account": "Ų…Ø¯ÛŒØąÛŒØĒ Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ Ø´Ų…Ø§", "manage_your_api_keys": "Ų…Ø¯ÛŒØąÛŒØĒ ÚŠŲ„ÛŒØ¯Ų‡Ø§ÛŒ API Ø´Ų…Ø§", "manage_your_devices": "Ų…Ø¯ÛŒØąÛŒØĒ Ø¯ØŗØĒÚ¯Ø§Ų‡â€ŒŲ‡Ø§ÛŒ Ų…ØĒØĩŲ„", "manage_your_oauth_connection": "Ų…Ø¯ÛŒØąÛŒØĒ اØĒØĩØ§Ų„ OAuth Ø´Ų…Ø§", "map": "Ų†Ų‚Ø´Ų‡", - "map_marker_with_image": "", "map_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ų†Ų‚Ø´Ų‡", "matches": "ØĒØˇØ§Ø¨Ų‚â€ŒŲ‡Ø§", "media_type": "Ų†ŲˆØš ØąØŗØ§Ų†Ų‡", "memories": "ØŽØ§ØˇØąØ§ØĒ", - "memories_setting_description": "", "memory": "ØŽØ§ØˇØąŲ‡", "menu": "Ų…Ų†Ųˆ", "merge": "ادØēØ§Ų…", "merge_people": "ادØēØ§Ų… Ø§ŲØąØ§Ø¯", - "merge_people_limit": "", - "merge_people_prompt": "", "merge_people_successfully": "ادØēØ§Ų… Ø§ŲØąØ§Ø¯ با Ų…ŲˆŲŲ‚ÛŒØĒ Ø§Ų†ØŦØ§Ų… شد", "minimize": "ÚŠŲˆÚ†ÚŠ ÚŠØąØ¯Ų†", "minute": "Ø¯Ų‚ÛŒŲ‚Ų‡", @@ -649,28 +530,18 @@ "next": "بؚدی", "next_memory": "ØŽØ§ØˇØąŲ‡ بؚدی", "no": "ØŽÛŒØą", - "no_albums_message": "", - "no_archived_assets_message": "", - "no_assets_message": "", "no_duplicates_found": "Ų‡ÛŒÚ† ØĒÚŠØąØ§ØąÛŒ ÛŒØ§ŲØĒ Ų†Ø´Ø¯.", "no_exif_info_available": "Ø§ØˇŲ„Ø§ØšØ§ØĒ EXIF Ų…ŲˆØŦŲˆØ¯ Ų†ÛŒØŗØĒ", - "no_explore_results_message": "", - "no_favorites_message": "", - "no_libraries_message": "", "no_name": "Ø¨Ø¯ŲˆŲ† Ų†Ø§Ų…", "no_places": "Ų…ÚŠØ§Ų†ÛŒ ÛŒØ§ŲØĒ Ų†Ø´Ø¯", "no_results": "Ų†ØĒیØŦŲ‡â€ŒØ§ÛŒ ÛŒØ§ŲØĒ Ų†Ø´Ø¯", - "no_shared_albums_message": "", "not_in_any_album": "Ø¯Øą Ų‡ÛŒÚ† ØĸŲ„Ø¨ŲˆŲ…ÛŒ Ų†ÛŒØŗØĒ", - "note_apply_storage_label_to_previously_uploaded assets": "", "notes": "یادداشØĒâ€ŒŲ‡Ø§", "notification_toggle_setting_description": "Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§ÛŒ Ø§ÛŒŲ…ÛŒŲ„ÛŒ ØąØ§ ŲØšØ§Ų„ ÚŠŲ†ÛŒØ¯", "notifications": "Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§", "notifications_setting_description": "Ų…Ø¯ÛŒØąÛŒØĒ Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§", - "oauth": "OAuth", "offline": "ØĸŲŲ„Ø§ÛŒŲ†", "offline_paths": "Ų…ØŗÛŒØąŲ‡Ø§ÛŒ ØĸŲŲ„Ø§ÛŒŲ†", - "offline_paths_description": "", "ok": "ØĒØŖÛŒÛŒØ¯", "oldest_first": "Ų‚Ø¯ÛŒŲ…ÛŒâ€ŒØĒØąÛŒŲ† ابØĒدا", "online": "ØĸŲ†Ų„Ø§ÛŒŲ†", @@ -685,7 +556,6 @@ "owner": "Ų…Ø§Ų„ÚŠ", "partner": "Ø´ØąÛŒÚŠ", "partner_can_access": "{partner} Ų…ÛŒâ€ŒØĒŲˆØ§Ų†Ø¯ Ø¯ØŗØĒØąØŗÛŒ داشØĒŲ‡ باشد", - "partner_can_access_assets": "", "partner_can_access_location": "Ų…ÚŠØ§Ų†â€ŒŲ‡Ø§ÛŒÛŒ ÚŠŲ‡ ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ Ø´Ų…Ø§ Ú¯ØąŲØĒŲ‡ Ø´Ø¯Ų‡â€ŒØ§Ų†Ø¯", "partner_sharing": "اشØĒØąØ§ÚŠâ€ŒÚ¯Ø°Ø§ØąÛŒ با Ø´ØąÛŒÚŠ", "partners": "Ø´ØąÚŠØ§", @@ -693,11 +563,6 @@ "password_does_not_match": "ØąŲ…Ø˛ ØšØ¨ŲˆØą Ų…ØˇØ§Ø¨Ų‚ØĒ Ų†Ø¯Ø§ØąØ¯", "password_required": "ØąŲ…Ø˛ ØšØ¨ŲˆØą Ų…ŲˆØąØ¯ Ų†ÛŒØ§Ø˛ Ø§ØŗØĒ", "password_reset_success": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØąŲ…Ø˛ ØšØ¨ŲˆØą Ų…ŲˆŲŲ‚ÛŒØĒ‌ØĸŲ…ÛŒØ˛ Ø¨ŲˆØ¯", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, "path": "Ų…ØŗÛŒØą", "pattern": "Ø§Ų„Ú¯Ųˆ", "pause": "ØĒŲˆŲ‚Ų", @@ -705,14 +570,12 @@ "paused": "Ų…ØĒŲˆŲ‚Ų Ø´Ø¯Ų‡", "pending": "Ø¯Øą Ø§Ų†ØĒØ¸Ø§Øą", "people": "Ø§ŲØąØ§Ø¯", - "people_sidebar_description": "", "permanent_deletion_warning": "Ų‡Ø´Ø¯Ø§Øą Ø­Ø°Ų داØĻŲ…ÛŒ", "permanent_deletion_warning_setting_description": "Ų†Ų…Ø§ÛŒØ´ Ų‡Ø´Ø¯Ø§Øą Ų‡Ų†Ú¯Ø§Ų… Ø­Ø°Ų داØĻŲ…ÛŒ Ų…Ø­ØĒŲˆØ§Ų‡Ø§", "permanently_delete": "Ø­Ø°Ų داØĻŲ…ÛŒ", "permanently_deleted_asset": "Ų…Ø­ØĒŲˆØ§ÛŒ Ø­Ø°Ų Ø´Ø¯Ų‡ داØĻŲ…ÛŒ", "person": "ŲØąØ¯", "photos": "ØšÚŠØŗâ€ŒŲ‡Ø§", - "photos_count": "", "photos_from_previous_years": "ØšÚŠØŗâ€ŒŲ‡Ø§ÛŒ ØŗØ§Ų„â€ŒŲ‡Ø§ÛŒ گذشØĒŲ‡", "pick_a_location": "یڊ Ų…ÚŠØ§Ų† Ø§Ų†ØĒ؎اب ÚŠŲ†ÛŒØ¯", "place": "Ų…ÚŠØ§Ų†", @@ -736,38 +599,27 @@ "recent_searches": "ØŦØŗØĒØŦŲˆŲ‡Ø§ÛŒ Ø§ØŽÛŒØą", "refresh": "ØĒØ§Ø˛Ų‡ ØŗØ§Ø˛ÛŒ", "refreshed": "ØĒØ§Ø˛Ų‡ ØŗØ§Ø˛ÛŒ شد", - "refreshes_every_file": "", "remove": "Ø­Ø°Ų", "remove_deleted_assets": "Ø­Ø°Ų Ų…Ø­ØĒŲˆØ§Ų‡Ø§ÛŒ Ø­Ø°Ųâ€ŒØ´Ø¯Ų‡", "remove_from_album": "Ø­Ø°Ų Ø§Ø˛ ØĸŲ„Ø¨ŲˆŲ…", "remove_from_favorites": "Ø­Ø°Ų Ø§Ø˛ ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒâ€ŒŲ‡Ø§", - "remove_from_shared_link": "", - "removed_api_key": "", "rename": "ØĒØēÛŒÛŒØą Ų†Ø§Ų…", "repair": "ØĒØšŲ…ÛŒØą", - "repair_no_results_message": "", "replace_with_upload": "ØŦØ§ÛŒÚ¯Ø˛ÛŒŲ†ÛŒ با ØĸŲžŲ„ŲˆØ¯", - "require_password": "", - "require_user_to_change_password_on_first_login": "", "reset": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ", "reset_password": "Ø¨Ø§Ø˛Ų†Ø´Ø§Ų†ÛŒ ØąŲ…Ø˛ ØšØ¨ŲˆØą", - "reset_people_visibility": "", - "resolved_all_duplicates": "", "restore": "Ø¨Ø§Ø˛ÛŒØ§Ø¨ÛŒ", "restore_all": "Ø¨Ø§Ø˛ÛŒØ§Ø¨ÛŒ Ų‡Ų…Ų‡", "restore_user": "Ø¨Ø§Ø˛ÛŒØ§Ø¨ÛŒ ÚŠØ§ØąØ¨Øą", "resume": "Ø§Ø¯Ø§Ų…Ų‡", - "retry_upload": "", "review_duplicates": "Ø¨ØąØąØŗÛŒ ØĒÚŠØąØ§ØąÛŒâ€ŒŲ‡Ø§", "role": "Ų†Ų‚Ø´", "save": "Ø°ØŽÛŒØąŲ‡", - "saved_api_key": "", "saved_profile": "ŲžØąŲˆŲØ§ÛŒŲ„ Ø°ØŽÛŒØąŲ‡ شد", "saved_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø°ØŽÛŒØąŲ‡ شد", "say_something": "Ú†ÛŒØ˛ÛŒ Ø¨Ú¯ŲˆÛŒÛŒØ¯", "scan_all_libraries": "Ø§ØŗÚŠŲ† Ų‡Ų…Ų‡ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡â€ŒŲ‡Ø§", "scan_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø§ØŗÚŠŲ†", - "scanning_for_album": "", "search": "ØŦØŗØĒØŦ؈", "search_albums": "ØŦØŗØĒØŦŲˆÛŒ ØĸŲ„Ø¨ŲˆŲ…â€ŒŲ‡Ø§", "search_by_context": "ØŦØŗØĒØŦ؈ Ø¨ØąØ§ØŗØ§Øŗ Ø˛Ų…ÛŒŲ†Ų‡", @@ -781,8 +633,6 @@ "search_state": "ØŦØŗØĒØŦŲˆÛŒ Ø§ÛŒØ§Ų„ØĒ...", "search_timezone": "ØŦØŗØĒØŦŲˆÛŒ Ų…Ų†ØˇŲ‚Ų‡ Ø˛Ų…Ø§Ų†ÛŒ...", "search_type": "Ų†ŲˆØš ØŦØŗØĒØŦ؈", - "search_your_photos": "", - "searching_locales": "", "second": "ØĢØ§Ų†ÛŒŲ‡", "select_album_cover": "Ø§Ų†ØĒ؎اب ØŦŲ„Ø¯ ØĸŲ„Ø¨ŲˆŲ…", "select_all": "Ø§Ų†ØĒ؎اب Ų‡Ų…Ų‡", @@ -793,41 +643,28 @@ "select_library_owner": "Ø§Ų†ØĒ؎اب Ų…Ø§Ų„ÚŠ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡", "select_new_face": "Ø§Ų†ØĒ؎اب Ú†Ų‡ØąŲ‡ ØŦدید", "select_photos": "Ø§Ų†ØĒ؎اب ØšÚŠØŗâ€ŒŲ‡Ø§", - "select_trash_all": "", "selected": "Ø§Ų†ØĒ؎اب Ø´Ø¯Ų‡", "send_message": "Ø§ØąØŗØ§Ų„ ŲžÛŒØ§Ų…", "send_welcome_email": "Ø§ØąØŗØ§Ų„ Ø§ÛŒŲ…ÛŒŲ„ ØŽŲˆØ´â€ŒØĸŲ…Ø¯Ú¯ŲˆÛŒÛŒ", "server_stats": "ØĸŲ…Ø§Øą ØŗØąŲˆØą", "set": "ØĒŲ†Ø¸ÛŒŲ…", - "set_as_album_cover": "", - "set_as_profile_picture": "", "set_date_of_birth": "ØĒŲ†Ø¸ÛŒŲ… ØĒØ§ØąÛŒØŽ ØĒŲˆŲ„Ø¯", "set_profile_picture": "ØĒŲ†Ø¸ÛŒŲ… ØĒØĩŲˆÛŒØą ŲžØąŲˆŲØ§ÛŒŲ„", - "set_slideshow_to_fullscreen": "", "settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ", "settings_saved": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø°ØŽÛŒØąŲ‡ شد", "share": "اشØĒØąØ§ÚŠâ€ŒÚ¯Ø°Ø§ØąÛŒ", "shared": "Ų…Ø´ØĒØąÚŠ", "shared_by": "Ų…Ø´ØĒØąÚŠ ØĒŲˆØŗØˇ", - "shared_by_you": "", "shared_from_partner": "ØšÚŠØŗâ€ŒŲ‡Ø§ Ø§Ø˛ {partner}", "shared_links": "Ų„ÛŒŲ†ÚŠâ€ŒŲ‡Ø§ÛŒ اشØĒØąØ§ÚŠÛŒ", - "shared_photos_and_videos_count": "", "shared_with_partner": "Ų…Ø´ØĒØąÚŠ با {partner}", "sharing": "اشØĒØąØ§ÚŠâ€ŒÚ¯Ø°Ø§ØąÛŒ", - "sharing_sidebar_description": "", "show_album_options": "Ų†Ų…Ø§ÛŒØ´ Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ØĸŲ„Ø¨ŲˆŲ…", - "show_and_hide_people": "", "show_file_location": "Ų†Ų…Ø§ÛŒØ´ Ų…ØŗÛŒØą ŲØ§ÛŒŲ„", "show_gallery": "Ų†Ų…Ø§ÛŒØ´ Ú¯Ø§Ų„ØąÛŒ", "show_hidden_people": "Ų†Ų…Ø§ÛŒØ´ Ø§ŲØąØ§Ø¯ ŲžŲ†Ų‡Ø§Ų†", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", "show_metadata": "Ų†Ų…Ø§ÛŒØ´ Ø§ØˇŲ„Ø§ØšØ§ØĒ Ų…ØĒا", - "show_or_hide_info": "", "show_password": "Ų†Ų…Ø§ÛŒØ´ ØąŲ…Ø˛ ØšØ¨ŲˆØą", - "show_person_options": "", "show_progress_bar": "Ų†Ų…Ø§ÛŒØ´ Ų†ŲˆØ§Øą ŲžÛŒØ´ØąŲØĒ", "show_search_options": "Ų†Ų…Ø§ÛŒØ´ Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ØŦØŗØĒØŦ؈", "shuffle": "ØĒØĩØ§Ø¯ŲÛŒ", @@ -837,60 +674,39 @@ "skip_to_content": "ØąŲØĒŲ† Ø¨Ų‡ Ų…Ø­ØĒŲˆØ§", "slideshow": "Ų†Ų…Ø§ÛŒØ´ Ø§ØŗŲ„Ø§ÛŒØ¯", "slideshow_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ų†Ų…Ø§ÛŒØ´ Ø§ØŗŲ„Ø§ÛŒØ¯", - "sort_albums_by": "", "stack": "ŲžØ´ØĒŲ‡", - "stack_selected_photos": "", - "stacktrace": "", "start": "Ø´ØąŲˆØš", "start_date": "ØĒØ§ØąÛŒØŽ Ø´ØąŲˆØš", "state": "Ø§ÛŒØ§Ų„ØĒ", "status": "؈ØļØšÛŒØĒ", "stop_motion_photo": "ØĒŲˆŲ‚Ų ØšÚŠØŗ Ų…ØĒØ­ØąÚŠ", - "stop_photo_sharing": "", - "stop_photo_sharing_description": "", - "stop_sharing_photos_with_user": "", "storage": "؁Øļای Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ", "storage_label": "Ø¨ØąÚ†ØŗØ¨ ؁Øļای Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ", - "storage_usage": "", "submit": "Ø§ØąØŗØ§Ų„", "suggestions": "ŲžÛŒØ´Ų†Ų‡Ø§Ø¯Ø§ØĒ", - "sunrise_on_the_beach": "", "swap_merge_direction": "ØĒØēÛŒÛŒØą ØŦŲ‡ØĒ ادØēØ§Ų…", "sync": "Ų‡Ų…Ú¯Ø§Ų…â€ŒØŗØ§Ø˛ÛŒ", "template": "Ø§Ų„Ú¯Ųˆ", "theme": "ØĒŲ…", "theme_selection": "Ø§Ų†ØĒ؎اب ØĒŲ…", - "theme_selection_description": "", - "time_based_memories": "", "timezone": "Ų…Ų†ØˇŲ‚Ų‡ Ø˛Ų…Ø§Ų†ÛŒ", "to_archive": "Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ", "to_favorite": "Ø¨Ų‡ ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒâ€ŒŲ‡Ø§", - "to_trash": "", "toggle_settings": "ØĒØēÛŒÛŒØą ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ", "toggle_theme": "ØĒØēÛŒÛŒØą ØĒŲ… ØĒØ§ØąÛŒÚŠ", "total_usage": "Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ„ÛŒ", "trash": "ØŗØˇŲ„ Ø˛Ø¨Ø§Ų„Ų‡", - "trash_all": "", - "trash_count": "", - "trash_no_results_message": "", - "trashed_items_will_be_permanently_deleted_after": "", "type": "Ų†ŲˆØš", - "unarchive": "", "unfavorite": "Ø­Ø°Ų Ø§Ø˛ ØšŲ„Ø§Ų‚Ų‡â€ŒŲ…Ų†Ø¯ÛŒâ€ŒŲ‡Ø§", "unhide_person": "ØĸØ´ÚŠØ§Øą ÚŠØąØ¯Ų† ŲØąØ¯", "unknown": "Ų†Ø§Ø´Ų†Ø§ØŽØĒŲ‡", "unknown_year": "ØŗØ§Ų„ Ų†Ø§Ų…Ø´ØŽØĩ", "unlimited": "Ų†Ø§Ų…Ø­Ø¯ŲˆØ¯", "unlink_oauth": "Ų„Øē؈ اØĒØĩØ§Ų„ OAuth", - "unlinked_oauth_account": "", "unnamed_album": "ØĸŲ„Ø¨ŲˆŲ… Ø¨Ø¯ŲˆŲ† Ų†Ø§Ų…", "unnamed_share": "اشØĒØąØ§ÚŠ Ø¨Ø¯ŲˆŲ† Ų†Ø§Ų…", "unselect_all": "Ų„Øē؈ Ø§Ų†ØĒ؎اب Ų‡Ų…Ų‡", - "unstack": "", - "untracked_files": "", - "untracked_files_decription": "", "up_next": "Ų…ŲˆØąØ¯ بؚدی", - "updated_password": "", "upload": "ØĸŲžŲ„ŲˆØ¯", "upload_concurrency": "ØĒؚداد ØĸŲžŲ„ŲˆØ¯ Ų‡Ų…Ø˛Ų…Ø§Ų†", "url": "ØĸØ¯ØąØŗ", @@ -904,12 +720,8 @@ "validate": "اؚØĒØ¨Ø§ØąØŗŲ†ØŦی", "variables": "Ų…ØĒØēÛŒØąŲ‡Ø§", "version": "Ų†ØŗØŽŲ‡", - "version_announcement_message": "", "video": "ŲˆÛŒØ¯ÛŒŲˆ", - "video_hover_setting": "", - "video_hover_setting_description": "", "videos": "ŲˆÛŒØ¯ÛŒŲˆŲ‡Ø§", - "videos_count": "", "view": "Ų…Ø´Ø§Ų‡Ø¯Ų‡", "view_all": "Ų…Ø´Ø§Ų‡Ø¯Ų‡ Ų‡Ų…Ų‡", "view_all_users": "Ų…Ø´Ø§Ų‡Ø¯Ų‡ Ų‡Ų…Ų‡ ÚŠØ§ØąØ¨ØąØ§Ų†", @@ -919,9 +731,7 @@ "waiting": "Ø¯Øą Ø§Ų†ØĒØ¸Ø§Øą", "week": "؇؁ØĒŲ‡", "welcome": "ØŽŲˆØ´ ØĸŲ…Ø¯ÛŒØ¯", - "welcome_to_immich": "", "year": "ØŗØ§Ų„", "yes": "Ø¨Ų„Ų‡", - "you_dont_have_any_shared_links": "", "zoom_image": "Ø¨Ø˛ØąÚ¯Ų†Ų…Ø§ÛŒÛŒ ØĒØĩŲˆÛŒØą" } diff --git a/i18n/fi.json b/i18n/fi.json index 8ca468fbab..5aaf614342 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -2,19 +2,19 @@ "about": "Tietoja", "account": "Tili", "account_settings": "Tilin asetukset", - "acknowledge": "Tiedostan", + "acknowledge": "Hyväksy", "action": "Toiminta", "action_common_update": "Päivitä", "actions": "Toimintoja", "active": "Aktiivinen", - "activity": "Aktiviteetti", - "activity_changed": "Aktiviteetti {enabled, select, true {otettu käyttÃļÃļn} other {poistettu käytÃļstä}}", + "activity": "Tapahtumat", + "activity_changed": "Toiminto {enabled, select, true {otettu käyttÃļÃļn} other {poistettu käytÃļstä}}", "add": "Lisää", "add_a_description": "Lisää kuvaus", "add_a_location": "Lisää sijainti", "add_a_name": "Lisää nimi", "add_a_title": "Lisää otsikko", - "add_endpoint": "Add endpoint", + "add_endpoint": "Lisää päätepiste", "add_exclusion_pattern": "Lisää poissulkemismalli", "add_import_path": "Lisää tuontipolku", "add_location": "Lisää sijainti", @@ -28,7 +28,7 @@ "add_to_album_bottom_sheet_already_exists": "Kohde on jo albumissa {album}", "add_to_shared_album": "Lisää jaettuun albumiin", "add_url": "Lisää URL", - "added_to_archive": "Arkistoitu", + "added_to_archive": "Lisätty arkistoon", "added_to_favorites": "Lisätty suosikkeihin", "added_to_favorites_count": "{count, number} lisätty suosikkeihin", "admin": { @@ -39,12 +39,13 @@ "authentication_settings_disable_all": "Haluatko varmasti poistaa kaikki kirjautumistavat käytÃļstä? Kirjautuminen on tämän jälkeen mahdotonta.", "authentication_settings_reenable": "Ottaaksesi uudestaan käyttÃļÃļn, käytä Palvelin Komentoa.", "background_task_job": "TaustatyÃļt", - "backup_database": "Varmuuskopioi Tietokanta", - "backup_database_enable_description": "Ota käyttÃļÃļn tietokannan varmuuskopiointi", - "backup_keep_last_amount": "Varmuuskopioiden lukumäärä", - "backup_settings": "Varmuuskopioinnin asetukset", - "backup_settings_description": "Hallitse tietokannan varmuuskopioiden asetuksia", + "backup_database": "Luo tietokantavedos", + "backup_database_enable_description": "Ota tietokantavedokset käyttÃļÃļn", + "backup_keep_last_amount": "Säilytettävien tietokantavedosten määrä", + "backup_settings": "Tietokantavedosten asetukset", + "backup_settings_description": "Hallitse tietokannan vedosasetuksia. Huomautus: Näitä tehtäviä ei valvota, eikä sinulle ilmoiteta epäonnistumisista.", "check_all": "Tarkista kaikki", + "cleanup": "Siivous", "cleared_jobs": "TyÃļn {job} tehtävät tyhjennetty", "config_set_by_file": "Asetukset on tällä hetkellä määritelty tiedostosta", "confirm_delete_library": "Haluatko varmasti poistaa kirjaston {library}?", @@ -52,6 +53,7 @@ "confirm_email_below": "Kirjota \"{email}\" vahvistaaksesi", "confirm_reprocess_all_faces": "Haluatko varmasti käsitellä uudelleen kaikki kasvot? Tämä poistaa myÃļs nimetyt henkilÃļt.", "confirm_user_password_reset": "Haluatko varmasti nollata käyttäjän {user} salasanan?", + "confirm_user_pin_code_reset": "Haluatko varmasti nollata käyttäjän {user} PIN-koodin?", "create_job": "Luo tehtävä", "cron_expression": "Cron-lauseke", "cron_expression_description": "Aseta skannausväli käyttämällä cron-formaattia. Lisätietoja linkistä. Crontab Guru", @@ -69,8 +71,13 @@ "forcing_refresh_library_files": "Pakotetaan virkistämään kaikkien kirjastojen tiedostot", "image_format": "Tiedostomuoto", "image_format_description": "WebP tuottaa pienempiä tiedostoja kuin JPEG, mutta on hitaampi pakata.", + "image_fullsize_description": "Täysikokoinen kuva ilman metatietoja, käytetään zoomattaessa", + "image_fullsize_enabled": "Ota käyttÃļÃļn täysikokoisen kuvan luonti", + "image_fullsize_enabled_description": "Luo täysikokoinen kuva ei-verkkoystävällisille formaateille. Jos \"Suosi upotettua esikatselua\" on käytÃļssä, upotettuja esikatseluja käytetään suoraan ilman muuntamista. Ei vaikuta verkkoyhteensopiviin formaatteihin, kuten JPEG:hen.", + "image_fullsize_quality_description": "Täysikokoisen kuvan laatualue 1–100. Suurempi arvo on parempi, mutta johtaa suurempiin tiedostoihin.", + "image_fullsize_title": "Täysikokoisen kuvan asetukset", "image_prefer_embedded_preview": "Suosi upotettua esikatselua", - "image_prefer_embedded_preview_setting_description": "Käytä RAW-kuvissa upotettuja esikatselukuvia aina kun mahdollista. Tämä voi joissain kuvissa tuottaa tarkemmat värit, mutta esikatselun laatu on riippuvainen kamerasta ja kuvassa voi olla enemmän pakkauksesta aiheutuvia häiriÃļitä.", + "image_prefer_embedded_preview_setting_description": "Käytä RAW-kuviin upotettuja esikatseluja kuvankäsittelyn syÃļtteenä ja aina kun mahdollista. Tämä voi tarjota tarkempia värejä joillekin kuville, mutta esikatselun laatu riippuu kamerasta ja kuvassa voi olla enemmän pakkausartefakteja.", "image_prefer_wide_gamut": "Suosi laajaa väriskaalaa", "image_prefer_wide_gamut_setting_description": "Käytä Display P3 -nimiavaruutta pikkukuville. Tämä säilÃļÃļ värien vivahteet paremmin, mutta kuvat saattavat näyttää erilaisilta vanhemmissa laitteissa. sRGB-kuvat pidetään muuttumattomina, jottei värit muuttuisi.", "image_preview_description": "Keskikokoinen kuva, josta metatiedot on poistettu, käytetään yksittäisen resurssin katseluun ja koneoppimiseen", @@ -100,11 +107,11 @@ "library_scanning_enable_description": "Ota käyttÃļÃļn ajoittaiset kirjastojen skannaukset", "library_settings": "Ulkoinen kirjasto", "library_settings_description": "Hallitse ulkoisen kirjaston asetuksia", - "library_tasks_description": "Suorita kirjastotoimintoja", + "library_tasks_description": "Skannaa ulkoisia kirjastoja uusien ja/tai muutoksien varalta", "library_watching_enable_description": "Tarkkaile tiedostojen muuttumisia ulkoisissa kirjastoissa", "library_watching_settings": "Kirjaston tarkkailu (KOKEELLINEN)", "library_watching_settings_description": "Tarkkaile muuttuvia tiedostoja automaattisesti", - "logging_enable_description": "Ota käyttÃļÃļn lokitus", + "logging_enable_description": "Ota lokikirjaus käyttÃļÃļn", "logging_level_description": "Kun käytÃļssä, mitä lokituksen tasoa käytetään.", "logging_settings": "Lokit", "machine_learning_clip_model": "CLIP-malli", @@ -135,7 +142,7 @@ "machine_learning_smart_search_description": "Etsi kuvia merkityksellisemmin käyttäen CLIP-upotuksia", "machine_learning_smart_search_enabled": "Ota käyttÃļÃļn älykäs haku", "machine_learning_smart_search_enabled_description": "Jos ei käytÃļssä, kuvia ei koodata älykkäälle etsinnälle.", - "machine_learning_url_description": "Koneoppimispalvelimen URL. MIkäli URL:liä on useampi kuin yksi, järjestelmä yrittää ottaa yhteyden jokaiseen erikseen järjestyksessä ensimmäisestä viimeiseen, kunnes pyyntÃļÃļn vastataan onnistuneesti.", + "machine_learning_url_description": "Koneoppimispalvelimen URL-osoite. Jos lisätään useampi kuin yksi URL-osoite, kutakin osoitetta kohden yritetään kerran, kunnes yksi niistä vastaa. Yritykset tehdään järjestyksessä ensimmäisestä viimeiseen. Palvelimet, jotka eivät vastaa, ohitetaan tilapäisesti, kunnes ne ovat taas tavoitettavissa.", "manage_concurrency": "Hallitse yhtäaikaisia toimintoja", "manage_log_settings": "Hallitse lokien asetuksia", "map_dark_style": "Tumma teema", @@ -151,13 +158,15 @@ "map_settings": "Kartta", "map_settings_description": "Hallitse kartan asetuksia", "map_style_description": "style.json-karttateeman URL", + "memory_cleanup_job": "Muistin siivous", + "memory_generate_job": "Muistin generointi", "metadata_extraction_job": "Kerää metadata", "metadata_extraction_job_description": "Poimi metatiedot aineistoista, kuten GPS, kasvot ja resoluutio", "metadata_faces_import_setting": "Ota käyttÃļÃļn kasvojen tuonti", "metadata_faces_import_setting_description": "Tuo kasvot kuvan EXIF- ja kylkiäistiedostoista", "metadata_settings": "Metatietoasetukset", "metadata_settings_description": "Hallitse metatietoja", - "migration_job": "Migrointi", + "migration_job": "Migraatio", "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", "no_paths_added": "Polkuja ei asetettu", "no_pattern_added": "Kaavoja ei lisättynä", @@ -184,26 +193,22 @@ "oauth_auto_register": "Automaattinen rekisterÃļinti", "oauth_auto_register_description": "RekisterÃļi uudet OAuth:lla kirjautuvat käyttäjät automaattisesti", "oauth_button_text": "Painikkeen teksti", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Asiakassalaisuusavain", + "oauth_client_secret_description": "Vaaditaan, jos OAuth-palveluntarjoaja ei tue PKCE:tä (Proof Key for Code Exchange)", "oauth_enable_description": "Kirjaudu käyttäen OAuthia", - "oauth_issuer_url": "Toimitsijan URL", "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override_description": "Ota käyttÃļÃļn kun OAuth tarjoaja ei salli mobiili URI:a, kuten '{callback}'", - "oauth_profile_signing_algorithm": "Profiilin allekirjoitusalgoritmi", - "oauth_profile_signing_algorithm_description": "Algoritmi, jota käytetään käyttäjäprofiilin allekirjoittamiseen.", - "oauth_scope": "Skooppi (Scope)", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", - "oauth_signing_algorithm": "Allekirjoitusalgoritmi", "oauth_storage_label_claim": "Tallennustilan nimikkeen valtuutusväittämä (claim)", "oauth_storage_label_claim_description": "Määriä käyttäjän tallennustilan nimike tämän väittämän arvoksi automaattisesti.", "oauth_storage_quota_claim": "Tallennustilan kiintiÃļn väittämä (claim)", "oauth_storage_quota_claim_description": "Aseta automaattisesti käyttäjien tallennustilan määrä tähän arvoon.", "oauth_storage_quota_default": "Tallennustilan oletuskiintiÃļ (Gt)", "oauth_storage_quota_default_description": "Käytettävä kiintiÃļn määrä gigatavuissa, käytetään kun väittämää ei ole annettu (0 rajoittamaton kiintiÃļ).", + "oauth_timeout": "PyynnÃļn aikakatkaisu", + "oauth_timeout_description": "PyyntÃļjen aikakatkaisu millisekunteina", "offline_paths": "Offline-tilan polut", "offline_paths_description": "Nämä tulokset voivat johtua tiedostoista, jotka on käsin poistettu, eivätkä ole ulkoisessa kirjastossa.", "password_enable_description": "Kirjaudu käyttäen sähkÃļpostiosoitetta ja salasanaa", @@ -243,7 +248,7 @@ "storage_template_hash_verification_enabled_description": "Ottaa käyttÃļÃļn tarkistussummien laskennan. Älä poista käytÃļstä, ellet ole aivan varma seurauksista", "storage_template_migration": "Tallennustilan mallien migraatio", "storage_template_migration_description": "Käytä nykyistä {template}a aikaisemmin lähetettyihin kohteisiin", - "storage_template_migration_info": "Malli vaikuttaa vain uusiin kohteisiin. Käyttääksesi mallia jo olemassa oleviin kohteisiin, aja {job}.", + "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyÃļ", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", "storage_template_onboarding_description": "Kun tämä ominaisuus on käytÃļssä, se järjestää tiedostot automaattisesti käyttäjän määrittämän mallin perusteella. Vakausongelmien vuoksi ominaisuus on oletuksena poistettu käytÃļstä. Lisätietoja on dokumentaatiossa.", @@ -268,7 +273,7 @@ "theme_settings": "Teeman asetukset", "theme_settings_description": "Kustomoi Immichin web-käyttÃļliittymää", "these_files_matched_by_checksum": "Näillä tiedostoilla on yhteinen tarkistussumma", - "thumbnail_generation_job": "Generoi pikkukuvat", + "thumbnail_generation_job": "Luo pikkukuvat", "thumbnail_generation_job_description": "Generoi isot, pienet sekä sumeat pikkukuvat jokaisesta aineistosta, kuten myÃļs henkilÃļistä", "transcoding_acceleration_api": "Kiihdytysrajapinta", "transcoding_acceleration_api_description": "Rajapinta, jolla keskustellaan laittesi kanssa nopeuttaaksemme koodausta. Tämä asetus on paras mahdollinen: Mikäli ongelmia ilmenee, palataan käyttämään ohjelmistopohjaista koodausta. VP9 voi toimia tai ei, riippuen laitteistosi kokoonpanosta.", @@ -319,7 +324,7 @@ "transcoding_settings_description": "Hallitse, mitkä videot transkoodataan ja miten niitä käsitellään", "transcoding_target_resolution": "Kohderesoluutio", "transcoding_target_resolution_description": "Korkeampi resoluutio on tarkempi, mutta kestää kauemmin enkoodata, vie enemmän tilaa ja voi hidastaa sovelluksen responsiivisuutta.", - "transcoding_temporal_aq": "Temporal AQ", + "transcoding_temporal_aq": "Väliaikainen AQ", "transcoding_temporal_aq_description": "Vaikuttaa vain NVENC:lle. Parantaa laatua kohtauksissa, joissa on paljon yksityiskohtia ja vähän liikettä. Ei välttämättä ole yhteensopiva vanhempien laitteiden kanssa.", "transcoding_threads": "Säikeet", "transcoding_threads_description": "Korkeampi arvo nopeuttaa enkoodausta, mutta vie tilaa palvelimen muilta tehtäviltä. Tämä arvo ei tulisi olla suurempi mitä suorittimen ytimien määrä. Suurin mahdollinen käyttÃļ, mikäli arvo on 0.", @@ -344,6 +349,7 @@ "user_delete_delay_settings_description": "Kuinka monta päivää poistamisen jälkeen käyttäjä ja hänen aineistonsa poistetaan pysyvästi. Joka keskiyÃļ käydään läpi poistettavaksi merkityt käyttäjät. Tämä muutos astuu voimaan seuraavalla ajokerralla.", "user_delete_immediately": "{user}:n tili ja sen kohteet on ajastettu poistettavaksi heti.", "user_delete_immediately_checkbox": "Aseta tili ja sen kohteet jonoon välitÃļntä poistoa varten", + "user_details": "Käyttäjätiedot", "user_management": "Käyttäjien hallinta", "user_password_has_been_reset": "Käyttäjän salasana on nollattu:", "user_password_reset_description": "Anna väliaikainen salasana ja ohjeista käyttäjää vaihtamaan se seuraavan kirjautumisen yhteydessä.", @@ -363,13 +369,17 @@ "admin_password": "Ylläpitäjän salasana", "administration": "Ylläpito", "advanced": "Edistyneet", - "advanced_settings_log_level_title": "Lokitaso: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Käytä tätä vaihtoehtoa suodattaaksesi mediaa synkronoinnin aikana vaihtoehtoisten kriteerien perusteella. Kokeile tätä vain, jos sovelluksessa on ongelmia kaikkien albumien tunnistamisessa.", + "advanced_settings_enable_alternate_media_filter_title": "[KOKEELLINEN] Käytä vaihtoehtoisen laitteen albumin synkronointisuodatinta", + "advanced_settings_log_level_title": "Kirjaustaso: {level}", "advanced_settings_prefer_remote_subtitle": "Jotkut laitteet ovat erittäin hitaita lataamaan esikatselukuvia laitteen kohteista. Aktivoi tämä asetus käyttääksesi etäkuvia.", "advanced_settings_prefer_remote_title": "Suosi etäkuvia", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_proxy_headers_subtitle": "Määritä välityspalvelimen otsikot(proxy headers), jotka Immichin tulisi lähettää jokaisen verkkopyynnÃļn mukana", + "advanced_settings_proxy_headers_title": "Välityspalvelimen otsikot", "advanced_settings_self_signed_ssl_subtitle": "Ohita SSL sertifikaattivarmennus palvelimen päätepisteellä. Vaaditaan self-signed -sertifikaateissa.", "advanced_settings_self_signed_ssl_title": "Salli self-signed SSL -sertifikaatit", + "advanced_settings_sync_remote_deletions_subtitle": "Poista tai palauta kohde automaattisesti tällä laitteella, kun kyseinen toiminto suoritetaan verkossa", + "advanced_settings_sync_remote_deletions_title": "Synkronoi etäpoistot [KOKEELLINEN]", "advanced_settings_tile_subtitle": "Edistyneen käyttäjän asetukset", "advanced_settings_troubleshooting_subtitle": "Ota vianetsinnän lisäominaisuudet käyttÃļÃļn", "advanced_settings_troubleshooting_title": "Vianetsintä", @@ -392,9 +402,9 @@ "album_remove_user_confirmation": "Oletko varma että haluat poistaa {user}?", "album_share_no_users": "Näyttää että olet jakanut tämän albumin kaikkien kanssa, tai sinulla ei ole käyttäjiä joille jakaa.", "album_thumbnail_card_item": "1 kohde", - "album_thumbnail_card_items": "{} kohdetta", - "album_thumbnail_card_shared": "Jaettu", - "album_thumbnail_shared_by": "Jakanut {}", + "album_thumbnail_card_items": "{count} kohdetta", + "album_thumbnail_card_shared": " ¡ Jaettu", + "album_thumbnail_shared_by": "Jakanut {user}", "album_updated": "Albumi päivitetty", "album_updated_setting_description": "Saa sähkÃļpostia kun jaetussa albumissa on uutta sisältÃļä", "album_user_left": "Poistuttiin albumista {album}", @@ -418,6 +428,7 @@ "allow_edits": "Salli muutokset", "allow_public_user_to_download": "Salli julkisten käyttäjien ladata tiedostoja", "allow_public_user_to_upload": "Salli julkisten käyttäjien lähettää tiedostoja", + "alt_text_qr_code": "QR-koodi", "anti_clockwise": "Vastapäivään", "api_key": "API-avain", "api_key_description": "Tämä arvo näytetään vain kerran. Varmista, että olet kopioinut sen ennen kuin suljet ikkunan.", @@ -431,10 +442,10 @@ "archive": "Arkisto", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_page_no_archived_assets": "Arkistoituja kohteita ei lÃļytynyt", - "archive_page_title": "Arkisto ({})", + "archive_page_title": "Arkisto ({count})", "archive_size": "Arkiston koko", "archive_size_description": "Määritä arkiston koko latauksissa (Gt)", - "archived": "Archived", + "archived": "Arkistoitu", "archived_count": "{count, plural, other {Arkistoitu #}}", "are_these_the_same_person": "Ovatko he sama henkilÃļ?", "are_you_sure_to_do_this": "Haluatko varmasti tehdä tämän?", @@ -456,63 +467,62 @@ "asset_list_settings_title": "Kuvaruudukko", "asset_offline": "Aineisto offline-tilassa", "asset_offline_description": "Tätä ulkoista resurssia ei enää lÃļydy levyltä. Ole hyvä ja ota yhteyttä Immich-järjestelmänvalvojaan saadaksesi apua.", - "asset_restored_successfully": "Asset restored successfully", + "asset_restored_successfully": "Kohde palautettu onnistuneesti", "asset_skipped": "Ohitettu", "asset_skipped_in_trash": "Roskakorissa", "asset_uploaded": "Lähetetty", - "asset_uploading": "Lähetetäänâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", + "asset_uploading": "Ladataanâ€Ļ", + "asset_viewer_settings_subtitle": "Galleriakatseluohjelman asetusten hallinta", "asset_viewer_settings_title": "Katselin", - "assets": "kohdetta", + "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_name_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}} {hasName, select, true {{name}} other {uuteen albumiin}}", "assets_count": "{count, plural, one {# media} other {# mediaa}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi", + "assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta", "assets_moved_to_trash_count": "Siirretty {count, plural, one {# media} other {# mediaa}} roskakoriin", "assets_permanently_deleted_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "assets_removed_count": "{count, plural, one {# media} other {# mediaa}} poistettu", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", - "assets_restore_confirmation": "Haluatko varmasti palauttaa kaikki roskakoriisi siirretyt resurssit? Tätä toimintoa ei voi peruuttaa! Huomaa, että offline-resursseja ei voida palauttaa tällä tavalla.", + "assets_removed_permanently_from_device": "{count} kohdetta on poistettu pysyvästi laitteeltasi", + "assets_restore_confirmation": "Haluatko varmasti palauttaa kaikki roskakoriisi siirretyt kohteet? Tätä toimintoa ei voi peruuttaa! Huomaa, että offline-kohteita ei voida palauttaa tällä tavalla.", "assets_restored_count": "{count, plural, one {# media} other {# mediaa}} palautettu", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{count} kohdetta palautettu onnistuneesti", + "assets_trashed": "{count} kohdetta siirretty roskakoriin", "assets_trashed_count": "{count, plural, one {# media} other {# mediaa}} siirretty roskakoriin", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{count} kohdetta siirretty roskakoriin Immich-palvelimelta", "assets_were_part_of_album_count": "{count, plural, one {Media oli} other {Mediat olivat}} jo albumissa", "authorized_devices": "Valtuutetut laitteet", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla", + "automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Laitteen albumit ({})", + "background_location_permission": "Taustasijainnin käyttÃļoikeus", + "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", "backup_album_selection_page_assets_scatter": "Kohteet voivat olla hajaantuneina useisiin albumeihin. Albumeita voidaan sisällyttää varmuuskopiointiin tai jättää siitä pois.", "backup_album_selection_page_select_albums": "Valitse albumit", "backup_album_selection_page_selection_info": "Valintatiedot", - "backup_album_selection_page_total_assets": "Uniikkeja kohteita yhteensä", + "backup_album_selection_page_total_assets": "Ainulaatuisia kohteita yhteensä", "backup_all": "Kaikki", - "backup_background_service_backup_failed_message": "Kohteiden varmuuskopiointi epäonnistui. Yritetään uudelleen...", - "backup_background_service_connection_failed_message": "Palvelimeen ei saatu yhteyttä. Yritetään uudelleen...", - "backup_background_service_current_upload_notification": "Lähetetään {}", - "backup_background_service_default_notification": "Tarkistetaan uusia kohteita...", + "backup_background_service_backup_failed_message": "Kohteiden varmuuskopiointi epäonnistui. Yritetään uudelleenâ€Ļ", + "backup_background_service_connection_failed_message": "Palvelimeen ei saatu yhteyttä. Yritetään uudelleenâ€Ļ", + "backup_background_service_current_upload_notification": "Lähetetään {filename}", + "backup_background_service_default_notification": "Tarkistetaan uusia kohteitaâ€Ļ", "backup_background_service_error_title": "Virhe varmuuskopioinnissa", - "backup_background_service_in_progress_notification": "Varmuuskopioidaan kohteita...", - "backup_background_service_upload_failure_notification": "Lähetys palvelimelle epäonnistui {}", + "backup_background_service_in_progress_notification": "Varmuuskopioidaan kohteitaâ€Ļ", + "backup_background_service_upload_failure_notification": "Lähetys epäonnistui {filename}", "backup_controller_page_albums": "Varmuuskopioi albumit", - "backup_controller_page_background_app_refresh_disabled_content": "Salli sovelluksen päivittäminen taustalla suorittaaksesi varmuuskopiointia taustalla: Asetukset > Yleiset > Appien päivitys taustalla", + "backup_controller_page_background_app_refresh_disabled_content": "Salli sovelluksen päivittäminen taustalla suorittaaksesi varmuuskopiointia taustalla: Asetukset > Yleiset > Appien päivitys taustalla.", "backup_controller_page_background_app_refresh_disabled_title": "Sovelluksen päivittäminen taustalla on pois päältä", "backup_controller_page_background_app_refresh_enable_button_text": "Siirry asetuksiin", "backup_controller_page_background_battery_info_link": "Näytä minulle miten", "backup_controller_page_background_battery_info_message": "Kytke pois päältä kaikki Immichin taustatyÃļskentelyyn liittyvät akun optimoinnit, jotta varmistat taustavarmuuskopioinnin parhaan mahdollisen toiminnan.\n\nKoska tämä on laitekohtaista, tarkista tarvittavat toimet laitevalmistajan ohjeista.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Akun optimointi", "backup_controller_page_background_charging": "Vain laitteen ollessa kytkettynä laturiin", "backup_controller_page_background_configure_error": "Taustapalvelun asettaminen epäonnistui", - "backup_controller_page_background_delay": "Viivästytä uusien kohteiden varmuuskopiointia: {}", + "backup_controller_page_background_delay": "Viivästytä uusien kohteiden varmuuskopiointia: {duration}", "backup_controller_page_background_description": "Kytke taustapalvelu päälle varmuuskopioidaksesi uudet kohteet automaattisesti, ilman sovelluksen avaamista", "backup_controller_page_background_is_off": "Automaattinen varmuuskopiointi taustalla on pois päältä", "backup_controller_page_background_is_on": "Automaattinen varmuuskopiointi taustalla on päällä", @@ -520,14 +530,13 @@ "backup_controller_page_background_turn_on": "Kytke taustapalvelu päälle", "backup_controller_page_background_wifi": "Vain WiFi-verkossa", "backup_controller_page_backup": "Varmuuskopiointi", - "backup_controller_page_backup_selected": "Valittu:", + "backup_controller_page_backup_selected": "Valittu: ", "backup_controller_page_backup_sub": "Varmuuskopioidut kuvat ja videot", - "backup_controller_page_created": "Luotu: {}", + "backup_controller_page_created": "Luotu: {date}", "backup_controller_page_desc_backup": "Kytke varmuuskopiointi päälle lähettääksesi uudet kohteet palvelimelle automaattisesti.", - "backup_controller_page_excluded": "Jätetty pois:", - "backup_controller_page_failed": "Epäonnistui ({})", - "backup_controller_page_filename": "Tiedoston nimi: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Poissuljettu: ", + "backup_controller_page_failed": "Epäonnistui ({count})", + "backup_controller_page_filename": "Tiedostonimi: {filename} [{size}]", "backup_controller_page_info": "Varmuuskopioinnin tiedot", "backup_controller_page_none_selected": "Ei mitään", "backup_controller_page_remainder": "Jäljellä", @@ -536,20 +545,20 @@ "backup_controller_page_start_backup": "Aloita varmuuskopiointi", "backup_controller_page_status_off": "Varmuuskopiointi on pois päältä", "backup_controller_page_status_on": "Varmuuskopiointi on päällä", - "backup_controller_page_storage_format": "{} / {} käytetty", + "backup_controller_page_storage_format": "{used} / {total} käytetty", "backup_controller_page_to_backup": "Varmuuskopioitavat albumit", "backup_controller_page_total_sub": "Kaikki uniikit kuvat ja videot valituista albumeista", "backup_controller_page_turn_off": "Varmuuskopiointi pois päältä", "backup_controller_page_turn_on": "Varmuuskopiointi päälle", "backup_controller_page_uploading_file_info": "Tiedostojen lähetystiedot", "backup_err_only_album": "Vähintään yhden albumin tulee olla valittuna", - "backup_info_card_assets": "kohdetta", + "backup_info_card_assets": "kohteet", "backup_manual_cancelled": "Peruutettu", - "backup_manual_in_progress": "Lähetys palvelimelle on jo käynnissä. Kokeile uudelleen hetken kuluttua.", + "backup_manual_in_progress": "Lähetys palvelimelle on jo käynnissä. Kokeile myÃļhemmin uudelleen", "backup_manual_success": "Onnistui", "backup_manual_title": "Lähetyksen tila", "backup_options_page_title": "Varmuuskopioinnin asetukset", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_setting_subtitle": "Hallinnoi aktiivisia ja taustalla olevia lähetysasetuksia", "backward": "Taaksepäin", "birthdate_saved": "Syntymäaika tallennettu", "birthdate_set_description": "Syntymäaikaa käytetään laskemaan henkilÃļn ikä kuvanottohetkellä.", @@ -561,21 +570,21 @@ "bulk_keep_duplicates_confirmation": "Haluatko varmasti säilyttää {count, plural, one {# kaksoiskappaleen} other {# kaksoiskappaleet}}? Tämä merkitsee kaikki kaksoiskappaleet ratkaistuiksi, eikä poista mitään.", "bulk_trash_duplicates_confirmation": "Haluatko varmasti siirtää {count, plural, one {# kaksoiskappaleen} other {# kaksoiskappaleet}} roskakoriin? Tämä säilyttää kustakin mediasta kookkaimman ja siirtää loput roskakoriin.", "buy": "Osta lisenssi Immich:iin", - "cache_settings_album_thumbnails": "Kirjastosivun esikatselukuvat ({} kohdetta)", + "cache_settings_album_thumbnails": "Kirjastosivun esikatselukuvat ({count} kohdetta)", "cache_settings_clear_cache_button": "Tyhjennä välimuisti", "cache_settings_clear_cache_button_title": "Tyhjennä sovelluksen välimuisti. Tämä vaikuttaa merkittävästi sovelluksen suorituskykyyn, kunnes välimuisti on rakennettu uudelleen.", "cache_settings_duplicated_assets_clear_button": "Tyhjennä", "cache_settings_duplicated_assets_subtitle": "Sovelluksen mustalle listalle merkitsemät valokuvat ja videot", - "cache_settings_duplicated_assets_title": "Kaksoiskappaleet ({})", - "cache_settings_image_cache_size": "Kuvien välimuistin koko ({} kohdetta)", + "cache_settings_duplicated_assets_title": "Kaksoiskappaleet ({count})", + "cache_settings_image_cache_size": "Kuvavälimuistin koko ({count} kohdetta)", "cache_settings_statistics_album": "Kirjaston esikatselukuvat", - "cache_settings_statistics_assets": "{} kohdetta ({})", + "cache_settings_statistics_assets": "{count} kohdetta ({size})", "cache_settings_statistics_full": "Täysikokoiset kuvat", "cache_settings_statistics_shared": "Jaettujen albumien esikatselukuvat", "cache_settings_statistics_thumbnail": "Esikatselukuvat", "cache_settings_statistics_title": "Välimuistin käyttÃļ", "cache_settings_subtitle": "Hallitse Immich-mobiilisovelluksen välimuistin käyttÃļä", - "cache_settings_thumbnail_size": "Esikatselukuvien välimuistin koko ({} kohdetta)", + "cache_settings_thumbnail_size": "Esikatselukuvien välimuistin koko ({count} kohdetta)", "cache_settings_tile_subtitle": "Hallitse paikallista tallenustilaa", "cache_settings_tile_title": "Paikallinen tallennustila", "cache_settings_title": "Välimuistin asetukset", @@ -584,12 +593,12 @@ "camera_model": "Kameran malli", "cancel": "Peruuta", "cancel_search": "Peru haku", - "canceled": "Canceled", + "canceled": "Peruutettu", "cannot_merge_people": "Ihmisiä ei voitu yhdistää", "cannot_undo_this_action": "Et voi perua tätä toimintoa!", "cannot_update_the_description": "Kuvausta ei voi päivittää", "change_date": "Vaihda päiväys", - "change_display_order": "Change display order", + "change_display_order": "Muuta näyttÃļjärjestystä", "change_expiration_time": "Muuta erääntymisaikaa", "change_location": "Vaihda sijainti", "change_name": "Vaihda nimi", @@ -597,16 +606,17 @@ "change_password": "Vaihda Salasana", "change_password_description": "Tämä on joko ensimmäinen kertasi kun kirjaudut järjestelmään, tai salasanasi on pyydetty vaihtamaan. Määritä uusi salasana alle.", "change_password_form_confirm_password": "Vahvista salasana", - "change_password_form_description": "Hei {name},\n\nTämä on joko ensimmäinen kirjautumisesi järjestelmään tai salasanan vaihtaminen vaihtaminen on pakotettu. Ole hyvä ja syÃļtä uusi salasana alle.", + "change_password_form_description": "Hei {name},\n\nTämä on joko ensimmäinen kerta, kun kirjaudut järjestelmään, tai sinulta on pyydetty salasanan vaihtoa. Ole hyvä ja syÃļtä uusi salasana alle.", "change_password_form_new_password": "Uusi salasana", "change_password_form_password_mismatch": "Salasanat eivät täsmää", "change_password_form_reenter_new_password": "Uusi salasana uudelleen", + "change_pin_code": "Vaihda PIN-koodi", "change_your_password": "Vaihda salasanasi", "changed_visibility_successfully": "Näkyvyys vaihdettu", "check_all": "Valitse kaikki", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "Vioittuneiden varmuuskopioiden tarkistaminen", + "check_corrupt_asset_backup_button": "Suorita tarkistus", + "check_corrupt_asset_backup_description": "Suorita tämä tarkistus vain Wi-Fi-yhteyden kautta ja vasta, kun kaikki kohteet on varmuuskopioitu. Toimenpide voi kestää muutamia minuutteja.", "check_logs": "Katso lokeja", "choose_matching_people_to_merge": "Valitse henkilÃļt joka yhdistetään", "city": "Kaupunki", @@ -615,14 +625,11 @@ "clear_all_recent_searches": "Tyhjennä viimeisimmät haut", "clear_message": "Tyhjennä viesti", "clear_value": "Tyhjää arvo", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "Asiakasvarmenne tuotu", + "client_cert_invalid_msg": "Virheellinen varmennetiedosto tai väärä salasana", + "client_cert_remove_msg": "Asiakassertifikaatti on poistettu", + "client_cert_subtitle": "Vain PKCS12 (.p12, .pfx) -muotoa tuetaan. Varmenteen tuonti/poisto on käytettävissä vain ennen sisäänkirjautumista", + "client_cert_title": "SSL-asiakassertifikaatti", "clockwise": "MyÃļtäpäivään", "close": "Sulje", "collapse": "Supista", @@ -635,22 +642,24 @@ "comments_are_disabled": "Kommentointi ei käytÃļssä", "common_create_new_album": "Luo uusi albumi", "common_server_error": "Tarkista internet-yhteytesi. Varmista että palvelin on saavutettavissa ja sovellus-/palvelinversiot ovat yhteensopivia.", - "completed": "Completed", + "completed": "Valmis", "confirm": "Vahvista", "confirm_admin_password": "Vahvista ylläpitäjän salasana", + "confirm_delete_face": "Haluatko poistaa {name} kasvot kohteesta?", "confirm_delete_shared_link": "Haluatko varmasti poistaa tämän jaetun linkin?", "confirm_keep_this_delete_others": "Kuvapinon muut kuvat tätä lukuunottamatta poistetaan. Oletko varma, että haluat jatkaa?", + "confirm_new_pin_code": "Vahvista uusi PIN-koodi", "confirm_password": "Vahvista salasana", "contain": "Mahduta", "context": "Konteksti", "continue": "Jatka", - "control_bottom_app_bar_album_info_shared": "{} kohdetta ¡ Jaettu", + "control_bottom_app_bar_album_info_shared": "{count} kohdetta ¡ Jaettu", "control_bottom_app_bar_create_new_album": "Luo uusi albumi", "control_bottom_app_bar_delete_from_immich": "Poista Immichistä", "control_bottom_app_bar_delete_from_local": "Poista laitteelta", "control_bottom_app_bar_edit_location": "Muokkaa sijaintia", "control_bottom_app_bar_edit_time": "Muokkaa aikaa", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Jaa linkki", "control_bottom_app_bar_share_to": "Jaa", "control_bottom_app_bar_trash_from_immich": "Siirrä roskakoriin", "copied_image_to_clipboard": "Kuva kopioitu leikepÃļydälle.", @@ -672,7 +681,6 @@ "create_link": "Luo linkki", "create_link_to_share": "Luo linkki jaettavaksi", "create_link_to_share_description": "Salli kaikkien linkin saaneiden nähdä valitut kuvat", - "create_new": "CREATE NEW", "create_new_person": "Luo uusi henkilÃļ", "create_new_person_hint": "Määritä valitut mediat uudelle henkilÃļlle", "create_new_user": "Luo uusi käyttäjä", @@ -682,19 +690,21 @@ "create_tag_description": "Luo uusi tunniste. Sisäkkäisiä tunnisteita varten syÃļtä tunnisteen täydellinen polku kauttaviivat mukaan luettuna.", "create_user": "Luo käyttäjä", "created": "Luotu", - "crop": "Crop", + "created_at": "Luotu", + "crop": "Rajaa", "curated_object_page_title": "Asiat", "current_device": "Nykyinen laite", - "current_server_address": "Current server address", + "current_pin_code": "Nykyinen PIN-koodi", + "current_server_address": "Nykyinen palvelinosoite", "custom_locale": "Muokatut maa-asetukset", "custom_locale_description": "Muotoile päivämäärät ja numerot perustuen alueen kieleen", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "E, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tumma", "date_after": "Päivämäärän jälkeen", "date_and_time": "Päivämäärä ja aika", "date_before": "Päivä ennen", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "E d. LLL y â€ĸ hh:mm", "date_of_birth_saved": "Syntymäaika tallennettu", "date_range": "Päivämäärän rajaus", "day": "Päivä", @@ -715,6 +725,7 @@ "delete_dialog_ok_force": "Poista kuitenkin", "delete_dialog_title": "Poista pysyvästi", "delete_duplicates_confirmation": "Haluatko varmasti poistaa nämä kaksoiskappaleet pysyvästi?", + "delete_face": "Poista kasvot", "delete_key": "Poista avain", "delete_library": "Poista kirjasto", "delete_link": "Poista linkki", @@ -727,15 +738,14 @@ "delete_tag_confirmation_prompt": "Haluatko varmasti poistaa tunnisteen {tagName}?", "delete_user": "Poista käyttäjä", "deleted_shared_link": "Jaettu linkki poistettu", - "deletes_missing_assets": "Poistaa levyltä puuttuvat resurssit", + "deletes_missing_assets": "Poistaa levyltä puuttuvat kohteet", "description": "Kuvaus", "description_input_hint_text": "Lisää kuvaus...", "description_input_submit_error": "Virhe kuvauksen päivittämisessä, tarkista lisätiedot lokista", - "details": "TIEDOT", + "details": "Tiedot", "direction": "Suunta", "disabled": "Poistettu käytÃļstä", "disallow_edits": "Älä salli muokkauksia", - "discord": "Discord", "discover": "Tutki", "dismiss_all_errors": "Sivuuta kaikki virheet", "dismiss_error": "Sivuuta virhe", @@ -747,26 +757,25 @@ "documentation": "Dokumentaatio", "done": "Valmis", "download": "Lataa", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", + "download_canceled": "Lataus peruutettu", + "download_complete": "Lataus valmis", + "download_enqueue": "Latausjonossa", + "download_failed": "Lataus epäonnistui", + "download_filename": "tiedosto: {filename}", + "download_finished": "Lataus valmis", "download_include_embedded_motion_videos": "Upotetut videot", "download_include_embedded_motion_videos_description": "Sisällytä liikekuviin upotetut videot erillisinä tiedostoina", - "download_notfound": "Download not found", - "download_paused": "Download paused", + "download_notfound": "Latausta ei lÃļytynyt", + "download_paused": "Lataus keskeytetty", "download_settings": "Lataukset", "download_settings_description": "Hallitse aineiston lataukseen liittyviä asetuksia", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", + "download_started": "Lataus aloitettu", + "download_sucess": "Lataus onnistui", + "download_sucess_android": "Media on ladattu DCIM/Immichiin", + "download_waiting_to_retry": "Odotetaan uudelleenyritystä", "downloading": "Ladataan", "downloading_asset_filename": "Ladataan mediaa {filename}", - "downloading_media": "Downloading media", + "downloading_media": "Median lataaminen", "drop_files_to_upload": "Pudota tiedostot mihin tahansa ladataksesi ne", "duplicates": "Kaksoiskappaleet", "duplicates_description": "Selvitä jokaisen kohdalla mitkä (jos yksikään) ovat kaksoiskappaleita", @@ -796,18 +805,20 @@ "editor_crop_tool_h2_aspect_ratios": "Kuvasuhteet", "editor_crop_tool_h2_rotation": "Rotaatio", "email": "SähkÃļposti", - "empty_folder": "This folder is empty", + "email_notifications": "SähkÃļposti-ilmoitukset", + "empty_folder": "Kansio on tyhjä", "empty_trash": "Tyhjennä roskakori", "empty_trash_confirmation": "Haluatko varmasti tyhjentää roskakorin? Tämä poistaa pysyvästi kaikki tiedostot Immich:stä.\nToimintoa ei voi perua!", "enable": "Ota käyttÃļÃļn", "enabled": "KäytÃļssä", "end_date": "Päättymispäivä", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Lisätty jonoon", + "enter_wifi_name": "Anna Wi-Fi-verkon nimi", "error": "Virhe", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "Albumin lajittelujärjestyksen muuttaminen epäonnistui", + "error_delete_face": "Virhe kasvojen poistamisessa kohteesta", "error_loading_image": "Kuvan lataus ei onnistunut", - "error_saving_image": "Error: {}", + "error_saving_image": "Virhe: {error}", "error_title": "Virhe - Jotain meni pieleen", "errors": { "cannot_navigate_next_asset": "Seuraavaan mediaan ei voi siirtyä", @@ -837,10 +848,12 @@ "failed_to_keep_this_delete_others": "Muiden kohteiden poisto epäonnistui", "failed_to_load_asset": "Kohteen lataus epäonnistui", "failed_to_load_assets": "Kohteiden lataus epäonnistui", + "failed_to_load_notifications": "Ilmoitusten lataaminen epäonnistui", "failed_to_load_people": "HenkilÃļiden lataus epäonnistui", "failed_to_remove_product_key": "Tuoteavaimen poistaminen epäonnistui", "failed_to_stack_assets": "Medioiden pinoaminen epäonnistui", "failed_to_unstack_assets": "Medioiden pinoamisen purku epäonnistui", + "failed_to_update_notification_status": "Ilmoituksen tilan päivittäminen epäonnistui", "import_path_already_exists": "Tämä tuontipolku on jo olemassa.", "incorrect_email_or_password": "Väärä sähkÃļpostiosoite tai salasana", "paths_validation_failed": "{paths, plural, one {# polun} other {# polun}} validointi epäonnistui", @@ -908,7 +921,8 @@ "unable_to_remove_reaction": "Reaktion poistaminen epäonnistui", "unable_to_repair_items": "Kohteiden korjaaminen epäonnistui", "unable_to_reset_password": "Salasanan nollaaminen epäonnistui", - "unable_to_resolve_duplicate": "Virheilmoitus näkyy, kun palvelin palauttaa virheen painettaessa roskakorin tai säilytä-painiketta.", + "unable_to_reset_pin_code": "PIN-koodin nollaaminen epäonnistui", + "unable_to_resolve_duplicate": "Kaksoiskappaleen ratkaiseminen epäonnistui", "unable_to_restore_assets": "Kohteen palauttaminen epäonnistui", "unable_to_restore_trash": "Kohteiden palauttaminen epäonnistui", "unable_to_restore_user": "Käyttäjän palauttaminen epäonnistui", @@ -935,16 +949,15 @@ "unable_to_update_user": "Käyttäjän muokkaus epäonnistui", "unable_to_upload_file": "Tiedostoa ei voitu ladata" }, - "exif": "Exif", "exif_bottom_sheet_description": "Lisää kuvausâ€Ļ", "exif_bottom_sheet_details": "TIEDOT", "exif_bottom_sheet_location": "SIJAINTI", "exif_bottom_sheet_people": "IHMISET", "exif_bottom_sheet_person_add_person": "Lisää nimi", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Ikä {age}", + "exif_bottom_sheet_person_age_months": "Ikä {months} kuukautta", + "exif_bottom_sheet_person_age_year_months": "Ikä 1 vuosi, {months} kuukautta", + "exif_bottom_sheet_person_age_years": "Ikä {years}", "exit_slideshow": "Poistu diaesityksestä", "expand_all": "Laajenna kaikki", "experimental_settings_new_asset_list_subtitle": "TyÃļn alla", @@ -961,12 +974,12 @@ "extension": "Tiedostopääte", "external": "Ulkoisesta", "external_libraries": "Ulkoiset kirjastot", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Ulkoinen verkko", + "external_network_sheet_info": "Kun laite ei ole yhteydessä valittuun Wi-Fi-verkkoon, sovellus yrittää muodostaa yhteyden palvelimeen alla olevista URL-osoitteista ylhäältä alas, kunnes yhteys muodostuu", "face_unassigned": "Ei määritelty", - "failed": "Failed", + "failed": "Epäonnistui", "failed_to_load_assets": "Kohteiden lataus epäonnistui", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", "favorites": "Suosikit", @@ -978,23 +991,23 @@ "file_name_or_extension": "Tiedostonimi tai tiedostopääte", "filename": "Tiedostonimi", "filetype": "Tiedostotyyppi", - "filter": "Filter", "filter_people": "Suodata henkilÃļt", + "filter_places": "Suodata paikkoja", "find_them_fast": "LÃļydä nopeasti hakemalla nimellä", "fix_incorrect_match": "Korjaa virheellinen osuma", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Kansio", + "folder_not_found": "Kansiota ei lÃļytynyt", "folders": "Kansiot", "folders_feature_description": "Käytetään kansionäkymää valokuvien ja videoiden selaamiseen järjestelmässä", "forward": "Eteenpäin", "general": "Yleinen", "get_help": "Hae apua", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "Wi-Fi-verkon nimen hakeminen epäonnistui. Varmista, että olet myÃļntänyt tarvittavat käyttÃļoikeudet ja että olet yhteydessä Wi-Fi-verkkoon", "getting_started": "Aloittaminen", "go_back": "Palaa", "go_to_folder": "Mene kansioon", "go_to_search": "Siirry hakuun", - "grant_permission": "Grant permission", + "grant_permission": "MyÃļnnä lupa", "group_albums_by": "Ryhmitä albumi...", "group_country": "Ryhmitä maan mukaan", "group_no": "Ei ryhmitystä", @@ -1004,12 +1017,12 @@ "haptic_feedback_switch": "Ota haptinen palaute käyttÃļÃļn", "haptic_feedback_title": "Haptinen palaute", "has_quota": "On kiintiÃļ", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Lisää otsikko", + "header_settings_field_validator_msg": "Arvo ei voi olla tyhjä", + "header_settings_header_name_input": "Otsikon nimi", + "header_settings_header_value_input": "Otsikon arvo", + "headers_settings_tile_subtitle": "Määritä välityspalvelimen otsikot, jotka sovelluksen tulisi lähettää jokaisen verkkopyynnÃļn mukana", + "headers_settings_tile_title": "Mukautettu proxy headers", "hi_user": "Hei {name} ({email})", "hide_all_people": "Piilota kaikki henkilÃļt", "hide_gallery": "Piilota galleria", @@ -1028,13 +1041,13 @@ "home_page_delete_remote_err_local": "Paikallisia kohteita etäkohdevalintojen joukossa, ohitetaan", "home_page_favorite_err_local": "Paikallisten kohteiden lisääminen suosikkeihin ei ole mahdollista, ohitetaan", "home_page_favorite_err_partner": "Kumppanin kohteita ei voi vielä merkitä suosikiksi. Hypätään yli", - "home_page_first_time_notice": "Jos käytät sovellusta ensimmäistä kertaa, muista valita varmuuskopioitavat albumi(t), jotta aikajanalla voi olla kuvia ja videoita.", + "home_page_first_time_notice": "Jos käytät sovellusta ensimmäistä kertaa, muista valita varmuuskopioitavat albumi(t), jotta aikajanalla voi olla kuvia ja videoita", "home_page_share_err_local": "Paikallisia kohteita ei voitu jakaa linkkien avulla. Hypätään yli", "home_page_upload_err_limit": "Voit lähettää palvelimelle enintään 30 kohdetta kerrallaan, ohitetaan", "host": "Isäntä", "hour": "Tunti", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", + "ignore_icloud_photos": "Ohita iCloud-kuvat", + "ignore_icloud_photos_description": "iCloudiin tallennettuja kuvia ei ladata Immich-palvelimelle", "image": "Kuva", "image_alt_text_date": "{isVideo, select, true {Video} other {Kuva}} otettu {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Kuva}} otettu {person1} kanssa {date}", @@ -1046,7 +1059,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Kuva}} otettu {city}ssä, {country}ssä {person1}n ja {person2}n kanssa {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Kuva}} otettu {city}ssä, {country}ssä {person1}n, {person2}n ja {person3}n kanssa {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Kuva}} otettu {city}ssä, {country}ssä {person1}n, {person2}n ja {additionalCount, number} muun kanssa {date}", - "image_saved_successfully": "Image saved", + "image_saved_successfully": "Kuva tallennettu", "image_viewer_page_state_provider_download_started": "Lataaminen aloitettu", "image_viewer_page_state_provider_download_success": "Lataus onnistui", "image_viewer_page_state_provider_share_error": "Jakovirhe", @@ -1068,8 +1081,8 @@ "night_at_midnight": "Joka yÃļ keskiyÃļllä", "night_at_twoam": "Joka yÃļ klo 02:00" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "Virheellinen päivämäärä", + "invalid_date_format": "Virheellinen päivämäärämuoto", "invite_people": "Kutsu ihmisiä", "invite_to_album": "Kutsu albumiin", "items_count": "{count, plural, one {# kpl} other {# kpl}}", @@ -1085,6 +1098,7 @@ "latest_version": "Viimeisin versio", "latitude": "Leveysaste", "leave": "Lähde", + "lens_model": "Objektiivin malli", "let_others_respond": "Anna muiden vastata", "level": "Taso", "library": "Kirjasto", @@ -1104,10 +1118,9 @@ "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", + "location_permission": "Sijainnin käyttÃļoikeus", + "location_permission_content": "Automaattisen vaihtotoiminnon käyttämiseksi Immich tarvitsee tarkan sijainnin käyttÃļoikeuden, jotta se voi lukea nykyisen Wi-Fi-verkon nimen", "location_picker_choose_on_map": "Valitse kartalta", "location_picker_latitude_error": "Lisää kelvollinen leveysaste", "location_picker_latitude_hint": "SyÃļtä leveysaste", @@ -1131,7 +1144,7 @@ "login_form_err_trailing_whitespace": "Lopussa välilyÃļnti", "login_form_failed_get_oauth_server_config": "Virhe kirjauduttaessa OAuth:lla, tarkista palvelimen URL", "login_form_failed_get_oauth_server_disable": "OAuth-ominaisuus ei ole käytÃļssä tällä palvelimella", - "login_form_failed_login": "Virhe kirjautumisessa. Tarkista palvelimen URL, sähkÃļpostiosoite ja salasana.", + "login_form_failed_login": "Virhe kirjautumisessa. Tarkista palvelimen URL, sähkÃļpostiosoite ja salasana", "login_form_handshake_exception": "Tapahtui poikkeus kättelyssä palvelimen kanssa. Kytke päälle self-signed -sertifikaattituki asetuksista, mikäli käytät self-signed -sertifikaatteja.", "login_form_password_hint": "salasana", "login_form_save_login": "Pysy kirjautuneena", @@ -1145,8 +1158,9 @@ "longitude": "Pituusaste", "look": "Tyyli", "loop_videos": "Toista videot uudelleen", - "loop_videos_description": "Ota käyttÃļÃļn videon automaattinen toisto tarkemmassa näkymässä.", + "loop_videos_description": "Ota käyttÃļÃļn jatkuva videotoisto tarkemmassa näkymässä.", "main_branch_warning": "Käytät kehitysversiota; suosittelemme vahvasti käyttämään julkaisuversiota!", + "main_menu": "Päävalikko", "make": "Valmistaja", "manage_shared_links": "Hallitse jaettuja linkkejä", "manage_sharing_with_partners": "Hallitse jakamista kumppaneille", @@ -1156,8 +1170,8 @@ "manage_your_devices": "Hallitse sisäänkirjautuneita laitteitasi", "manage_your_oauth_connection": "Hallitse OAuth-yhteyttäsi", "map": "Kartta", - "map_assets_in_bound": "{} kuva", - "map_assets_in_bounds": "{} kuvaa", + "map_assets_in_bound": "{count} kuva", + "map_assets_in_bounds": "{count} kuvaa", "map_cannot_get_user_location": "Käyttäjän sijaintia ei voitu määrittää", "map_location_dialog_yes": "Kyllä", "map_location_picker_page_use_location": "Käytä tätä sijaintia", @@ -1171,15 +1185,18 @@ "map_settings": "Kartta-asetukset", "map_settings_dark_mode": "Tumma tila", "map_settings_date_range_option_day": "Viimeiset 24 tuntia", - "map_settings_date_range_option_days": "Viimeiset {} päivää", + "map_settings_date_range_option_days": "Viimeiset {days} päivää", "map_settings_date_range_option_year": "Viimeisin vuosi", - "map_settings_date_range_option_years": "Viimeiset {} vuotta", + "map_settings_date_range_option_years": "Viimeiset {years} vuotta", "map_settings_dialog_title": "Kartta-asetukset", "map_settings_include_show_archived": "Sisällytä arkistoidut", "map_settings_include_show_partners": "Sisällytä kumppanit", "map_settings_only_show_favorites": "Näytä vain suosikit", "map_settings_theme_settings": "Kartan teema", "map_zoom_to_see_photos": "Tarkenna nähdäksesi kuvat", + "mark_all_as_read": "Merkitse kaikki luetuiksi", + "mark_as_read": "Merkitse luetuksi", + "marked_all_as_read": "Merkitty kaikki luetuiksi", "matches": "Osumia", "media_type": "Median tyyppi", "memories": "Muistoja", @@ -1188,8 +1205,6 @@ "memories_setting_description": "Hallitse mitä näet muistoissasi", "memories_start_over": "Aloita alusta", "memories_swipe_to_close": "Pyyhkäise ylÃļs sulkeaksesi", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Muisto", "memory_lane_title": "Muistojen polku {title}", "menu": "Valikko", @@ -1204,21 +1219,24 @@ "missing": "Puuttuu", "model": "Malli", "month": "Kuukauden mukaan", - "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", + "moved_to_archive": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} arkistoon", + "moved_to_library": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} kirjastoon", "moved_to_trash": "Siirretty roskakoriin", "multiselect_grid_edit_date_time_err_read_only": "Vain luku -tilassa olevien kohteiden päivämäärää ei voitu muokata, ohitetaan", "multiselect_grid_edit_gps_err_read_only": "Vain luku-tilassa olevien kohteiden sijantitietoja ei voitu muokata, ohitetaan", - "my_albums": "Albumini", + "mute_memories": "Mykistä muistot", + "my_albums": "Omat albumit", "name": "Nimi", "name_or_nickname": "Nimi tai lempinimi", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "Verkko", + "networking_subtitle": "Hallitse palvelinasetuksia", "never": "ei koskaan", "new_album": "Uusi Albumi", "new_api_key": "Uusi API-avain", "new_password": "Uusi salasana", "new_person": "Uusi henkilÃļ", + "new_pin_code": "Uusi PIN-koodi", "new_user_created": "Uusi käyttäjä lisätty", "new_version_available": "UUSI VERSIO SAATAVILLA", "newest_first": "Uusin ensin", @@ -1237,12 +1255,14 @@ "no_favorites_message": "Lisää suosikkeja lÃļytääksesi nopeasti parhaat kuvasi ja videosi", "no_libraries_message": "Luo ulkoinen kirjasto nähdäksesi valokuvasi ja videot", "no_name": "Ei nimeä", + "no_notifications": "Ei ilmoituksia", + "no_people_found": "Ei vastaavia henkilÃļitä", "no_places": "Ei paikkoja", "no_results": "Ei tuloksia", "no_results_description": "Kokeile synonyymiä tai yleisempää avainsanaa", "no_shared_albums_message": "Luo albumi, jotta voit jakaa kuvia ja videoita toisille", "not_in_any_album": "Ei yhdessäkään albumissa", - "not_selected": "Not selected", + "not_selected": "Ei valittu", "note_apply_storage_label_to_previously_uploaded assets": "Huom: Jotta voit soveltaa tallennustunnistetta aiemmin ladattuihin kohteisiin, suorita", "notes": "Muistiinpanot", "notification_permission_dialog_content": "Ottaaksesi ilmoitukset käyttÃļÃļn, siirry asetuksiin ja valitse 'salli'.", @@ -1252,21 +1272,18 @@ "notification_toggle_setting_description": "Ota sähkÃļposti-ilmoitukset käyttÃļÃļn", "notifications": "Ilmoitukset", "notifications_setting_description": "Hallitse ilmoituksia", - "oauth": "OAuth", "official_immich_resources": "Viralliset Immich-resurssit", - "offline": "Offline", "offline_paths": "Offline-polut", "offline_paths_description": "Nämä tulokset voivat johtua tiedostojen manuaalisesta poistamisesta, jotka eivät ole osa ulkoista kirjastoa.", - "ok": "Ok", "oldest_first": "Vanhin ensin", - "on_this_device": "On this device", + "on_this_device": "Laitteella", "onboarding": "KäyttÃļÃļnotto", "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin, ja ne voidaan poistaa käytÃļstä milloin tahansa hallinta asetuksista.", "onboarding_theme_description": "Valitse väriteema istunnollesi. Voit muuttaa tämän myÃļhemmin asetuksistasi.", "onboarding_welcome_description": "Aloitetaa laittamalla istuntoosi joitakin yleisiä asetuksia.", "onboarding_welcome_user": "Tervetuloa {user}", - "online": "Online", "only_favorites": "Vain suosikit", + "open": "Avaa", "open_in_map_view": "Avaa karttanäkymässä", "open_in_openstreetmap": "Avaa OpenStreetMapissa", "open_the_search_filters": "Avaa hakusuodattimet", @@ -1290,7 +1307,7 @@ "partner_page_partner_add_failed": "Kumppanin lisääminen epäonnistui", "partner_page_select_partner": "Valitse kumppani", "partner_page_shared_to_title": "Jaettu henkilÃļille", - "partner_page_stop_sharing_content": "{} ei voi enää käyttää kuviasi.", + "partner_page_stop_sharing_content": "{partner} ei voi enää käyttää kuviasi.", "partner_sharing": "Kumppanijako", "partners": "Kumppanit", "password": "Salasana", @@ -1316,7 +1333,7 @@ "permanent_deletion_warning_setting_description": "Näytä varoitus, kun poistat kohteita pysyvästi", "permanently_delete": "Poista pysyvästi", "permanently_delete_assets_count": "Poista pysyvästi {count, plural, one {kohde} other {kohteita}}", - "permanently_delete_assets_prompt": "Oletko varma, että haluat poistaa pysyvästi {count, plural, one {tämän kohteen?} other {nämä # kohteet?}} Tämä poistaa myÃļs {count, plural, one {sen sen} other {ne niiden}} albumista.", + "permanently_delete_assets_prompt": "Haluatko varmasti poistaa pysyvästi {count, plural, one {tämän kohteen?} other {nämä # kohteet?}} Tämä poistaa myÃļs {count, plural, one {sen} other {ne}} kaikista albumeista.", "permanently_deleted_asset": "Media poistettu pysyvästi", "permanently_deleted_assets_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "permission_onboarding_back": "Takaisin", @@ -1328,6 +1345,7 @@ "permission_onboarding_permission_limited": "Rajoitettu käyttÃļoikeus. Salliaksesi Immichin varmuuskopioida ja hallita koko kuvakirjastoasi, myÃļnnä oikeus kuviin ja videoihin asetuksista.", "permission_onboarding_request": "Immich vaatii käyttÃļoikeuden kuvien ja videoiden käyttämiseen.", "person": "HenkilÃļ", + "person_birthdate": "Syntynyt {date}", "person_hidden": "{name}{hidden, select, true { (piilotettu)} other {}}", "photo_shared_all_users": "Näyttää että olet jakanut kuvasi kaikkien käyttäjien kanssa, tai sinulla ei ole käyttäjää kenelle jakaa.", "photos": "Kuvat", @@ -1335,14 +1353,18 @@ "photos_count": "{count, plural, one {{count, number} Kuva} other {{count, number} kuvaa}}", "photos_from_previous_years": "Kuvia edellisiltä vuosilta", "pick_a_location": "Valitse sijainti", + "pin_code_changed_successfully": "PIN-koodin vaihto onnistui", + "pin_code_reset_successfully": "PIN-koodin nollaus onnistui", + "pin_code_setup_successfully": "PIN-koodin asettaminen onnistui", "place": "Sijainti", "places": "Paikat", + "places_count": "{count, plural, one {{count, number} Paikka} other {{count, number} Paikkaa}}", "play": "Toista", "play_memories": "Toista muistot", "play_motion_photo": "Toista Liikekuva", "play_or_pause_video": "Toista tai keskeytä video", "port": "Portti", - "preferences_settings_subtitle": "Manage the app's preferences", + "preferences_settings_subtitle": "Hallitse sovelluksen asetuksia", "preferences_settings_title": "Asetukset", "preset": "Asetus", "preview": "Esikatselu", @@ -1351,11 +1373,11 @@ "previous_or_next_photo": "Edellinen tai seuraava kuva", "primary": "Ensisijainen", "privacy": "Yksityisyys", + "profile": "Profiili", "profile_drawer_app_logs": "Lokit", "profile_drawer_client_out_of_date_major": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", "profile_drawer_client_out_of_date_minor": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään versioon.", "profile_drawer_client_server_up_to_date": "Asiakassovellus ja palvelin ovat ajan tasalla", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Palvelimen ohjelmistoversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", "profile_drawer_server_out_of_date_minor": "Palvelimen ohjelmistoversio on vanhentunut. Päivitä viimeisimpään versioon.", "profile_image_of_user": "Käyttäjän {user} profiilikuva", @@ -1364,7 +1386,7 @@ "public_share": "Julkinen jako", "purchase_account_info": "Tukija", "purchase_activated_subtitle": "Kiitos Immichin ja avoimen lähdekoodin ohjelmiston tukemisesta", - "purchase_activated_time": "Aktivoitu {date, date}", + "purchase_activated_time": "Aktivoitu {date}", "purchase_activated_title": "Avaimesi on aktivoitu onnistuneesti", "purchase_button_activate": "Aktivoi", "purchase_button_buy": "Osta", @@ -1407,8 +1429,10 @@ "recent": "Viimeisin", "recent-albums": "Viimeisimmät albumit", "recent_searches": "Edelliset haut", - "recently_added": "Recently added", + "recently_added": "Viimeksi lisätty", "recently_added_page_title": "Viimeksi lisätyt", + "recently_taken": "Viimeksi otettu", + "recently_taken_page_title": "Viimeksi Otettu", "refresh": "Päivitä", "refresh_encoded_videos": "Päivitä enkoodatut videot", "refresh_faces": "Päivitä kasvot", @@ -1429,12 +1453,16 @@ "remove_from_album": "Poista albumista", "remove_from_favorites": "Poista suosikeista", "remove_from_shared_link": "Poista jakolinkistä", + "remove_memory": "Tyhjennä muisti", + "remove_photo_from_memory": "Poista kuva muistista", "remove_url": "Poista URL", "remove_user": "Poista käyttäjä", "removed_api_key": "API-avain {name} poistettu", "removed_from_archive": "Poistettu arkistosta", "removed_from_favorites": "Poistettu suosikeista", "removed_from_favorites_count": "{count, plural, other {Poistettu #}} suosikeista", + "removed_memory": "Poistettu muistista", + "removed_photo_from_memory": "Kuva poistettu muistista", "removed_tagged_assets": "Poistettu tunniste {count, plural, one {# kohteesta} other {# kohteesta}}", "rename": "Nimeä uudelleen", "repair": "Korjaa", @@ -1443,9 +1471,11 @@ "repository": "Tietovarasto", "require_password": "Vaadi salasana", "require_user_to_change_password_on_first_login": "Vaadi käyttäjää vaihtamaan salasana seuraavalla kirjautumiskerralla", + "rescan": "Skannaa uudelleen", "reset": "Nollaa", "reset_password": "Nollaa salasana", "reset_people_visibility": "Nollaa henkilÃļiden näkyvyysasetukset", + "reset_pin_code": "Nollaa PIN-koodi", "reset_to_default": "Palauta oletusasetukset", "resolve_duplicates": "Ratkaise kaksoiskappaleet", "resolved_all_duplicates": "Kaikki kaksoiskappaleet selvitetty", @@ -1460,7 +1490,7 @@ "role_editor": "Editori", "role_viewer": "Toistin", "save": "Tallenna", - "save_to_gallery": "Save to gallery", + "save_to_gallery": "Tallenna galleriaan", "saved_api_key": "API-avain tallennettu", "saved_profile": "Profiili tallennettu", "saved_settings": "Asetukset tallennettu", @@ -1474,6 +1504,7 @@ "search_albums": "Etsi albumeita", "search_by_context": "Etsi kontekstin perusteella", "search_by_description": "Etsi kuvauksen perusteella", + "search_by_description_example": "Vaelluspäivä Sapassa", "search_by_filename": "Hae tiedostonimen tai -päätteen mukaan", "search_by_filename_example": "esim. IMG_1234.JPG tai PNG", "search_camera_make": "Etsi kameramerkkiä...", @@ -1481,30 +1512,28 @@ "search_city": "Etsi kaupunkia...", "search_country": "Etsi maata...", "search_filter_apply": "Käytä", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", + "search_filter_camera_title": "Valitse kameratyyppi", + "search_filter_date_title": "Valitse aikaväli", "search_filter_display_option_not_in_album": "Ei kuulu albumiin", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", + "search_filter_display_options": "NäyttÃļasetukset", + "search_filter_filename": "Etsi tiedostonimellä", + "search_filter_location": "Sijainti", + "search_filter_location_title": "Valitse sijainti", + "search_filter_media_type_title": "Valitse mediatyyppi", + "search_filter_people_title": "Valitse ihmiset", + "search_for": "Hae", "search_for_existing_person": "Etsi olemassa olevaa henkilÃļä", - "search_no_more_result": "No more results", + "search_no_more_result": "Ei enää tuloksia", "search_no_people": "Ei henkilÃļitä", "search_no_people_named": "Ei \"{name}\" nimisiä henkilÃļitä", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "Ei tuloksia, kokeile toista hakusanaa tai -yhdistelmää", "search_options": "Hakuvaihtoehdot", "search_page_categories": "Kategoriat", "search_page_motion_photos": "Liikekuvat", "search_page_no_objects": "Objektitietoja ei ole saatavilla", "search_page_no_places": "Paikkatietoja ei ole saatavilla", "search_page_screenshots": "NäyttÃļkuvat", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "Hae kuvia tai videoita", "search_page_selfies": "Selfiet", "search_page_things": "Asiat", "search_page_view_all_button": "Näytä kaikki", @@ -1512,10 +1541,11 @@ "search_page_your_map": "Sinun karttasi", "search_people": "Etsi ihmisiä", "search_places": "Etsi paikkoja", + "search_rating": "Hae luokituksen mukaan...", "search_result_page_new_search_hint": "Uusi haku", "search_settings": "Hakuasetukset", - "search_state": "Etsi tilaa...", - "search_suggestion_list_smart_search_hint_1": "Älykäs haku on oletuksena käytÃļssä. Käytä metatietojen etsimiseen syntaksia", + "search_state": "Etsi maakuntaa...", + "search_suggestion_list_smart_search_hint_1": "Älykäs haku on oletuksena käytÃļssä. Käytä metatietojen etsimiseen syntaksia ", "search_suggestion_list_smart_search_hint_2": "m:hakusana", "search_tags": "Etsi tunnisteita...", "search_timezone": "Etsi aikavyÃļhyke...", @@ -1524,6 +1554,7 @@ "searching_locales": "Etsitään lokaaleja...", "second": "Toinen", "see_all_people": "Näytä kaikki henkilÃļt", + "select": "Valitse", "select_album_cover": "Valitse albmin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", @@ -1534,6 +1565,7 @@ "select_keep_all": "Valitse pidä kaikki", "select_library_owner": "Valitse kirjaston omistaja", "select_new_face": "Valitse uudet kasvot", + "select_person_to_tag": "Valitse henkilÃļ, jonka haluat merkitä", "select_photos": "Valitse kuvat", "select_trash_all": "Valitse kaikki roskakoriin", "select_user_for_sharing_page_err_album": "Albumin luonti epäonnistui", @@ -1541,7 +1573,7 @@ "selected_count": "{count, plural, other {# valittu}}", "send_message": "Lähetä viesti", "send_welcome_email": "Lähetä tervetuloviesti", - "server_endpoint": "Server Endpoint", + "server_endpoint": "Palvelinosoite", "server_info_box_app_version": "Sovelluksen versio", "server_info_box_server_url": "Palvelimen URL-osoite", "server_offline": "Palvelin Offline-tilassa", @@ -1555,35 +1587,36 @@ "set_date_of_birth": "Aseta syntymäaika", "set_profile_picture": "Aseta profiilikuva", "set_slideshow_to_fullscreen": "Näytä diaesitys koko ruudulla", - "setting_image_viewer_help": "Sovellus lataa ensin pienen esikatselukuvan, toisena keskitarkkuuksisen kuvan (jos käytÃļssä) ja kolmantena alkuperäisen täysitarkkuuksisen kuvan (jos käytÃļssä)", + "setting_image_viewer_help": "Kuvaa katseltaessa ensin ladataan pikkukuva, sitten keskilaatuinen pikkukuva (jos käytÃļssä) ja lopuksi alkuperäinen (jos käytÃļssä).", "setting_image_viewer_original_subtitle": "Ota käyttÃļÃļn ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytÃļstä vähentääksesi datan käyttÃļä (sekä verkossa että laitteen välimuistissa).", "setting_image_viewer_original_title": "Lataa alkuperäinen kuva", "setting_image_viewer_preview_subtitle": "Ota käyttÃļÃļn ladataksesi keskitarkkuuksinen kuva. Poista käytÃļstä ladataksesi alkuperäinen kuva tai käyttääksesi vain esikatselukuvaa.", "setting_image_viewer_preview_title": "Lataa esikatselukuva", "setting_image_viewer_title": "Kuvat", "setting_languages_apply": "Käytä", - "setting_languages_subtitle": "Change the app's language", + "setting_languages_subtitle": "Vaihda sovelluksen kieli", "setting_languages_title": "Kieli", - "setting_notifications_notify_failures_grace_period": "Ilmoita taustavarmuuskopioinnin epäonnistumisista: {}", - "setting_notifications_notify_hours": "{} tunnin välein", + "setting_notifications_notify_failures_grace_period": "Ilmoita taustalla tapahtuvista varmuuskopiointivirheistä: {duration}", + "setting_notifications_notify_hours": "{count} tuntia", "setting_notifications_notify_immediately": "heti", - "setting_notifications_notify_minutes": "{} minuutin välein", + "setting_notifications_notify_minutes": "{count} minuuttia", "setting_notifications_notify_never": "ei koskaan", - "setting_notifications_notify_seconds": "{} sekuntia", + "setting_notifications_notify_seconds": "{count} sekuntia", "setting_notifications_single_progress_subtitle": "Yksityiskohtainen tieto palvelimelle lähettämisen edistymisestä kohteittain", - "setting_notifications_single_progress_title": "Näytä taustavarmuuskopioinnin eidstminen", + "setting_notifications_single_progress_title": "Näytä taustavarmuuskopioinnin edistyminen", "setting_notifications_subtitle": "Ilmoitusasetusten määrittely", "setting_notifications_total_progress_subtitle": "Lähetyksen yleinen edistyminen (kohteita lähetetty/yhteensä)", "setting_notifications_total_progress_title": "Näytä taustavarmuuskopioinnin kokonaisedistyminen", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_looping_title": "Silmukkatoisto", + "setting_video_viewer_original_video_subtitle": "Kun toistat videota palvelimelta, toista alkuperäinen, vaikka transkoodattu versio olisi saatavilla. Tämä voi johtaa puskurointiin. Paikalliset videot toistetaan aina alkuperäislaadulla.", + "setting_video_viewer_original_video_title": "Pakota alkuperäinen video", "settings": "Asetukset", "settings_require_restart": "Käynnistä Immich uudelleen ottaaksesti tämän asetuksen käyttÃļÃļn", "settings_saved": "Asetukset tallennettu", + "setup_pin_code": "Määritä PIN-koodi", "share": "Jaa", "share_add_photos": "Lisää kuvia", - "share_assets_selected": "{} valittu", + "share_assets_selected": "{count} valittu", "share_dialog_preparing": "Valmistellaan...", "shared": "Jaettu", "shared_album_activities_input_disable": "Kommentointi on kytketty pois päältä", @@ -1597,39 +1630,39 @@ "shared_by_user": "Käyttäjän {user} jakama", "shared_by_you": "Sinun jakamasi", "shared_from_partner": "Kumppanin {partner} kuvia", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Lähetetty", "shared_link_app_bar_title": "Jaetut linkit", "shared_link_clipboard_copied_massage": "Kopioitu leikepÃļydältä", - "shared_link_clipboard_text": "Linkki: {}\nSalasana: {}", + "shared_link_clipboard_text": "Linkki: {link}\nSalasana: {password}", "shared_link_create_error": "Jaetun linkin luomisessa tapahtui virhe", "shared_link_edit_description_hint": "Lisää jaon kuvaus", "shared_link_edit_expire_after_option_day": "1 päivä", - "shared_link_edit_expire_after_option_days": "{} päivää", + "shared_link_edit_expire_after_option_days": "{count} päivää", "shared_link_edit_expire_after_option_hour": "1 tunti", - "shared_link_edit_expire_after_option_hours": "{} tuntia", + "shared_link_edit_expire_after_option_hours": "{count} tuntia", "shared_link_edit_expire_after_option_minute": "1 minuutti", - "shared_link_edit_expire_after_option_minutes": "{} minuuttia", - "shared_link_edit_expire_after_option_months": "{} kuukautta", - "shared_link_edit_expire_after_option_year": "{} vuosi", + "shared_link_edit_expire_after_option_minutes": "{count} minuuttia", + "shared_link_edit_expire_after_option_months": "{count} kuukautta", + "shared_link_edit_expire_after_option_year": "{count} vuosi", "shared_link_edit_password_hint": "SyÃļtä jaon salasana", "shared_link_edit_submit_button": "Päivitä linkki", "shared_link_error_server_url_fetch": "Palvelimen URL-osoitetta ei voitu hakea", - "shared_link_expires_day": "Voimassaolo päättyy {} päivän kuluttua", - "shared_link_expires_days": "Voimassaolo päättyy {} päivän kuluttua", - "shared_link_expires_hour": "Voimassaolo päättyy {} tunnin kuluttua", - "shared_link_expires_hours": "Voimassaolo päättyy {} tunnin kuluttua", - "shared_link_expires_minute": "Voimassaolo päättyy {} minuutin kuluttua", - "shared_link_expires_minutes": "Voimassaolo päättyy {} minuutin kuluttua", + "shared_link_expires_day": "Vanhenee {count} päivässä", + "shared_link_expires_days": "Vanhenee {count} päivässä", + "shared_link_expires_hour": "Vanhenee {count} tunnissa", + "shared_link_expires_hours": "Vanhenee {count} tunnissa", + "shared_link_expires_minute": "Vanhenee {count} minuutissa", + "shared_link_expires_minutes": "Vanhenee {count} minuutissa", "shared_link_expires_never": "Voimassaolo päättyy ∞", - "shared_link_expires_second": "Voimassaolo päättyy {} sekunnin kuluttua", - "shared_link_expires_seconds": "Voimassaolo päättyy {} sekunnin kuluttua", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", + "shared_link_expires_second": "Vanhenee {count} sekunnissa", + "shared_link_expires_seconds": "Vanhenee {count} sekunnissa", + "shared_link_individual_shared": "YksilÃļllisesti jaettu", "shared_link_manage_links": "Hallitse jaettuja linkkejä", "shared_link_options": "Jaetun linkin vaihtoehdot", "shared_links": "Jaetut linkit", + "shared_links_description": "Jaa kuvia ja videoita linkin avulla", "shared_photos_and_videos_count": "{assetCount, plural, other {# jaettua kuvaa ja videota.}}", - "shared_with_me": "Shared with me", + "shared_with_me": "Jaettu minulle", "shared_with_partner": "Jaa kumppanin {partner} kanssa", "sharing": "Jakaminen", "sharing_enter_password": "Nähdäksesi sivun sinun tulee antaa salasana.", @@ -1656,6 +1689,7 @@ "show_person_options": "Näytä henkilÃļasetukset", "show_progress_bar": "Näytä eteneminen", "show_search_options": "Näytä hakuvaihtoehdot", + "show_shared_links": "Näytä jaetut linkit", "show_slideshow_transition": "Näytä diaesitys siirtymä", "show_supporter_badge": "Kannattajan merkki", "show_supporter_badge_description": "Näytä kannattajan merkki", @@ -1687,7 +1721,7 @@ "stacktrace": "Vianetsintätiedot", "start": "Aloita", "start_date": "Alkupäivä", - "state": "Maakunta/osavaltio", + "state": "Maakunta", "status": "Tila", "stop_motion_photo": "Pysäytä liikkuva kuva", "stop_photo_sharing": "Lopetetaanko kuvien jakaminen?", @@ -1695,6 +1729,7 @@ "stop_sharing_photos_with_user": "Päätä kuviesi jakaminen tämän käyttäjän kanssa", "storage": "Tallennustila", "storage_label": "Tallennustilan nimike", + "storage_quota": "TallennuskiintiÃļ", "storage_usage": "{used} / {available} käytetty", "submit": "Lähetä", "suggestions": "Ehdotukset", @@ -1704,33 +1739,33 @@ "support_third_party_description": "Immich-asennuksesi on pakattu kolmannen osapuolen toimesta. Kohtaamasi ongelmat saattavat johtua tästä paketista, joten ilmoita niistä ensisijaisesti heille alla olevien linkkien kautta.", "swap_merge_direction": "Käännä yhdistämissuunta", "sync": "Synkronoi", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_albums": "Synkronoi albumit", + "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", + "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", "tag": "Lisää tunniste", "tag_assets": "Lisää tunnisteita", "tag_created": "Luotu tunniste: {tag}", "tag_feature_description": "Selaa valokuvia ja videoita, jotka on ryhmitelty loogisten tunnisteotsikoiden mukaan", "tag_not_found_question": "EtkÃļ lÃļydä tunnistetta? Luo uusi tunniste ", + "tag_people": "Merkitse henkilÃļ tunnisteella", "tag_updated": "Päivitetty tunniste: {tag}", "tagged_assets": "Tunnistettu {count, plural, one {# kohde} other {# kohdetta}}", "tags": "Tunnisteet", - "template": "Template", "theme": "Teema", "theme_selection": "Teeman valinta", "theme_selection_description": "Aseta vaalea tai tumma tila automaattisesti perustuen selaimesi asetuksiin", "theme_setting_asset_list_storage_indicator_title": "Näytä tallennustilan ilmaisin kohteiden kuvakkeissa", - "theme_setting_asset_list_tiles_per_row_title": "Kohteiden määrä rivillä ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Kohteiden määrä rivillä ({count})", + "theme_setting_colorful_interface_subtitle": "Levitä pääväri taustalle.", + "theme_setting_colorful_interface_title": "Värikäs käyttÃļliittymä", "theme_setting_image_viewer_quality_subtitle": "Säädä kuvien katselun laatua", "theme_setting_image_viewer_quality_title": "Kuvien katseluohjelman laatu", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", + "theme_setting_primary_color_subtitle": "Valitse sovelluksen pääväri.", + "theme_setting_primary_color_title": "Väriteema", + "theme_setting_system_primary_color_title": "Käytä järjestelmän väriteemaa", "theme_setting_system_theme_switch": "Automaattinen (seuraa järjestelmän asetusta)", "theme_setting_theme_subtitle": "Valitse sovelluksen teema-asetukset", - "theme_setting_three_stage_loading_subtitle": "Kolmivaiheinen lataaminen saattaa parantaa latauksen suorituskykyä, mutta lisää kaistankäyttÃļä huomattavasti.", + "theme_setting_three_stage_loading_subtitle": "Kolmivaiheinen lataus saattaa parantaa lataustehoa, mutta aiheuttaa huomattavasti suuremman verkon kuormituksen", "theme_setting_three_stage_loading_title": "Ota kolmivaiheinen lataus käyttÃļÃļn", "they_will_be_merged_together": "Nämä tullaan yhdistämään", "third_party_resources": "Kolmannen osapuolen resurssit", @@ -1751,27 +1786,31 @@ "trash_all": "Vie kaikki roskakoriin", "trash_count": "Roskakori {count, number}", "trash_delete_asset": "Poista / vie roskakoriin", - "trash_emptied": "Emptied trash", + "trash_emptied": "Roskakori tyhjennetty", "trash_no_results_message": "Roskakorissa olevat kuvat ja videot näytetään täällä.", "trash_page_delete_all": "Poista kaikki", - "trash_page_empty_trash_dialog_content": "Haluatko poistaa roskakoriin siirretyt kohteet? Kohteet poistetaan lopullisesti Immich:sta.", - "trash_page_info": "Roskakoriin siirretyt kohteet poistetaan lopullisesti {} päivän kuluttua", + "trash_page_empty_trash_dialog_content": "Haluatko tyhjentää roskakorin? Kohteet poistetaan lopullisesti Immich:sta", + "trash_page_info": "Roskakorissa olevat kohteet poistetaan pysyvästi {days} päivän kuluttua", "trash_page_no_assets": "Ei poistettuja kohteita", "trash_page_restore_all": "Palauta kaikki", "trash_page_select_assets_btn": "Valitse kohteet", - "trash_page_title": "Roskakori", + "trash_page_title": "Roskakori ({count})", "trashed_items_will_be_permanently_deleted_after": "Roskakorin kohteet poistetaan pysyvästi {days, plural, one {# päivän} other {# päivän}} päästä.", "type": "Tyyppi", + "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", + "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", "unarchive": "Palauta arkistosta", "unarchived_count": "{count, plural, other {# poistettu arkistosta}}", "unfavorite": "Poista suosikeista", "unhide_person": "Poista henkilÃļ piilosta", "unknown": "Tuntematon", + "unknown_country": "Tuntematon maa", "unknown_year": "Tuntematon vuosi", "unlimited": "Rajoittamaton", "unlink_motion_video": "Poista liikevideon linkitys", "unlink_oauth": "Poista OAuth-linkitys", "unlinked_oauth_account": "LinkittämätÃļn OAuth-tili", + "unmute_memories": "Poista muistojen mykistys", "unnamed_album": "NimetÃļn albumi", "unnamed_album_delete_confirmation": "Haluatko varmasti poistaa tämän albumin?", "unnamed_share": "NimetÃļn jako", @@ -1783,6 +1822,7 @@ "untracked_files": "Tiedostot joita ei seurata", "untracked_files_decription": "Järjestelmä ei seuraa näitä tiedostoja. Ne voivat johtua epäonnistuneista siirroista, keskeytyneistä latauksista, tai ovat jääneet ohjelmavian seurauksena", "up_next": "Seuraavaksi", + "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", "upload": "Siirrä palvelimelle", "upload_concurrency": "Latausten samanaikaisuus", @@ -1795,15 +1835,17 @@ "upload_status_errors": "Virheet", "upload_status_uploaded": "Ladattu", "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "Lähetä Immichiin ({count})", + "uploading": "Lähettään", "usage": "KäyttÃļ", - "use_current_connection": "use current connection", + "use_current_connection": "käytä nykyistä yhteyttä", "use_custom_date_range": "Käytä omaa aikaväliä", "user": "Käyttäjä", + "user_has_been_deleted": "Käyttäjä on poistettu.", "user_id": "Käyttäjän ID", "user_liked": "{user} tykkäsi {type, select, photo {kuvasta} video {videosta} asset {mediasta} other {tästä}}", + "user_pin_code_settings": "PIN-koodi", + "user_pin_code_settings_description": "Hallinnoi PIN-koodiasi", "user_purchase_settings": "Osta", "user_purchase_settings_description": "Hallitse ostostasi", "user_role_set": "Tee käyttäjästä {user} {role}", @@ -1814,19 +1856,18 @@ "users": "Käyttäjät", "utilities": "Apuohjelmat", "validate": "Validoi", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "Anna kelvollinen URL-osoite", "variables": "Muuttujat", "version": "Versio", "version_announcement_closing": "Ystäväsi Alex", "version_announcement_message": "Hei! Sovelluksen uusi versio on saatavilla. Käythän vilkaisemassa julkaisun tiedot ja varmistathan, että ohjelman määritykset ovat ajan tasalla. Erityisesti, jos käytÃļssä on Watchtower tai jokin muu mekanismi Immich-sovelluksen automaattista päivitystä varten.", "version_announcement_overlay_release_notes": "julkaisutiedoissa", "version_announcement_overlay_text_1": "Hei, kaveri! Uusi palvelinversio on saatavilla sovelluksesta", - "version_announcement_overlay_text_2": "Ota hetki aikaa vieraillaksesi", - "version_announcement_overlay_text_3": "ja varmista, että käyttämäsi docker-compose ja .env-asetukset ovat ajantasalla välttyäksesi asetusongelmilta. Varsinkin jos käytät WatchToweria tai jotain muuta mekanismia päivittääksesi palvelinsovellusta automaattisesti.", + "version_announcement_overlay_text_2": "Ota hetki aikaa vieraillaksesi ", + "version_announcement_overlay_text_3": " ja varmista, että käyttämäsi docker-compose ja .env-asetukset ovat ajantasalla välttyäksesi asetusongelmilta. Varsinkin jos käytät WatchToweria tai jotain muuta mekanismia päivittääksesi palvelinsovellusta automaattisesti.", "version_announcement_overlay_title": "Uusi palvelinversio saatavilla 🎉", "version_history": "Versiohistoria", "version_history_item": "Asennettu {version} päivänä {date}", - "video": "Video", "video_hover_setting": "Toista esikatselun video kun kursori viedään sen päälle", "video_hover_setting_description": "Toista videon esikatselukuva kun kursori on kuvan päällä. Vaikka toiminto on pois käytÃļstä, toiston voi aloittaa viemällä kursori toistokuvakkeen päälle.", "videos": "Videot", @@ -1836,10 +1877,12 @@ "view_all": "Näytä kaikki", "view_all_users": "Näytä kaikki käyttäjät", "view_in_timeline": "Näytä aikajanalla", + "view_link": "Näytä linkki", "view_links": "Näytä linkit", "view_name": "Näkymä", "view_next_asset": "Näytä seuraava", "view_previous_asset": "Näytä edellinen", + "view_qr_code": "Näytä QR-koodi", "view_stack": "Näytä pinona", "viewer_remove_from_stack": "Poista pinosta", "viewer_stack_use_as_main_asset": "Käytä pääkohteena", @@ -1850,11 +1893,11 @@ "week": "Viikko", "welcome": "Tervetuloa", "welcome_to_immich": "Tervetuloa Immichiin", - "wifi_name": "WiFi Name", + "wifi_name": "Wi-Fi-verkon nimi", "year": "Vuosi", "years_ago": "{years, plural, one {# vuosi} other {# vuotta}} sitten", "yes": "Kyllä", "you_dont_have_any_shared_links": "Sinulla ei ole jaettuja linkkejä", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Wi-Fi-verkkosi nimi", "zoom_image": "Zoomaa kuvaa" } diff --git a/i18n/fil.json b/i18n/fil.json index 4b5ba5bb7b..7368a1bccb 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -1,6 +1,5 @@ { "about": "Tungkol sa app na ito", - "account": "Account", "account_settings": "Mga Setting ng Account", "acknowledge": "Tanggapin", "action": "Aksyon", @@ -41,21 +40,17 @@ }, "album_user_left": "Umalis sa {album}", "all_albums": "Lahat ng albums", - "anti_clockwise": "", "api_key_description": "Isang beses lamang na ipapakita itong value. Siguraduhin na ikopya itong value bago iclose ang window na ito.", "are_these_the_same_person": "Itong tao na ito ay parehas?", "asset_adding_to_album": "Dinadagdag sa album...", "asset_filename_is_offline": "Offline ang asset {filename}", "asset_uploading": "Ina-upload...", - "discord": "Discord", "documentation": "Dokumentasyion", "done": "Tapos na", "download": "I-download", "edit": "I-edit", "edited": "Inedit", "editor_close_without_save_title": "Isara ang editor?", - "email": "Email", - "exif": "Exif", "explore": "I-explore", "export": "I-export", "has_quota": "May quota", diff --git a/i18n/fr.json b/i18n/fr.json index c116ee261a..4b5a09c664 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -3,9 +3,7 @@ "account": "Compte", "account_settings": "Paramètres du compte", "acknowledge": "Compris", - "action": "Action", "action_common_update": "Mise à jour", - "actions": "Actions", "active": "En cours", "activity": "ActivitÊ", "activity_changed": "ActivitÊ {enabled, select, true {autorisÊe} other {interdite}}", @@ -26,6 +24,7 @@ "add_to_album": "Ajouter à l'album", "add_to_album_bottom_sheet_added": "AjoutÊ à {album}", "add_to_album_bottom_sheet_already_exists": "DÊjà dans {album}", + "add_to_locked_folder": "Ajouter au dossier verrouillÊ", "add_to_shared_album": "Ajouter à l'album partagÊ", "add_url": "Ajouter l'URL", "added_to_archive": "AjoutÊ à l'archive", @@ -39,11 +38,11 @@ "authentication_settings_disable_all": "Êtes-vous sÃģr de vouloir dÊsactiver toutes les mÊthodes de connexion ? La connexion sera complètement dÊsactivÊe.", "authentication_settings_reenable": "Pour rÊactiver, utilisez une Commande Serveur.", "background_task_job": "TÃĸches de fond", - "backup_database": "Sauvegarde de la base de donnÊes", - "backup_database_enable_description": "Activer la sauvegarde", - "backup_keep_last_amount": "Nombre de sauvegardes à conserver", - "backup_settings": "Paramètres de la sauvegarde", - "backup_settings_description": "GÊrer les paramètres de la sauvegarde", + "backup_database": "CrÊation d'une image de la base de donnÊes", + "backup_database_enable_description": "Activer la crÊation d'images de la base de donnÊes", + "backup_keep_last_amount": "Nombre d'images à conserver", + "backup_settings": "Paramètres de crÊation d'images de la base de donnÊes", + "backup_settings_description": "GÊrer les paramètres de crÊation d'images de la base de donnÊes. Note : ces tÃĸches ne sont pas contrôlÊes et vous ne serez pas averti(e) en cas d'Êchec.", "check_all": "Tout cocher", "cleanup": "Nettoyage", "cleared_jobs": "TÃĸches supprimÊes pour : {job}", @@ -53,6 +52,7 @@ "confirm_email_below": "Pour confirmer, tapez ÂĢ {email} Âģ ci-dessous", "confirm_reprocess_all_faces": "Êtes-vous sÃģr de vouloir retraiter tous les visages ? Cela effacera Êgalement les personnes dÊjà identifiÊes.", "confirm_user_password_reset": "Êtes-vous sÃģr de vouloir rÊinitialiser le mot de passe de {user} ?", + "confirm_user_pin_code_reset": "Êtes-vous sÃģr de vouloir rÊinitialiser le code PIN de l'utilisateur {user} ?", "create_job": "CrÊer une tÃĸche", "cron_expression": "Expression cron", "cron_expression_description": "DÊfinir l'intervalle d'analyse à l'aide d'une expression cron. Pour plus d'informations, voir Crontab Guru", @@ -63,7 +63,7 @@ "external_library_created_at": "Bibliothèque externe (crÊÊe le {date})", "external_library_management": "Gestion de la bibliothèque externe", "face_detection": "DÊtection des visages", - "face_detection_description": "DÊtection des visages dans les mÊdias à l'aide de l'apprentissage automatique. Pour les vidÊos, seule la miniature est prise en compte. ÂĢ Actualiser Âģ (re)traite tous les mÊdias. ÂĢ RÊinitialiser Âģ retraite tous les mÊdias en repartant de zÊro. ÂĢ Manquant Âģ met en file d'attente les mÊdias qui n'ont pas encore ÊtÊ pris en compte. Lorsque la dÊtection est terminÊe, tous les visages dÊtectÊs sont ensuite mis en file d'attente pour la reconnaissance faciale.", + "face_detection_description": "DÊtection des visages dans les mÊdias à l'aide de l'apprentissage automatique. Pour les vidÊos, seule la miniature est prise en compte. ÂĢ Actualiser Âģ (re)traite tous les mÊdias. ÂĢ RÊinitialiser Âģ retraite tous les visages en repartant de zÊro. ÂĢ Manquant Âģ met en file d'attente les mÊdias qui n'ont pas encore ÊtÊ traitÊs. Lorsque la dÊtection est terminÊe, les visages dÊtectÊs seront mis en file d'attente pour la reconnaissance faciale.", "facial_recognition_job_description": "Regrouper les visages dÊtectÊs en personnes. Cette Êtape est exÊcutÊe une fois la dÊtection des visages terminÊe. ÂĢ RÊinitialiser Âģ (re)regroupe tous les visages. ÂĢ Manquant Âģ met en file d'attente les visages auxquels aucune personne n'a ÊtÊ attribuÊe.", "failed_job_command": "La commande {command} a ÊchouÊ pour la tÃĸche : {job}", "force_delete_user_warning": "ATTENTION : Cette opÊration entraÃŽne la suppression immÊdiate de l'utilisateur et de tous ses mÊdias. Cette opÊration ne peut ÃĒtre annulÊe et les fichiers ne peuvent ÃĒtre rÊcupÊrÊs.", @@ -87,9 +87,9 @@ "image_resolution_description": "Les rÊsolutions plus ÊlevÊes permettent de prÊserver davantage de dÊtails, mais l'encodage est plus long, les fichiers sont plus volumineux et la rÊactivitÊ de l'application peut s'en trouver rÊduite.", "image_settings": "Paramètres d'image", "image_settings_description": "Gestion de la qualitÊ et rÊsolution des images gÊnÊrÊes", - "image_thumbnail_description": "Petite vignette avec mÊtadonnÊes retirÊes, utilisÊe lors de la visualisation de groupes de photos comme sur la vue chronologique principale", - "image_thumbnail_quality_description": "QualitÊ des vignettes : de 1 à 100. Une valeur ÊlevÊe produit de meilleurs rÊsultats, mais elle produit des fichiers plus volumineux et peut rÊduire la rÊactivitÊ de l'application.", - "image_thumbnail_title": "Paramètres des vignettes", + "image_thumbnail_description": "Petite miniature avec les mÊtadonnÊes retirÊes, utilisÊe lors de la visualisation de groupes de photos comme sur la vue chronologique principale", + "image_thumbnail_quality_description": "QualitÊ des miniatures : de 1 à 100. Une valeur ÊlevÊe produit de meilleurs rÊsultats, mais elle produit des fichiers plus volumineux et peut rÊduire la rÊactivitÊ de l'application.", + "image_thumbnail_title": "Paramètres des miniatures", "job_concurrency": "{job} : nombre de tÃĸches simultanÊes", "job_created": "TÃĸche crÊÊe", "job_not_concurrency_safe": "Cette tÃĸche ne peut pas ÃĒtre exÊcutÊe en multitÃĸche de façon sÃģre.", @@ -169,7 +169,7 @@ "migration_job_description": "Migration des miniatures pour les mÊdias et les visages vers la dernière structure de dossiers", "no_paths_added": "Aucun chemin n'a ÊtÊ ajoutÊ", "no_pattern_added": "Aucun schÊma d'exclusion n'a ÊtÊ ajoutÊ", - "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'Êtiquette de stockage à des mÊdias prÊcÊdemment envoyÊs, exÊcutez la commande", + "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'Êtiquette de stockage à des mÊdias prÊcÊdemment tÊlÊversÊs, exÊcutez", "note_cannot_be_changed_later": "REMARQUE : Il n'est pas possible de modifier ce paramètre ultÊrieurement !", "notification_email_from_address": "Depuis l'adresse", "notification_email_from_address_description": "Adresse courriel de l'expÊditeur, par exemple : ÂĢ Serveur de photos Immich  Âģ", @@ -192,26 +192,22 @@ "oauth_auto_register": "Inscription automatique", "oauth_auto_register_description": "Inscrire automatiquement de nouveaux utilisateurs après leur connexion avec OAuth", "oauth_button_text": "Texte du bouton", - "oauth_client_id": "ID du client", - "oauth_client_secret": "Secret du client", + "oauth_client_secret_description": "NÊcessaire si le protocole PKCE (Proof Key for Code Exchange) n'est pas supportÊ mar le fournisseur d'authentification OAuth", "oauth_enable_description": "Connexion avec OAuth", - "oauth_issuer_url": "URL de l'Êmetteur", "oauth_mobile_redirect_uri": "URI de redirection mobile", "oauth_mobile_redirect_uri_override": "Remplacer l'URI de redirection mobile", "oauth_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme '{callback} '", - "oauth_profile_signing_algorithm": "Algorithme de signature de profil", - "oauth_profile_signing_algorithm_description": "Algorithme utilisÊ pour signer le profil utilisateur.", - "oauth_scope": "PÊrimètre", "oauth_settings": "OAuth", "oauth_settings_description": "GÊrer les paramètres de connexion OAuth", "oauth_settings_more_details": "Pour plus de dÊtails sur cette fonctionnalitÊ, consultez ce lien.", - "oauth_signing_algorithm": "Algorithme de signature", "oauth_storage_label_claim": "Demande d'Êtiquette de stockage", "oauth_storage_label_claim_description": "DÊfinir automatiquement l'Êtiquette de stockage de l'utilisateur sur la valeur de cette revendication.", "oauth_storage_quota_claim": "Demande de quota de stockage", "oauth_storage_quota_claim_description": "DÊfinir automatiquement le quota de stockage de l'utilisateur par la valeur de cette demande.", "oauth_storage_quota_default": "Quota de stockage par dÊfaut (Go)", "oauth_storage_quota_default_description": "Quota en Go à utiliser lorsqu'aucune valeur n'est prÊcisÊe (saisir 0 pour un quota illimitÊ).", + "oauth_timeout": "Expiration de la durÊe de la requÃĒte", + "oauth_timeout_description": "DÊlai d'expiration des requÃĒtes en millisecondes", "offline_paths": "Chemins d'accès hors ligne", "offline_paths_description": "Ces rÊsultats peuvent ÃĒtre dus à la suppression manuelle de fichiers qui ne font pas partie d'une bibliothèque externe.", "password_enable_description": "Connexion avec courriel et mot de passe", @@ -250,14 +246,14 @@ "storage_template_hash_verification_enabled": "VÊrification du hachage activÊe", "storage_template_hash_verification_enabled_description": "Active la vÊrification du hachage, ne dÊsactivez pas cette option à moins d'ÃĒtre sÃģr de ce que vous faites", "storage_template_migration": "Migration du modèle de stockage", - "storage_template_migration_description": "Appliquer le modèle courant {template} aux mÊdias prÊcÊdemment envoyÊs", - "storage_template_migration_info": "L'enregistrement des modèles convertit toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux mÊdias. Pour appliquer rÊtroactivement le modèle aux mÊdias prÊcÊdemment envoyÊs, exÊcutez la tÃĸche {job}.", + "storage_template_migration_description": "Appliquer le modèle courant {template} aux mÊdias prÊcÊdemment tÊlÊversÊs", + "storage_template_migration_info": "L'enregistrement des modèles va convertir toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux mÊdias. Pour appliquer rÊtroactivement le modèle aux mÊdias prÊcÊdemment tÊlÊversÊs, exÊcutez la tÃĸche {job}.", "storage_template_migration_job": "TÃĸche de migration du modèle de stockage", "storage_template_more_details": "Pour plus de dÊtails sur cette fonctionnalitÊ, reportez-vous au Modèle de stockage et à ses implications", "storage_template_onboarding_description": "Lorsqu'elle est activÊe, cette fonctionnalitÊ rÊorganise les fichiers basÊs sur un modèle dÊfini par l'utilisateur. En raison de problèmes de stabilitÊ, la fonction a ÊtÊ dÊsactivÊe par dÊfaut. Pour plus d'informations, veuillez consulter la documentation.", "storage_template_path_length": "Limite approximative de la longueur du chemin : {length, number}/{limit, number}", "storage_template_settings": "Modèle de stockage", - "storage_template_settings_description": "GÊrer la structure des dossiers et le nom des fichiers du mÊdia envoyÊ", + "storage_template_settings_description": "GÊrer la structure des dossiers et le nom des fichiers du mÊdia tÊlÊversÊ", "storage_template_user_label": "{label} est l'Êtiquette de stockage de l'utilisateur", "system_settings": "Paramètres du système", "tag_cleanup_job": "Nettoyage des Êtiquettes", @@ -345,13 +341,14 @@ "trash_settings": "Corbeille", "trash_settings_description": "GÊrer les paramètres de la corbeille", "untracked_files": "Fichiers non suivis", - "untracked_files_description": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat d'erreurs de dÊplacement, d'envois interrompus, ou d'abandons en raison d'un bug", + "untracked_files_description": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat d'Êchecs de dÊplacement, de tÊlÊversements interrompus, ou d'abandons en raison d'un bug", "user_cleanup_job": "Nettoyage des utilisateurs", "user_delete_delay": "La suppression dÊfinitive du compte et des mÊdias de {user} sera programmÊe dans {delay, plural, one {# jour} other {# jours}}.", "user_delete_delay_settings": "DÊlai de suppression", "user_delete_delay_settings_description": "Nombre de jours après la validation pour supprimer dÊfinitivement le compte et les mÊdias d'un utilisateur. La suppression des utilisateurs se lance à minuit. Les modifications apportÊes à ce paramètre seront pris en compte lors de la prochaine exÊcution.", "user_delete_immediately": "Le compte et les mÊdias de {user} seront mis en file d'attente en vue d'une suppression permanente immÊdiatement.", "user_delete_immediately_checkbox": "Mise en file d'attente d'un utilisateur et de mÊdias en vue d'une suppression immÊdiate", + "user_details": "DÊtails utilisateur", "user_management": "Gestion des utilisateurs", "user_password_has_been_reset": "Le mot de passe de l'utilisateur a ÊtÊ rÊinitialisÊ :", "user_password_reset_description": "Veuillez fournir le mot de passe temporaire à l'utilisateur et informez-le qu'il devra le changer à sa première connexion.", @@ -369,15 +366,18 @@ }, "admin_email": "Courriel Admin", "admin_password": "Mot de passe Admin", - "administration": "Administration", "advanced": "AvancÊ", - "advanced_settings_log_level_title": "Niveau de log : {}", - "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des vignettes à partir de ressources prÊsentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", + "advanced_settings_enable_alternate_media_filter_subtitle": "Utilisez cette option pour filtrer les mÊdia durant la synchronisation avec des critères alternatifs. N'utilisez cela que lorsque l'application n'arrive pas à dÊtecter tout les albums.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPÉRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", + "advanced_settings_log_level_title": "Niveau de journalisation : {level}", + "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources prÊsentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", "advanced_settings_prefer_remote_title": "PrÊfÊrer les images externes", "advanced_settings_proxy_headers_subtitle": "Ajoutez des en-tÃĒtes personnalisÊs à chaque requÃĒte rÊseau", "advanced_settings_proxy_headers_title": "En-tÃĒtes de proxy", "advanced_settings_self_signed_ssl_subtitle": "Permet d'ignorer la vÊrification du certificat SSL pour le point d'accès du serveur. Requis pour les certificats auto-signÊs.", "advanced_settings_self_signed_ssl_title": "Autoriser les certificats SSL auto-signÊs", + "advanced_settings_sync_remote_deletions_subtitle": "Supprimer ou restaurer automatiquement un mÊdia sur cet appareil lorsqu'une action a ÊtÊ faite sur le web", + "advanced_settings_sync_remote_deletions_title": "Synchroniser les suppressions depuis le serveur [EXPÉRIMENTAL]", "advanced_settings_tile_subtitle": "Paramètres d'utilisateur avancÊs", "advanced_settings_troubleshooting_subtitle": "Activer des fonctions supplÊmentaires pour le dÊpannage", "advanced_settings_troubleshooting_title": "DÊpannage", @@ -400,9 +400,9 @@ "album_remove_user_confirmation": "Êtes-vous sÃģr de vouloir supprimer {user} ?", "album_share_no_users": "Il semble que vous ayez partagÊ cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", "album_thumbnail_card_item": "1 ÊlÊment", - "album_thumbnail_card_items": "{} ÊlÊments", + "album_thumbnail_card_items": "{count} ÊlÊments", "album_thumbnail_card_shared": " ¡ PartagÊ", - "album_thumbnail_shared_by": "PartagÊ par {}", + "album_thumbnail_shared_by": "PartagÊ par {user}", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagÊ a de nouveaux mÊdias", "album_user_left": "{album} quittÊ", @@ -416,8 +416,6 @@ "album_viewer_appbar_share_to": "Partager à", "album_viewer_page_share_add_users": "Ajouter des utilisateurs", "album_with_link_access": "Permettre à n'importe qui possÊdant le lien de voir les photos et les personnes de cet album.", - "albums": "Albums", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", @@ -425,30 +423,28 @@ "allow_dark_mode": "Autoriser le mode sombre", "allow_edits": "Autoriser les modifications", "allow_public_user_to_download": "Permettre aux utilisateurs non connectÊs de tÊlÊcharger", - "allow_public_user_to_upload": "Permettre l'envoi aux utilisateurs non connectÊs", + "allow_public_user_to_upload": "Permettre le tÊlÊversement aux utilisateurs non connectÊs", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "ClÊ API", "api_key_description": "Cette valeur ne sera affichÊe qu'une seule fois. Assurez-vous de la copier avant de fermer la fenÃĒtre.", "api_key_empty": "Le nom de votre clÊ API ne doit pas ÃĒtre vide", "api_keys": "ClÊs d'API", - "app_bar_signout_dialog_content": "Êtes-vous sÃģr de vouloir vous dÊconnecter?", + "app_bar_signout_dialog_content": "Êtes-vous sÃģr(e) de vouloir vous dÊconnecter ?", "app_bar_signout_dialog_ok": "Oui", "app_bar_signout_dialog_title": "Se dÊconnecter", "app_settings": "Paramètres de l'application", "appears_in": "ApparaÃŽt dans", - "archive": "Archive", "archive_or_unarchive_photo": "Archiver ou dÊsarchiver une photo", "archive_page_no_archived_assets": "Aucun ÊlÊment archivÊ n'a ÊtÊ trouvÊ", - "archive_page_title": "Archive ({})", "archive_size": "Taille de l'archive", "archive_size_description": "Configurer la taille de l'archive maximale pour les tÊlÊchargements (en Go)", "archived": "Archives", "archived_count": "{count, plural, one {# archivÊ} other {# archivÊs}}", "are_these_the_same_person": "Est-ce la mÃĒme personne ?", "are_you_sure_to_do_this": "Êtes-vous sÃģr de vouloir faire ceci ?", - "asset_action_delete_err_read_only": "Impossible de supprimer le(s) ÊlÊment(s) en lecture seule.", - "asset_action_share_err_offline": "Impossible de rÊcupÊrer le(s) ÊlÊment(s) hors ligne.", + "asset_action_delete_err_read_only": "Impossible de supprimer le(s) mÊdia(s) en lecture seule, ils sont ignorÊs", + "asset_action_share_err_offline": "Impossible de rÊcupÊrer le(s) mÊdia(s) hors ligne, ils sont ignorÊs", "asset_added_to_album": "AjoutÊ à l'album", "asset_adding_to_album": "Ajout à l'albumâ€Ļ", "asset_description_updated": "La description du mÊdia a ÊtÊ mise à jour", @@ -468,7 +464,7 @@ "asset_restored_successfully": "ÉlÊment restaurÊ avec succès", "asset_skipped": "SautÊ", "asset_skipped_in_trash": "À la corbeille", - "asset_uploaded": "EnvoyÊ", + "asset_uploaded": "TÊlÊversÊ", "asset_uploading": "TÊlÊversementâ€Ļ", "asset_viewer_settings_subtitle": "Modifier les paramètres du visualiseur photos", "asset_viewer_settings_title": "Visualiseur d'ÊlÊments", @@ -477,18 +473,18 @@ "assets_added_to_album_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}} à l'album", "assets_added_to_name_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}} à {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, one {# mÊdia} other {# mÊdias}}", - "assets_deleted_permanently": "{} ÊlÊment(s) supprimÊ(s) dÊfinitivement", - "assets_deleted_permanently_from_server": "{} ÊlÊment(s) supprimÊ(s) dÊfinitivement du serveur Immich", + "assets_deleted_permanently": "{count} mÊdia(s) supprimÊ(s) dÊfinitivement", + "assets_deleted_permanently_from_server": "{count} mÊdia(s) supprimÊ(s) dÊfinitivement du serveur Immich", "assets_moved_to_trash_count": "{count, plural, one {# mÊdia dÊplacÊ} other {# mÊdias dÊplacÊs}} dans la corbeille", "assets_permanently_deleted_count": "{count, plural, one {# mÊdia supprimÊ} other {# mÊdias supprimÊs}} dÊfinitivement", "assets_removed_count": "{count, plural, one {# mÊdia supprimÊ} other {# mÊdias supprimÊs}}", - "assets_removed_permanently_from_device": "{} ÊlÊment(s) supprimÊ(s) dÊfinitivement de votre appareil", + "assets_removed_permanently_from_device": "{count} mÊdia(s) supprimÊ(s) dÊfinitivement de votre appareil", "assets_restore_confirmation": "Êtes-vous sÃģr de vouloir restaurer tous vos mÊdias de la corbeille ? Vous ne pouvez pas annuler cette action ! Notez que les mÊdias hors ligne ne peuvent ÃĒtre restaurÊs de cette façon.", "assets_restored_count": "{count, plural, one {# mÊdia restaurÊ} other {# mÊdias restaurÊs}}", - "assets_restored_successfully": "ÉlÊment restaurÊ avec succès", - "assets_trashed": "{} ÊlÊment(s) dÊplacÊ(s) vers la corbeille", + "assets_restored_successfully": "{count} ÊlÊment(s) restaurÊ(s) avec succès", + "assets_trashed": "{count} mÊdia(s) dÊplacÊ(s) vers la corbeille", "assets_trashed_count": "{count, plural, one {# mÊdia} other {# mÊdias}} mis à la corbeille", - "assets_trashed_from_server": "{} ÊlÊment(s) dÊplacÊ(s) vers la corbeille du serveur Immich", + "assets_trashed_from_server": "{count} mÊdia(s) dÊplacÊ(s) vers la corbeille du serveur Immich", "assets_were_part_of_album_count": "{count, plural, one {Un mÊdia est} other {Des mÊdias sont}} dÊjà dans l'album", "authorized_devices": "Appareils autorisÊs", "automatic_endpoint_switching_subtitle": "Se connecter localement lorsque connectÊ au WI-FI spÊcifiÊ mais utiliser une adresse alternative lorsque connectÊ à un autre rÊseau", @@ -497,46 +493,45 @@ "back_close_deselect": "Retournez en arrière, fermez ou dÊsÊlectionnez", "background_location_permission": "Permission de localisation en arrière plan", "background_location_permission_content": "Afin de pouvoir changer d'adresse en arrière plan, Immich doit avoir *en permanence* accès à la localisation prÊcise, afin d'accÊder au le nom du rÊseau Wi-Fi utilisÊ", - "backup_album_selection_page_albums_device": "Albums sur l'appareil ({})", + "backup_album_selection_page_albums_device": "Albums sur l'appareil ({count})", "backup_album_selection_page_albums_tap": "Tapez pour inclure, tapez deux fois pour exclure", "backup_album_selection_page_assets_scatter": "Les ÊlÊments peuvent ÃĒtre rÊpartis sur plusieurs albums. De ce fait, les albums peuvent ÃĒtre inclus ou exclus pendant le processus de sauvegarde.", "backup_album_selection_page_select_albums": "SÊlectionner les albums", "backup_album_selection_page_selection_info": "Informations sur la sÊlection", "backup_album_selection_page_total_assets": "Total des ÊlÊments uniques", "backup_all": "Tout", - "backup_background_service_backup_failed_message": "Échec de la sauvegarde des ÊlÊments. Nouvelle tentative...", - "backup_background_service_connection_failed_message": "Impossible de se connecter au serveur. Nouvelle tentative...", - "backup_background_service_current_upload_notification": "Transfert {}", - "backup_background_service_default_notification": "Recherche de nouveaux ÊlÊments...", + "backup_background_service_backup_failed_message": "Échec de la sauvegarde des mÊdias. Nouvelle tentativeâ€Ļ", + "backup_background_service_connection_failed_message": "Impossible de se connecter au serveur. Nouvelle tentativeâ€Ļ", + "backup_background_service_current_upload_notification": "TÊlÊversement de {filename}", + "backup_background_service_default_notification": "Recherche de nouveaux mÊdiasâ€Ļ", "backup_background_service_error_title": "Erreur de sauvegarde", - "backup_background_service_in_progress_notification": "Sauvegarde de vos ÊlÊments...", - "backup_background_service_upload_failure_notification": "Impossible de transfÊrer {}", + "backup_background_service_in_progress_notification": "Sauvegarde de vos mÊdiasâ€Ļ", + "backup_background_service_upload_failure_notification": "Échec lors du tÊlÊversement de {filename}", "backup_controller_page_albums": "Sauvegarder les albums", "backup_controller_page_background_app_refresh_disabled_content": "Activez le rafraÃŽchissement de l'application en arrière-plan dans Paramètres > GÊnÊral > RafraÃŽchissement de l'application en arrière-plan afin d'utiliser la sauvegarde en arrière-plan.", "backup_controller_page_background_app_refresh_disabled_title": "RafraÃŽchissement de l'application en arrière-plan dÊsactivÊ", "backup_controller_page_background_app_refresh_enable_button_text": "Aller aux paramètres", "backup_controller_page_background_battery_info_link": "Montrez-moi comment", "backup_controller_page_background_battery_info_message": "Pour une expÊrience optimale de la sauvegarde en arrière-plan, veuillez dÊsactiver toute optimisation de la batterie limitant l'activitÊ en arrière-plan pour Immich.\n\nÉtant donnÊ que cela est spÊcifique à chaque appareil, veuillez consulter les informations requises pour le fabricant de votre appareil.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimisation de la batterie", "backup_controller_page_background_charging": "Seulement pendant la charge", "backup_controller_page_background_configure_error": "Échec de la configuration du service d'arrière-plan", - "backup_controller_page_background_delay": "Retarder la sauvegarde des nouveaux ÊlÊments d'actif: {}", - "backup_controller_page_background_description": "Activez le service d'arrière-plan pour sauvegarder automatiquement tous les nouveaux ÊlÊments sans avoir à ouvrir l'application.", + "backup_controller_page_background_delay": "Retarder la sauvegarde des nouveaux mÊdias : {duration}", + "backup_controller_page_background_description": "Activez le service d'arrière-plan pour sauvegarder automatiquement tous les nouveaux mÊdias sans avoir à ouvrir l'application", "backup_controller_page_background_is_off": "La sauvegarde automatique en arrière-plan est dÊsactivÊe", "backup_controller_page_background_is_on": "La sauvegarde automatique en arrière-plan est activÊe", "backup_controller_page_background_turn_off": "DÊsactiver le service d'arrière-plan", "backup_controller_page_background_turn_on": "Activer le service d'arrière-plan", - "backup_controller_page_background_wifi": "Uniquement sur WiFi", + "backup_controller_page_background_wifi": "Uniquement en wifi", "backup_controller_page_backup": "SauvegardÊ", - "backup_controller_page_backup_selected": "SÊlectionnÊ: ", + "backup_controller_page_backup_selected": "SÊlectionnÊ : ", "backup_controller_page_backup_sub": "Photos et vidÊos sauvegardÊes", - "backup_controller_page_created": "CrÊÊ le: {}", - "backup_controller_page_desc_backup": "Activez la sauvegarde pour envoyer automatiquement les nouveaux ÊlÊments sur le serveur.", - "backup_controller_page_excluded": "Exclus: ", - "backup_controller_page_failed": "Échec de l'opÊration ({})", - "backup_controller_page_filename": "Nom du fichier: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_created": "CrÊÊ le : {date}", + "backup_controller_page_desc_backup": "Activez la sauvegarde au premier plan pour tÊlÊverser automatiquement les nouveaux mÊdias sur le serveur lors de l'ouverture de l'application.", + "backup_controller_page_excluded": "Exclus : ", + "backup_controller_page_failed": "Échec de l'opÊration ({count})", + "backup_controller_page_filename": "Nom du fichier : {filename} [{size}]", + "backup_controller_page_id": "ID : {id}", "backup_controller_page_info": "Informations de sauvegarde", "backup_controller_page_none_selected": "Aucune sÊlection", "backup_controller_page_remainder": "Restant", @@ -545,21 +540,25 @@ "backup_controller_page_start_backup": "DÊmarrer la sauvegarde", "backup_controller_page_status_off": "La sauvegarde est dÊsactivÊe", "backup_controller_page_status_on": "La sauvegarde est activÊe", - "backup_controller_page_storage_format": "{} de {} utilisÊ", + "backup_controller_page_storage_format": "{used} sur {total} utilisÊs", "backup_controller_page_to_backup": "Albums à sauvegarder", "backup_controller_page_total_sub": "Toutes les photos et vidÊos uniques des albums sÊlectionnÊs", "backup_controller_page_turn_off": "DÊsactiver la sauvegarde", "backup_controller_page_turn_on": "Activer la sauvegarde", - "backup_controller_page_uploading_file_info": "Transfert des informations du fichier", + "backup_controller_page_uploading_file_info": "TÊlÊversement des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", "backup_info_card_assets": "ÊlÊments", "backup_manual_cancelled": "AnnulÊ", - "backup_manual_in_progress": "TÊlÊchargement dÊjà en cours. Essayez après un instant", - "backup_manual_success": "Succès ", - "backup_manual_title": "Statut du tÊlÊchargement ", + "backup_manual_in_progress": "TÊlÊversement dÊjà en cours. RÊessayez plus tard", + "backup_manual_success": "Succès", + "backup_manual_title": "Statut du tÊlÊversement", "backup_options_page_title": "Options de sauvegarde", - "backup_setting_subtitle": "Ajuster les paramètres de sauvegarde", + "backup_setting_subtitle": "Ajuster les paramètres de tÊlÊversement au premier et en arrière-plan", "backward": "Arrière", + "biometric_auth_enabled": "Authentification biomÊtrique activÊe", + "biometric_locked_out": "L'authentification biomÊtrique est verrouillÊ", + "biometric_no_options": "Aucune option biomÊtrique disponible", + "biometric_not_available": "L'authentification biomÊtrique n'est pas disponible sur cet appareil", "birthdate_saved": "Date de naissance enregistrÊe avec succès", "birthdate_set_description": "La date de naissance est utilisÊe pour calculer l'Ãĸge de cette personne au moment oÚ la photo a ÊtÊ prise.", "blurred_background": "Arrière-plan floutÊ", @@ -570,21 +569,21 @@ "bulk_keep_duplicates_confirmation": "Êtes-vous sÃģr de vouloir conserver {count, plural, one {# doublon} other {# doublons}} ? Cela rÊsoudra tous les groupes de doublons sans rien supprimer.", "bulk_trash_duplicates_confirmation": "Êtes-vous sÃģr de vouloir mettre à la corbeille {count, plural, one {# doublon} other {# doublons}} ? Cette opÊration permet de conserver le plus grand mÊdia de chaque groupe et de mettre à la corbeille tous les autres doublons.", "buy": "Acheter Immich", - "cache_settings_album_thumbnails": "vignettes de la page bibliothèque ({} ÊlÊments)", + "cache_settings_album_thumbnails": "Page des miniatures de la bibliothèque ({count} mÊdias)", "cache_settings_clear_cache_button": "Effacer le cache", "cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.", "cache_settings_duplicated_assets_clear_button": "EFFACER", "cache_settings_duplicated_assets_subtitle": "Photos et vidÊos qui sont exclues par l'application", - "cache_settings_duplicated_assets_title": "ÉlÊments dupliquÊs ({})", - "cache_settings_image_cache_size": "Taille du cache des images ({} ÊlÊments)", - "cache_settings_statistics_album": "vignettes de la bibliothèque", - "cache_settings_statistics_assets": "{} ÊlÊments ({})", + "cache_settings_duplicated_assets_title": "MÊdias dupliquÊs ({count})", + "cache_settings_image_cache_size": "Taille du cache des images ({count} mÊdias)", + "cache_settings_statistics_album": "Miniatures de la bibliothèque", + "cache_settings_statistics_assets": "{count} mÊdias ({size})", "cache_settings_statistics_full": "Images complètes", - "cache_settings_statistics_shared": "vignettes d'albums partagÊs", - "cache_settings_statistics_thumbnail": "vignettes", + "cache_settings_statistics_shared": "Miniatures de l'album partagÊ", + "cache_settings_statistics_thumbnail": "Miniatures", "cache_settings_statistics_title": "Utilisation du cache", "cache_settings_subtitle": "Contrôler le comportement de mise en cache de l'application mobile Immich", - "cache_settings_thumbnail_size": "Taille du cache des vignettes ({} ÊlÊments)", + "cache_settings_thumbnail_size": "Taille du cache des miniatures ({count} mÊdias)", "cache_settings_tile_subtitle": "Contrôler le comportement du stockage local", "cache_settings_tile_title": "Stockage local", "cache_settings_title": "Paramètres de mise en cache", @@ -597,7 +596,9 @@ "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", + "cast": "Cast", "change_date": "Changer la date", + "change_description": "Changer la description", "change_display_order": "Modifier l'ordre d'affichage", "change_expiration_time": "Modifier le dÊlai d'expiration", "change_location": "Changer la localisation", @@ -610,13 +611,14 @@ "change_password_form_new_password": "Nouveau mot de passe", "change_password_form_password_mismatch": "Les mots de passe ne correspondent pas", "change_password_form_reenter_new_password": "Saisissez à nouveau le nouveau mot de passe", + "change_pin_code": "Changer le code PIN", "change_your_password": "Changer votre mot de passe", "changed_visibility_successfully": "VisibilitÊ modifiÊe avec succès", "check_all": "Tout sÊlectionner", "check_corrupt_asset_backup": "VÊrifier la corruption des ÊlÊments enregistrÊs", "check_corrupt_asset_backup_button": "VÊrifier", "check_corrupt_asset_backup_description": "Lancer cette vÊrification uniquement lorsque connectÊ à un rÊseau Wi-Fi et que tout le contenu a ÊtÊ enregistrÊ. Cette procÊdure peut durer plusieurs minutes.", - "check_logs": "VÊrifier les logs", + "check_logs": "VÊrifier les journaux", "choose_matching_people_to_merge": "Choisir les personnes à fusionner", "city": "Ville", "clear": "Effacer", @@ -650,17 +652,19 @@ "confirm_delete_face": "Êtes-vous sÃģr de vouloir supprimer le visage de {name} du mÊdia ?", "confirm_delete_shared_link": "Voulez-vous vraiment supprimer ce lien partagÊ ?", "confirm_keep_this_delete_others": "Tous les autres mÊdias dans la pile seront supprimÊs sauf celui-ci. Êtes-vous sÃģr de vouloir continuer ?", + "confirm_new_pin_code": "Confirmer le nouveau code PIN", "confirm_password": "Confirmer le mot de passe", + "connected_to": "ConnectÊ à", "contain": "Contenu", "context": "Contexte", "continue": "Continuer", - "control_bottom_app_bar_album_info_shared": "{} ÊlÊments - PartagÊs", + "control_bottom_app_bar_album_info_shared": "{count} mÊdias ¡ PartagÊs", "control_bottom_app_bar_create_new_album": "CrÊer un nouvel album", "control_bottom_app_bar_delete_from_immich": "Supprimer de Immich", "control_bottom_app_bar_delete_from_local": "Supprimer de l'appareil", "control_bottom_app_bar_edit_location": "Modifier la localisation", "control_bottom_app_bar_edit_time": "Modifier la date et l'heure", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Lien partagÊ", "control_bottom_app_bar_share_to": "Partager à", "control_bottom_app_bar_trash_from_immich": "DÊplacer vers la corbeille", "copied_image_to_clipboard": "Image copiÊe dans le presse-papiers.", @@ -692,10 +696,12 @@ "create_tag_description": "CrÊer une nouvelle Êtiquette. Pour les Êtiquettes imbriquÊes, veuillez entrer le chemin complet de l'Êtiquette, y compris les caractères \"/\".", "create_user": "CrÊer un utilisateur", "created": "CrÊÊ", + "created_at": "CrÊÊ à", "crop": "Recadrer", "curated_object_page_title": "Objets", "current_device": "Appareil actuel", - "current_server_address": "Adresse actuelle du serveur ", + "current_pin_code": "Code PIN actuel", + "current_server_address": "Adresse actuelle du serveur", "custom_locale": "Paramètres rÊgionaux personnalisÊs", "custom_locale_description": "Afficher les dates et nombres en fonction des paramètres rÊgionaux", "daily_title_text_date": "E, dd MMM", @@ -704,7 +710,6 @@ "date_after": "Date après", "date_and_time": "Date et heure", "date_before": "Date avant", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Date de naissance enregistrÊe avec succès", "date_range": "Plage de dates", "day": "Jour", @@ -718,10 +723,10 @@ "delete": "Supprimer", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clÊ API ?", - "delete_dialog_alert": "Ces ÊlÊments seront dÊfinitivement supprimÊs de Immich et de votre appareil.", - "delete_dialog_alert_local": "Ces ÊlÊments seront dÊfinitivement supprimÊs de votre appareil mais resteront disponibles sur le serveur d'Immich.", - "delete_dialog_alert_local_non_backed_up": "Certains ÊlÊments ne sont pas sauvegardÊs sur Immich et seront dÊfinitivement supprimÊs de votre appareil.", - "delete_dialog_alert_remote": "Ces ÊlÊments seront dÊfinitivement supprimÊs du serveur Immich.", + "delete_dialog_alert": "Ces mÊdias seront dÊfinitivement supprimÊs de Immich et de votre appareil", + "delete_dialog_alert_local": "Ces mÊdias seront dÊfinitivement supprimÊs de votre appareil mais resteront disponibles sur le serveur d'Immich", + "delete_dialog_alert_local_non_backed_up": "Certains mÊdias ne sont pas sauvegardÊs sur Immich et seront dÊfinitivement supprimÊs de votre appareil", + "delete_dialog_alert_remote": "Ces mÊdias seront dÊfinitivement supprimÊs du serveur Immich", "delete_dialog_ok_force": "Supprimer tout de mÃĒme", "delete_dialog_title": "Supprimer dÊfinitivement", "delete_duplicates_confirmation": "Êtes-vous certain de vouloir supprimer dÊfinitivement ces doublons ?", @@ -739,14 +744,11 @@ "delete_user": "Supprimer l'utilisateur", "deleted_shared_link": "Lien partagÊ supprimÊ", "deletes_missing_assets": "Supprimer les mÊdias manquants du disque", - "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vÊrifier le journal pour plus de dÊtails", "details": "DÊtails", - "direction": "Direction", "disabled": "DÊsactivÊ", "disallow_edits": "Ne pas autoriser les modifications", - "discord": "Discord", "discover": "DÊcouvrir", "dismiss_all_errors": "Ignorer toutes les erreurs", "dismiss_error": "Ignorer l'erreur", @@ -755,7 +757,6 @@ "display_original_photos": "Afficher les photos originales", "display_original_photos_setting_description": "Afficher de prÊfÊrence la photo originale lors de la visualisation d'un mÊdia plutôt que sa miniature lorsque cela est possible. Cela peut entraÃŽner des vitesses d'affichage plus lentes.", "do_not_show_again": "Ne plus afficher ce message", - "documentation": "Documentation", "done": "TerminÊ", "download": "TÊlÊcharger", "download_canceled": "TÊlÊchargement annulÊ", @@ -763,7 +764,7 @@ "download_enqueue": "TÊlÊchargement en attente", "download_error": "Erreur de tÊlÊchargement", "download_failed": "TÊlÊchargement ÊchouÊ", - "download_filename": "fichier : {}", + "download_filename": "fichier : {filename}", "download_finished": "TÊlÊchargement terminÊ", "download_include_embedded_motion_videos": "VidÊos intÊgrÊes", "download_include_embedded_motion_videos_description": "Inclure des vidÊos intÊgrÊes dans les photos de mouvement comme un fichier sÊparÊ", @@ -778,7 +779,7 @@ "downloading": "TÊlÊchargement", "downloading_asset_filename": "TÊlÊchargement du mÊdia {filename}", "downloading_media": "TÊlÊchargement du mÊdia", - "drop_files_to_upload": "DÊposez les fichiers n'importe oÚ pour envoyer", + "drop_files_to_upload": "DÊposez les fichiers n'importe oÚ pour tÊlÊverser", "duplicates": "Doublons", "duplicates_description": "Examiner chaque groupe et indiquer s'il y a des doublons", "duration": "DurÊe", @@ -787,6 +788,8 @@ "edit_avatar": "Modifier l'avatar", "edit_date": "Modifier la date", "edit_date_and_time": "Modifier la date et l'heure", + "edit_description": "Modifier la description", + "edit_description_prompt": "Choisir une nouvelle description :", "edit_exclusion_pattern": "Modifier le schÊma d'exclusion", "edit_faces": "Modifier les visages", "edit_import_path": "Modifier le chemin d'importation", @@ -805,21 +808,24 @@ "editor_close_without_save_prompt": "Les changements ne seront pas enregistrÊs", "editor_close_without_save_title": "Fermer l'Êditeur ?", "editor_crop_tool_h2_aspect_ratios": "Rapports hauteur/largeur", - "editor_crop_tool_h2_rotation": "Rotation", "email": "Courriel", + "email_notifications": "Notifications email", "empty_folder": "Ce dossier est vide", "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sÃģr de vouloir vider la corbeille ? Cela supprimera dÊfinitivement de Immich tous les mÊdias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biomÊtrique", "enabled": "ActivÊ", "end_date": "Date de fin", "enqueued": "Mis en file", - "enter_wifi_name": "Entrez le nom du rÊseau ", + "enter_wifi_name": "Entrez le nom du rÊseau wifi", + "enter_your_pin_code": "Entrez votre code PIN", + "enter_your_pin_code_subtitle": "Entrez votre code PIN pour accÊder au dossier verrouillÊ", "error": "Erreur", "error_change_sort_album": "Impossible de modifier l'ordre de tri des albums", "error_delete_face": "Erreur lors de la suppression du visage pour le mÊdia", "error_loading_image": "Erreur de chargement de l'image", - "error_saving_image": "Erreur : {}", + "error_saving_image": "Erreur : {error}", "error_title": "Erreur - Quelque chose s'est mal passÊ", "errors": { "cannot_navigate_next_asset": "Impossible de naviguer jusqu'au prochain mÊdia", @@ -827,7 +833,7 @@ "cant_apply_changes": "Impossible d'appliquer les changements", "cant_change_activity": "Impossible {enabled, select, true {d'interdire} other {d'autoriser}} l'activitÊ", "cant_change_asset_favorite": "Impossible de changer le favori du mÊdia", - "cant_change_metadata_assets_count": "Impossible de modifier les mÊtadonnÊes {count, plural, one {d'un mÊdia} other {de # mÊdias}}", + "cant_change_metadata_assets_count": "Impossible de modifier les mÊtadonnÊes {count, plural, une {# asset} autre {# assets}}", "cant_get_faces": "Impossible d'obtenir des visages", "cant_get_number_of_comments": "Impossible d'obtenir le nombre de commentaires", "cant_search_people": "Impossible de rechercher des personnes", @@ -849,10 +855,12 @@ "failed_to_keep_this_delete_others": "Impossible de conserver ce mÊdia et de supprimer les autres mÊdias", "failed_to_load_asset": "Impossible de charger le mÊdia", "failed_to_load_assets": "Impossible de charger les mÊdias", + "failed_to_load_notifications": "Erreur de rÊcupÊration des notifications", "failed_to_load_people": "Impossible de charger les personnes", "failed_to_remove_product_key": "Échec de suppression de la clÊ du produit", "failed_to_stack_assets": "Impossible d'empiler les mÊdias", "failed_to_unstack_assets": "Impossible de dÊpiler les mÊdias", + "failed_to_update_notification_status": "Erreur de mise à jour du statut des notifications", "import_path_already_exists": "Ce chemin d'importation existe dÊjà.", "incorrect_email_or_password": "Courriel ou mot de passe incorrect", "paths_validation_failed": "Validation ÊchouÊe pour {paths, plural, one {# un chemin} other {# plusieurs chemins}}", @@ -870,6 +878,7 @@ "unable_to_archive_unarchive": "Impossible {archived, select, true {d'archiver} other {de supprimer de l'archive}}", "unable_to_change_album_user_role": "Impossible de changer le rôle de l'utilisateur de l'album", "unable_to_change_date": "Impossible de modifier la date", + "unable_to_change_description": "Échec de la modification de la description", "unable_to_change_favorite": "Impossible de changer de favori pour le mÊdia", "unable_to_change_location": "Impossible de changer la localisation", "unable_to_change_password": "Impossible de changer le mot de passe", @@ -907,7 +916,8 @@ "unable_to_log_out_all_devices": "Incapable de dÊconnecter tous les appareils", "unable_to_log_out_device": "Impossible de dÊconnecter l'appareil", "unable_to_login_with_oauth": "Impossible de se connecter avec OAuth", - "unable_to_play_video": "Impossible de jouer la vidÊo", + "unable_to_move_to_locked_folder": "Échec du dÊplacement vers le dossier verrouillÊ", + "unable_to_play_video": "Impossible de lancer la vidÊo", "unable_to_reassign_assets_existing_person": "Impossible de rÊattribuer les mÊdias à {name, select, null {une personne existante} other {{name}}}", "unable_to_reassign_assets_new_person": "Impossible de rÊattribuer les mÊdias à une nouvelle personne", "unable_to_refresh_user": "Impossible d'actualiser l'utilisateur", @@ -920,6 +930,7 @@ "unable_to_remove_reaction": "Impossible de supprimer la rÊaction", "unable_to_repair_items": "Impossible de rÊparer les ÊlÊments", "unable_to_reset_password": "Impossible de rÊinitialiser le mot de passe", + "unable_to_reset_pin_code": "Impossible de rÊinitialiser le code PIN", "unable_to_resolve_duplicate": "Impossible de rÊsoudre le doublon", "unable_to_restore_assets": "Impossible de restaurer les mÊdias", "unable_to_restore_trash": "Impossible de restaurer la corbeille", @@ -945,23 +956,22 @@ "unable_to_update_settings": "Impossible de mettre à jour les paramètres", "unable_to_update_timeline_display_status": "Impossible de mettre à jour le statut d'affichage de la vue chronologique", "unable_to_update_user": "Impossible de mettre à jour l'utilisateur", - "unable_to_upload_file": "Impossible d'envoyer le fichier" + "unable_to_upload_file": "Impossible de tÊlÊverser le fichier" }, - "exif": "Exif", "exif_bottom_sheet_description": "Ajouter une description...", "exif_bottom_sheet_details": "DÉTAILS", "exif_bottom_sheet_location": "LOCALISATION", "exif_bottom_sheet_people": "PERSONNES", "exif_bottom_sheet_person_add_person": "Ajouter un nom", - "exif_bottom_sheet_person_age": "Âge {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Âge {age}", + "exif_bottom_sheet_person_age_months": "Âge {months} mois", + "exif_bottom_sheet_person_age_year_months": "Âge 1 an, {months} mois", + "exif_bottom_sheet_person_age_years": "Âge {years}", "exit_slideshow": "Quitter le diaporama", "expand_all": "Tout dÊvelopper", "experimental_settings_new_asset_list_subtitle": "En cours de dÊveloppement", "experimental_settings_new_asset_list_title": "Activer la grille de photos expÊrimentale", - "experimental_settings_subtitle": "Utilisez à vos dÊpends!", + "experimental_settings_subtitle": "Utilisez à vos dÊpends !", "experimental_settings_title": "ExpÊrimental", "expire_after": "Expire", "expired": "ExpirÊ", @@ -970,15 +980,15 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", - "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", "external_network": "RÊseau externe", - "external_network_sheet_info": "Quand vous n'ÃĒtes pas connectÊ à votre rÊseau prÊfÊrÊ, l'application va tenter de se connecter aux adresses ci-dessous, en commençant par la première", + "external_network_sheet_info": "Quand vous n'ÃĒtes pas connectÊ(e) à votre rÊseau wifi prÊfÊrÊ, l'application va tenter de se connecter aux adresses ci-dessous, en commençant par la première", "face_unassigned": "Non attribuÊ", "failed": "Échec", + "failed_to_authenticate": "Échec de l'authentification", "failed_to_load_assets": "Échec du chargement des ressources", - "failed_to_load_folder": "Impossible d'ouvrir le dossier", + "failed_to_load_folder": "Échec de chargement du dossier", "favorite": "Favori", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", @@ -992,6 +1002,7 @@ "filetype": "Type de fichier", "filter": "Filtres", "filter_people": "Filtrer les personnes", + "filter_places": "Filtrer par lieu", "find_them_fast": "Pour les retrouver rapidement par leur nom", "fix_incorrect_match": "Corriger une association incorrecte", "folder": "Dossier", @@ -1001,12 +1012,12 @@ "forward": "Avant", "general": "GÊnÊral", "get_help": "Obtenir de l'aide", - "get_wifiname_error": "Impossible d'obtenir le nom du rÊseau Wi-Fi. Assurez-vous d'avoir donnÊ les permissions nÊcessaires à l'application et que vous ÃĒtes connectÊ à un rÊseau Wi-Fi.", + "get_wifiname_error": "Impossible d'obtenir le nom du rÊseau wifi. Assurez-vous d'avoir donnÊ les permissions nÊcessaires à l'application et que vous ÃĒtes connectÊ à un rÊseau wifi", "getting_started": "Commencer", "go_back": "Retour", "go_to_folder": "Dossier", "go_to_search": "Faire une recherche", - "grant_permission": "Accorder les permissions ", + "grant_permission": "Accorder les permissions", "group_albums_by": "Grouper les albums par...", "group_country": "Grouper par pays", "group_no": "Pas de groupe", @@ -1030,24 +1041,25 @@ "hide_person": "Masquer la personne", "hide_unnamed_people": "Cacher les personnes non nommÊes", "home_page_add_to_album_conflicts": "{added} ÊlÊments ajoutÊs à l'album {album}. Les ÊlÊments {failed} sont dÊjà dans l'album.", - "home_page_add_to_album_err_local": "Impossible d'ajouter des ÊlÊments locaux aux albums pour le moment, Êtape ignorÊe", + "home_page_add_to_album_err_local": "Impossible d'ajouter des mÊdias locaux aux albums, ils sont ignorÊs", "home_page_add_to_album_success": "{added} ÊlÊments ajoutÊs à l'album {album}.", - "home_page_album_err_partner": "Il n'est pas encore possible d'ajouter des ÊlÊments d'un partenaire à un album.", - "home_page_archive_err_local": "Impossible d'archiver les ressources locales pour l'instant, Êtape ignorÊe", - "home_page_archive_err_partner": "Impossible d'archiver les ÊlÊments d'un partenaire.", + "home_page_album_err_partner": "Impossible d'ajouter des mÊdias d'un partenaire à un album, ils sont ignorÊs", + "home_page_archive_err_local": "Impossible d'archiver les mÊdias locaux, ils sont ignorÊs", + "home_page_archive_err_partner": "Impossible d'archiver les mÊdias d'un partenaire, ils sont ignorÊs", "home_page_building_timeline": "Construction de la chronologie", - "home_page_delete_err_partner": "Ne peut pas supprimer les ÊlÊments d'un partenaire.", - "home_page_delete_remote_err_local": "Des ÊlÊments locaux sont dans la sÊlection de suppression à distance, ils sont donc ignorÊs.", - "home_page_favorite_err_local": "Impossible d'ajouter des ÊlÊments locaux aux favoris pour le moment, Êtape ignorÊe", - "home_page_favorite_err_partner": "Il n'est pas encore possible de mettre en favori les ÊlÊments d'un partenaire.", - "home_page_first_time_notice": "Si c'est la première fois que vous utilisez l'application, veillez à choisir un ou plusieurs albums de sauvegarde afin que la chronologie puisse alimenter les photos et les vidÊos de cet ou ces albums.", - "home_page_share_err_local": "Impossible de partager par lien les mÊdias locaux, cette opÊration est donc ignorÊe.", - "home_page_upload_err_limit": "Limite de tÊlÊchargement de 30 ÊlÊments en mÃĒme temps, demande ignorÊe", + "home_page_delete_err_partner": "Impossible de supprimer les mÊdias d'un partenaire, ils sont ignorÊs", + "home_page_delete_remote_err_local": "Des mÊdias locaux sont dans la sÊlection de suppression à distance, ils sont ignorÊs", + "home_page_favorite_err_local": "Impossible d'ajouter des mÊdias locaux aux favoris, ils sont ignorÊs", + "home_page_favorite_err_partner": "Impossible de mettre en favori les mÊdias d'un partenaire, ils sont ignorÊs", + "home_page_first_time_notice": "Si c'est la première fois que vous utilisez l'application, veillez à choisir un ou plusieurs albums de sauvegarde afin que la chronologie puisse alimenter les photos et les vidÊos de cet ou ces albums", + "home_page_locked_error_local": "Impossible de dÊplacer l'objet vers le dossier verrouillÊ, passer", + "home_page_locked_error_partner": "Impossible de dÊplacer l'objet du collaborateur vers le dossier verrouillÊ, opÊration ignorÊe", + "home_page_share_err_local": "Impossible de partager par lien les mÊdias locaux, ils sont ignorÊs", + "home_page_upload_err_limit": "Impossible de tÊlÊverser plus de 30 mÊdias en mÃĒme temps, demande ignorÊe", "host": "Hôte", "hour": "Heure", "ignore_icloud_photos": "Ignorer les photos iCloud", - "ignore_icloud_photos_description": "Les photos stockÊes sur iCloud ne sont pas enregistrÊes sur Immich", - "image": "Image", + "ignore_icloud_photos_description": "Les photos stockÊes sur iCloud ne sont pas tÊlÊversÊes sur le serveur Immich", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} prise le {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} prise avec {person1} le {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} prise avec {person1} et {person2} le {date}", @@ -1095,7 +1107,6 @@ "language_setting_description": "SÊlectionnez votre langue prÊfÊrÊe", "last_seen": "Dernièrement utilisÊ", "latest_version": "Dernière version", - "latitude": "Latitude", "leave": "Quitter", "lens_model": "Modèle d'objectif", "let_others_respond": "Laisser les autres rÊagir", @@ -1119,20 +1130,22 @@ "loading_search_results_failed": "Chargement des rÊsultats ÊchouÊ", "local_network": "RÊseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connectÊ à ce rÊseau Wi-Fi", - "location_permission": "Autorisation de localisation ", - "location_permission_content": "Afin de pouvoir changer d'adresse automatiquement, Immich doit avoir accès à la localisation prÊcise, afin d'accÊder au le nom du rÊseau Wi-Fi utilisÊ", + "location_permission": "Autorisation de localisation", + "location_permission_content": "Afin de pouvoir changer d'adresse automatiquement, Immich doit avoir accès à la localisation prÊcise, afin d'accÊder au nom du rÊseau wifi utilisÊ", "location_picker_choose_on_map": "SÊlectionner sur la carte", "location_picker_latitude_error": "Saisir une latitude correcte", "location_picker_latitude_hint": "Saisir la latitude ici", "location_picker_longitude_error": "Saisir une longitude correcte", "location_picker_longitude_hint": "Saisir la longitude ici", + "lock": "Verrouiller", + "locked_folder": "Dossier verrouillÊ", "log_out": "Se dÊconnecter", "log_out_all_devices": "DÊconnecter tous les appareils", "logged_out_all_devices": "DÊconnectÊ de tous les appareils", "logged_out_device": "DÊconnectÊ de l'appareil", "login": "Connexion", - "login_disabled": "La connexion a ÊtÊ dÊsactivÊe ", - "login_form_api_exception": "Erreur de l'API. Veuillez vÊrifier l'URL du serveur et et rÊessayer.", + "login_disabled": "La connexion a ÊtÊ dÊsactivÊe", + "login_form_api_exception": "Erreur de l'API. Veuillez vÊrifier l'URL du serveur et rÊessayer.", "login_form_back_button_text": "Retour", "login_form_email_hint": "votrecourriel@email.com", "login_form_endpoint_hint": "http://adresse-ip-serveur:port", @@ -1155,7 +1168,6 @@ "login_password_changed_success": "Mot de passe mis à jour avec succès", "logout_all_device_confirmation": "Êtes-vous sÃģr de vouloir dÊconnecter tous les appareils ?", "logout_this_device_confirmation": "Êtes-vous sÃģr de vouloir dÊconnecter cet appareil ?", - "longitude": "Longitude", "look": "Regarder", "loop_videos": "VidÊos en boucle", "loop_videos_description": "Activer pour voir la vidÊo en boucle dans le lecteur dÊtaillÊ.", @@ -1170,30 +1182,31 @@ "manage_your_devices": "GÊrer vos appareils", "manage_your_oauth_connection": "GÊrer votre connexion OAuth", "map": "Carte", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", "map_cannot_get_user_location": "Impossible d'obtenir la localisation de l'utilisateur", "map_location_dialog_yes": "Oui", "map_location_picker_page_use_location": "Utiliser ma position", - "map_location_service_disabled_content": "Le service de localisation doit ÃĒtre activÊ pour afficher les ÊlÊments de votre emplacement actuel. Souhaitez-vous l'activer maintenant?", + "map_location_service_disabled_content": "Le service de localisation doit ÃĒtre activÊ pour afficher les mÊdias de votre emplacement actuel. Souhaitez-vous l'activer maintenant ?", "map_location_service_disabled_title": "Service de localisation dÊsactivÊ", "map_marker_for_images": "Marqueur de carte pour les images prises à {city}, {country}", "map_marker_with_image": "Marqueur de carte avec image", "map_no_assets_in_bounds": "Pas de photos dans cette zone", - "map_no_location_permission_content": "L'autorisation de localisation est nÊcessaire pour afficher les ÊlÊments de votre emplacement actuel. Souhaitez-vous l'autoriser maintenant?", + "map_no_location_permission_content": "L'autorisation de localisation est nÊcessaire pour afficher les mÊdias de votre emplacement actuel. Souhaitez-vous l'autoriser maintenant ?", "map_no_location_permission_title": "Permission de localisation refusÊe", "map_settings": "Paramètres de la carte", "map_settings_dark_mode": "Mode sombre", "map_settings_date_range_option_day": "Dernières 24 heures", - "map_settings_date_range_option_days": "{} derniers jours", + "map_settings_date_range_option_days": "{days} derniers jours", "map_settings_date_range_option_year": "AnnÊe passÊe", - "map_settings_date_range_option_years": "{} dernières annÊes", + "map_settings_date_range_option_years": "{years} dernières annÊes", "map_settings_dialog_title": "Paramètres de la carte", "map_settings_include_show_archived": "Inclure les archives", "map_settings_include_show_partners": "Inclure les partenaires", "map_settings_only_show_favorites": "Afficher uniquement les favoris", "map_settings_theme_settings": "Thème de la carte", "map_zoom_to_see_photos": "DÊzoomer pour voir les photos", + "mark_all_as_read": "Tout marquer comme lu", + "mark_as_read": "Marquer comme lu", + "marked_all_as_read": "Tout a ÊtÊ marquÊ comme lu", "matches": "Correspondances", "media_type": "Type de mÊdia", "memories": "Souvenirs", @@ -1202,11 +1215,8 @@ "memories_setting_description": "GÊrer ce que vous voyez dans vos souvenirs", "memories_start_over": "Recommencer", "memories_swipe_to_close": "Balayez vers le haut pour fermer", - "memories_year_ago": "Il y a un an", - "memories_years_ago": "Il y a {} ans", "memory": "Souvenir", "memory_lane_title": "Fil de souvenirs {title}", - "menu": "Menu", "merge": "Fusionner", "merge_people": "Fusionner les personnes", "merge_people_limit": "Vous pouvez seulement fusionner 5 visages à la fois", @@ -1214,26 +1224,32 @@ "merge_people_successfully": "Fusion des personnes rÊussie", "merged_people_count": "{count, plural, one {# personne fusionnÊe} other {# personnes fusionnÊes}}", "minimize": "RÊduire", - "minute": "Minute", "missing": "Manquant", "model": "Modèle", "month": "Mois", - "monthly_title_text_date_format": "MMMM y", "more": "Plus", + "move": "DÊplacer", + "move_off_locked_folder": "DÊplacer en dehors du dossier verrouillÊ", + "move_to_locked_folder": "DÊplacer dans le dossier verrouillÊ", + "move_to_locked_folder_confirmation": "Ces photos et vidÊos seront retirÊs de tout les albums et ne seront visibles que dans le dossier verrouillÊ", + "moved_to_archive": "{count, plural, one {# ÊlÊment dÊplacÊ} other {# ÊlÊments dÊplacÊs}} vers les archives", + "moved_to_library": "{count, plural, one {# ÊlÊment dÊplacÊ} other {# ÊlÊments dÊplacÊs}} vers la bibliothèque", "moved_to_trash": "DÊplacÊ dans la corbeille", - "multiselect_grid_edit_date_time_err_read_only": "Impossible de modifier la date d'un ÊlÊment d'actif en lecture seule.", - "multiselect_grid_edit_gps_err_read_only": "Impossible de modifier l'emplacement d'un ÊlÊment en lecture seule.", + "multiselect_grid_edit_date_time_err_read_only": "Impossible de modifier la date de mÊdias en lecture seule, ils sont ignorÊs", + "multiselect_grid_edit_gps_err_read_only": "Impossible de modifier l'emplacement de mÊdias en lecture seule, ils sont ignorÊs", "mute_memories": "Mettre en sourdine les souvenirs", "my_albums": "Mes albums", "name": "Nom", "name_or_nickname": "Nom ou surnom", - "networking_settings": "RÊseau ", + "networking_settings": "RÊseau", "networking_subtitle": "GÊrer les adresses du serveur", "never": "Jamais", "new_album": "Nouvel Album", "new_api_key": "Nouvelle clÊ API", "new_password": "Nouveau mot de passe", "new_person": "Nouvelle personne", + "new_pin_code": "Nouveau code PIN", + "new_pin_code_subtitle": "C'est votre premier accès au dossier verrouillÊ. CrÊez un code PIN pour sÊcuriser l'accès à cette page", "new_user_created": "Nouvel utilisateur crÊÊ", "new_version_available": "NOUVELLE VERSION DISPONIBLE", "newest_first": "RÊcents en premier", @@ -1244,35 +1260,35 @@ "no_albums_with_name_yet": "Il semble que vous n'ayez pas encore d'albums avec ce nom.", "no_albums_yet": "Il semble que vous n'ayez pas encore d'album.", "no_archived_assets_message": "Archiver des photos et vidÊos pour les masquer dans votre bibliothèque", - "no_assets_message": "CLIQUER ICI POUR ENVOYER VOTRE PREMIÈRE PHOTO", + "no_assets_message": "CLIQUER ICI POUR TÉLÉVERSER VOTRE PREMIÈRE PHOTO", "no_assets_to_show": "Aucun ÊlÊment à afficher", "no_duplicates_found": "Aucun doublon n'a ÊtÊ trouvÊ.", "no_exif_info_available": "Aucune information exif disponible", - "no_explore_results_message": "Envoyez plus de photos pour explorer votre collection.", + "no_explore_results_message": "TÊlÊversez plus de photos pour explorer votre collection.", "no_favorites_message": "Ajouter des photos et vidÊos à vos favoris pour les retrouver plus rapidement", "no_libraries_message": "CrÊer une bibliothèque externe pour voir vos photos et vidÊos dans un autre espace de stockage", + "no_locked_photos_message": "Les photos et vidÊos du dossier verrouillÊ sont masquÊs et ne s'afficheront pas dans votre galerie.", "no_name": "Pas de nom", + "no_notifications": "Pas de notification", + "no_people_found": "Aucune personne correspondante trouvÊe", "no_places": "Pas de lieu", "no_results": "Aucun rÊsultat", "no_results_description": "Essayez un synonyme ou un mot-clÊ plus gÊnÊral", "no_shared_albums_message": "CrÊer un album pour partager vos photos et vidÊos avec les personnes de votre rÊseau", "not_in_any_album": "Dans aucun album", "not_selected": "Non sÊlectionnÊ", - "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'Êtiquette de stockage aux mÊdias dÊjà envoyÊs, lancer la", - "notes": "Notes", + "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'Êtiquette de stockage aux mÊdias dÊjà tÊlÊversÊs, exÊcutez", + "nothing_here_yet": "Rien pour le moment", "notification_permission_dialog_content": "Pour activer les notifications, allez dans Paramètres et sÊlectionnez Autoriser.", "notification_permission_list_tile_content": "Accordez la permission d'activer les notifications.", "notification_permission_list_tile_enable_button": "Activer les notifications", "notification_permission_list_tile_title": "Permission de notification", "notification_toggle_setting_description": "Activer les notifications par courriel", - "notifications": "Notifications", "notifications_setting_description": "GÊrer les notifications", - "oauth": "OAuth", "official_immich_resources": "Ressources Immich officielles", "offline": "Hors ligne", "offline_paths": "Chemins hors ligne", "offline_paths_description": "Ces rÊsultats peuvent ÃĒtre causÊs par la suppression manuelle de fichiers qui n'Êtaient pas dans une bibliothèque externe.", - "ok": "Ok", "oldest_first": "Anciens en premier", "on_this_device": "Sur cet appareil", "onboarding": "Accueil", @@ -1282,13 +1298,12 @@ "onboarding_welcome_user": "Bienvenue {user}", "online": "En ligne", "only_favorites": "Uniquement les favoris", + "open": "Ouvrir", "open_in_map_view": "Montrer sur la carte", "open_in_openstreetmap": "Ouvrir dans OpenStreetMap", "open_the_search_filters": "Ouvrir les filtres de recherche", - "options": "Options", "or": "ou", "organize_your_library": "Organiser votre bibliothèque", - "original": "original", "other": "Autre", "other_devices": "Autres appareils", "other_variables": "Autres variables", @@ -1305,7 +1320,7 @@ "partner_page_partner_add_failed": "Échec de l'ajout d'un partenaire", "partner_page_select_partner": "SÊlectionner un partenaire", "partner_page_shared_to_title": "PartagÊ avec", - "partner_page_stop_sharing_content": "{} ne pourra plus accÊder à vos photos.", + "partner_page_stop_sharing_content": "{partner} ne pourra plus accÊder à vos photos.", "partner_sharing": "Partage avec les partenaires", "partners": "Partenaires", "password": "Mot de passe", @@ -1319,7 +1334,6 @@ }, "path": "Chemin", "pattern": "SchÊma", - "pause": "Pause", "pause_memories": "Mettre en pause les souvenirs", "paused": "En pause", "pending": "En attente", @@ -1339,26 +1353,28 @@ "permission_onboarding_get_started": "Commencer", "permission_onboarding_go_to_settings": "AccÊder aux paramètres", "permission_onboarding_permission_denied": "Permission refusÊe. Pour utiliser Immich, accordez lautorisation pour les photos et vidÊos dans les Paramètres.", - "permission_onboarding_permission_granted": "Permission accordÊe! Vous ÃĒtes prÃĒts.", + "permission_onboarding_permission_granted": "Permission accordÊe ! Vous ÃĒtes prÃĒts.", "permission_onboarding_permission_limited": "Permission limitÊe. Pour permettre à Immich de sauvegarder et de gÊrer l'ensemble de votre bibliothèque, accordez l'autorisation pour les photos et vidÊos dans les Paramètres.", - "permission_onboarding_request": "Immich demande l'autorisation de visionner vos photos et vidÊo", + "permission_onboarding_request": "Immich nÊcessite l'autorisation d'accÊder à vos photos et vidÊos.", "person": "Personne", "person_birthdate": "NÊ(e) le {date}", "person_hidden": "{name}{hidden, select, true { (cachÊ)} other {}}", "photo_shared_all_users": "Il semble que vous ayez partagÊ vos photos avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec qui les partager.", - "photos": "Photos", "photos_and_videos": "Photos et vidÊos", - "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos des annÊes prÊcÊdentes", "pick_a_location": "Choisissez un lieu", + "pin_code_changed_successfully": "Code PIN changÊ avec succès", + "pin_code_reset_successfully": "RÊinitialisation du code PIN rÊussie", + "pin_code_setup_successfully": "DÊfinition du code PIN rÊussie", + "pin_verification": "VÊrification du code PIN", "place": "Lieu", "places": "Lieux", "places_count": "{count, plural, one {{count, number} Lieu} other {{count, number} Lieux}}", - "play": "Jouer", + "play": "Lancer", "play_memories": "Lancer les souvenirs", "play_motion_photo": "Jouer la photo animÊe", - "play_or_pause_video": "Jouer ou mettre en pause la vidÊo", - "port": "Port", + "play_or_pause_video": "Lancer ou mettre en pause la vidÊo", + "please_auth_to_access": "Merci de vous authentifier pour accÊder", "preferences_settings_subtitle": "GÊrer les prÊfÊrences de l'application", "preferences_settings_title": "PrÊfÊrences", "preset": "PrÊrÊglage", @@ -1372,7 +1388,6 @@ "profile_drawer_client_out_of_date_major": "L'application mobile est obsolète. Veuillez effectuer la mise à jour vers la dernière version majeure.", "profile_drawer_client_out_of_date_minor": "L'application mobile est obsolète. Veuillez effectuer la mise à jour vers la dernière version mineure.", "profile_drawer_client_server_up_to_date": "Le client et le serveur sont à jour", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version majeure.", "profile_drawer_server_out_of_date_minor": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version mineure.", "profile_image_of_user": "Image de profil de {user}", @@ -1381,7 +1396,7 @@ "public_share": "Partage public", "purchase_account_info": "Contributeur", "purchase_activated_subtitle": "Merci d'avoir apportÊ votre soutien à Immich et aux logiciels open source", - "purchase_activated_time": "ActivÊ le {date, date}", + "purchase_activated_time": "ActivÊ le {date}", "purchase_activated_title": "Votre clÊ a ÊtÊ activÊe avec succès", "purchase_button_activate": "Activer", "purchase_button_buy": "Acheter", @@ -1390,7 +1405,7 @@ "purchase_button_reminder": "Me le rappeler dans 30 jours", "purchase_button_remove_key": "Supprimer la clÊ", "purchase_button_select": "SÊlectionner", - "purchase_failed_activation": "Erreur à l'activation. Veuillez vÊrifier votre courriel pour obtenir la clÊ du produit correcte !", + "purchase_failed_activation": "Échec de l'activation. Veuillez vÊrifier votre courriel pour obtenir la clÊ correcte du produit !", "purchase_individual_description_1": "Pour un utilisateur", "purchase_individual_description_2": "Statut de contributeur", "purchase_individual_title": "Utilisateur", @@ -1426,17 +1441,19 @@ "recent_searches": "Recherches rÊcentes", "recently_added": "RÊcemment ajoutÊ", "recently_added_page_title": "RÊcemment ajoutÊ", + "recently_taken": "RÊcemment photographiÊ", + "recently_taken_page_title": "RÊcemment photographiÊ", "refresh": "Actualiser", "refresh_encoded_videos": "Actualiser les vidÊos encodÊes", "refresh_faces": "Actualiser les visages", "refresh_metadata": "Actualiser les mÊtadonnÊes", - "refresh_thumbnails": "Actualiser les vignettes", + "refresh_thumbnails": "Actualiser les miniatures", "refreshed": "ActualisÊ", "refreshes_every_file": "Actualise tous les fichiers (existants et nouveaux)", "refreshing_encoded_video": "Actualisation de la vidÊo encodÊe", "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des mÊtadonnÊes", - "regenerating_thumbnails": "RegÊnÊration des vignettes", + "regenerating_thumbnails": "RegÊnÊration des miniatures", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sÃģr de vouloir supprimer {count, plural, one {# mÊdia} other {# mÊdias}} de l'album ?", "remove_assets_shared_link_confirmation": "Êtes-vous sÃģr de vouloir supprimer {count, plural, one {# mÊdia} other {# mÊdias}} de ce lien partagÊ ?", @@ -1445,6 +1462,8 @@ "remove_deleted_assets": "Supprimer les fichiers hors ligne", "remove_from_album": "Supprimer de l'album", "remove_from_favorites": "Supprimer des favoris", + "remove_from_locked_folder": "Supprimer du dossier verrouillÊ", + "remove_from_locked_folder_confirmation": "Êtes vous sÃģr de vouloir dÊplacer ces photos et vidÊos en dehors du dossier verrouillÊ ? Elles seront visibles dans votre galerie", "remove_from_shared_link": "Supprimer des liens partagÊs", "remove_memory": "Supprimer le souvenir", "remove_photo_from_memory": "Supprimer la photo de ce souvenir", @@ -1460,7 +1479,7 @@ "rename": "Renommer", "repair": "RÊparer", "repair_no_results_message": "Les fichiers non importÊs ou absents s'afficheront ici", - "replace_with_upload": "Remplacer par tÊlÊchargement", + "replace_with_upload": "Remplacer par tÊlÊversement", "repository": "DÊpôt", "require_password": "Demander le mot de passe", "require_user_to_change_password_on_first_login": "Demander à l'utilisateur de changer son mot de passe lors de sa première connexion", @@ -1468,6 +1487,7 @@ "reset": "RÊinitialiser", "reset_password": "RÊinitialiser le mot de passe", "reset_people_visibility": "RÊinitialiser la visibilitÊ des personnes", + "reset_pin_code": "RÊinitialiser le code PIN", "reset_to_default": "RÊtablir les valeurs par dÊfaut", "resolve_duplicates": "RÊsoudre les doublons", "resolved_all_duplicates": "RÊsolution de tous les doublons", @@ -1476,7 +1496,7 @@ "restore_user": "Restaurer l'utilisateur", "restored_asset": "MÊdia restaurÊ", "resume": "Reprendre", - "retry_upload": "RÊessayer l'envoi", + "retry_upload": "RÊessayer le tÊlÊversement", "review_duplicates": "Consulter les doublons", "role": "Rôle", "role_editor": "Éditeur", @@ -1505,7 +1525,6 @@ "search_country": "Rechercher par pays...", "search_filter_apply": "Appliquer le filtre", "search_filter_camera_title": "SÊlectionner le type d'appareil", - "search_filter_date": "Date", "search_filter_date_interval": "{start} à {end}", "search_filter_date_title": "SÊlectionner une pÊriode", "search_filter_display_option_not_in_album": "Pas dans un album", @@ -1518,7 +1537,7 @@ "search_filter_people_title": "SÊlectionner une personne", "search_for": "Chercher", "search_for_existing_person": "Rechercher une personne existante", - "search_no_more_result": "\nPlus de rÊsultats", + "search_no_more_result": "Plus de rÊsultats", "search_no_people": "Aucune personne", "search_no_people_named": "Aucune personne nommÊe ÂĢ {name} Âģ", "search_no_result": "Aucun rÊsultat trouvÊ, essayez un autre terme de recherche ou une autre combinaison", @@ -1529,7 +1548,7 @@ "search_page_no_places": "Aucune information disponible sur la localisation", "search_page_screenshots": "Captures d'Êcran", "search_page_search_photos_videos": "Rechercher dans vos photos et vidÊos", - "search_page_selfies": "Selfies", + "search_page_selfies": "Autoportraits (Selfies)", "search_page_things": "Objets", "search_page_view_all_button": "Voir tout", "search_page_your_activity": "Votre activitÊ", @@ -1540,7 +1559,7 @@ "search_result_page_new_search_hint": "Nouvelle recherche", "search_settings": "Paramètres de recherche", "search_state": "Rechercher par Êtat/rÊgion...", - "search_suggestion_list_smart_search_hint_1": "La recherche intelligente est activÊe par dÊfaut. Pour rechercher des mÊtadonnÊes, utilisez la syntaxe suivante", + "search_suggestion_list_smart_search_hint_1": "La recherche intelligente est activÊe par dÊfaut. Pour rechercher des mÊtadonnÊes, utilisez la syntaxe suivante ", "search_suggestion_list_smart_search_hint_2": "m:votre-terme-de-recherche", "search_tags": "Recherche d'Êtiquettes...", "search_timezone": "Rechercher par fuseau horaire...", @@ -1560,6 +1579,7 @@ "select_keep_all": "Choisir de tout garder", "select_library_owner": "SÊlectionner le propriÊtaire de la bibliothèque", "select_new_face": "SÊlectionner un nouveau visage", + "select_person_to_tag": "SÊlectionner une personne à identifier", "select_photos": "SÊlectionner les photos", "select_trash_all": "Choisir de tout supprimer", "select_user_for_sharing_page_err_album": "Échec de la crÊation de l'album", @@ -1581,25 +1601,23 @@ "set_date_of_birth": "Changer la date de naissance", "set_profile_picture": "DÊfinir la photo de profil", "set_slideshow_to_fullscreen": "Afficher le diaporama en plein Êcran", - "setting_image_viewer_help": "Le visualiseur de dÊtails charge d'abord la petite vignette, puis l'aperçu de taille moyenne (s'il est activÊ), enfin l'original (s'il est activÊ).", - "setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image en rÊsolution originale (volumineux!). DÊsactiver pour rÊduire l'utilisation des donnÊes (rÊseau et cache de l'appareil).", + "setting_image_viewer_help": "Le visualiseur de dÊtails charge d'abord la petite miniature, puis l'aperçu de taille moyenne (s'il est activÊ), enfin l'original (s'il est activÊ).", + "setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image en rÊsolution originale (fichier volumineux !). DÊsactiver pour rÊduire l'utilisation des donnÊes (rÊseau et cache de l'appareil).", "setting_image_viewer_original_title": "Charger l'image originale", - "setting_image_viewer_preview_subtitle": "Activer pour charger une image de rÊsolution moyenne. DÊsactiver pour charger directement l'original ou utiliser uniquement la vignette.", + "setting_image_viewer_preview_subtitle": "Activer pour charger une image de rÊsolution moyenne. DÊsactiver pour charger directement l'original ou utiliser uniquement la miniature.", "setting_image_viewer_preview_title": "Charger l'image d'aperçu", - "setting_image_viewer_title": "Images", "setting_languages_apply": "Appliquer", "setting_languages_subtitle": "Changer la langue de l'application", "setting_languages_title": "Langues", - "setting_notifications_notify_failures_grace_period": "Notifier les Êchecs de la sauvegarde en arrière-plan: {}", - "setting_notifications_notify_hours": "{} heures", + "setting_notifications_notify_failures_grace_period": "Notifier les Êchecs de la sauvegarde en arrière-plan : {duration}", + "setting_notifications_notify_hours": "{count} heures", "setting_notifications_notify_immediately": "immÊdiatement", - "setting_notifications_notify_minutes": "{} minutes", "setting_notifications_notify_never": "jamais", - "setting_notifications_notify_seconds": "{} secondes", - "setting_notifications_single_progress_subtitle": "Informations dÊtaillÊes sur la progression du transfert par ÊlÊment", + "setting_notifications_notify_seconds": "{count} secondes", + "setting_notifications_single_progress_subtitle": "Informations dÊtaillÊes sur la progression du tÊlÊversement par mÊdia", "setting_notifications_single_progress_title": "Afficher la progression du dÊtail de la sauvegarde en arrière-plan", "setting_notifications_subtitle": "Ajustez vos prÊfÊrences de notification", - "setting_notifications_total_progress_subtitle": "Progression globale du transfert (effectuÊ/total des ÊlÊments)", + "setting_notifications_total_progress_subtitle": "Progression globale du tÊlÊversement (effectuÊ/total des mÊdias)", "setting_notifications_total_progress_title": "Afficher la progression totale de la sauvegarde en arrière-plan", "setting_video_viewer_looping_title": "Boucle", "setting_video_viewer_original_video_subtitle": "Lors de la diffusion d'une vidÊo depuis le serveur, lisez l'original mÃĒme si un transcodage est disponible. Cela peut entraÃŽner de la mise en mÊmoire tampon. Les vidÊos disponibles localement sont lues en qualitÊ d'origine, quel que soit ce paramètre.", @@ -1607,13 +1625,15 @@ "settings": "Paramètres", "settings_require_restart": "Veuillez redÊmarrer Immich pour appliquer ce paramètre", "settings_saved": "Paramètres sauvegardÊs", + "setup_pin_code": "DÊfinir un code PIN", "share": "Partager", "share_add_photos": "Ajouter des photos", - "share_assets_selected": "{} sÊlÊctionnÊ(s)", + "share_assets_selected": "{count} sÊlectionnÊ(s)", "share_dialog_preparing": "PrÊparation...", + "share_link": "Partager le lien", "shared": "PartagÊ", "shared_album_activities_input_disable": "Les commentaires sont dÊsactivÊs", - "shared_album_activity_remove_content": "Souhaitez-vous supprimer cette activitÊ?", + "shared_album_activity_remove_content": "Souhaitez-vous supprimer cette activitÊ ?", "shared_album_activity_remove_title": "Supprimer l'activitÊ", "shared_album_section_people_action_error": "Erreur lors de la suppression", "shared_album_section_people_action_leave": "Supprimer l'utilisateur de l'album", @@ -1623,34 +1643,31 @@ "shared_by_user": "PartagÊ par {user}", "shared_by_you": "PartagÊ par vous", "shared_from_partner": "Photos de {partner}", - "shared_intent_upload_button_progress_text": "{} / {} TÊlÊversÊ", + "shared_intent_upload_button_progress_text": "{current} / {total} TÊlÊversÊ(s)", "shared_link_app_bar_title": "Liens partagÊs", - "shared_link_clipboard_copied_massage": "CopiÊ dans le presse-papier\n", - "shared_link_clipboard_text": "\nLien : {}\nMot de passe : {}", + "shared_link_clipboard_copied_massage": "CopiÊ dans le presse-papier", + "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", "shared_link_create_error": "Erreur pendant la crÊation du lien partagÊ", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", - "shared_link_edit_expire_after_option_days": "{} jours", + "shared_link_edit_expire_after_option_days": "{count} jours", "shared_link_edit_expire_after_option_hour": "1 heure", - "shared_link_edit_expire_after_option_hours": "{} heures", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} mois", - "shared_link_edit_expire_after_option_year": "{} ans", + "shared_link_edit_expire_after_option_hours": "{count} heures", + "shared_link_edit_expire_after_option_months": "{count} mois", + "shared_link_edit_expire_after_option_year": "{count} an", "shared_link_edit_password_hint": "Saisir le mot de passe de partage", "shared_link_edit_submit_button": "Mettre à jour le lien", "shared_link_error_server_url_fetch": "Impossible de rÊcupÊrer l'url du serveur", - "shared_link_expires_day": "Expire dans {} jour", - "shared_link_expires_days": "Expire dans {} jours", - "shared_link_expires_hour": "Expire dans {} heure", - "shared_link_expires_hours": "Expire dans {} heures", - "shared_link_expires_minute": "Expire dans {} minute", - "shared_link_expires_minutes": "Expire dans {} minutes", + "shared_link_expires_day": "Expire dans {count} jour", + "shared_link_expires_days": "Expire dans {count} jours", + "shared_link_expires_hour": "Expire dans {count} heure", + "shared_link_expires_hours": "Expire dans {count} heures", + "shared_link_expires_minute": "Expire dans {count} minute", + "shared_link_expires_minutes": "Expire dans {count} minutes", "shared_link_expires_never": "Expire ∞", - "shared_link_expires_second": "Expire dans {} seconde", - "shared_link_expires_seconds": "Expire dans {} secondes", + "shared_link_expires_second": "Expire dans {count} seconde", + "shared_link_expires_seconds": "Expire dans {count} secondes", "shared_link_individual_shared": "PartagÊ individuellement", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "GÊrer les liens partagÊs", "shared_link_options": "Options de lien partagÊ", "shared_links": "Liens partagÊs", @@ -1706,7 +1723,6 @@ "sort_people_by_similarity": "Trier les personnes par similitude", "sort_recent": "Photo la plus rÊcente", "sort_title": "Titre", - "source": "Source", "stack": "Empiler", "stack_duplicates": "Empiler les doublons", "stack_select_one_photo": "SÊlectionnez une photo principale pour la pile", @@ -1723,18 +1739,17 @@ "stop_sharing_photos_with_user": "ArrÃĒter de partager vos photos avec cet utilisateur", "storage": "Stockage", "storage_label": "Étiquette de stockage", + "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisÊ", "submit": "Soumettre", - "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", - "support": "Support", "support_and_feedback": "Support & Retours", "support_third_party_description": "Votre installation d'Immich est packagÊe via une application tierce. Si vous rencontrez des anomalies, elles peuvent venir de ce packaging tiers, merci de crÊer les anomalies avec ces tiers en premier lieu en utilisant les liens ci-dessous.", "swap_merge_direction": "Inverser la direction de fusion", "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", - "sync_albums_manual_subtitle": "Synchroniser toutes les vidÊos et photos sauvegardÊes dans les albums sÊlectionnÊs", - "sync_upload_album_setting_subtitle": "CrÊer et sauvegarde vos photos et vidÊos dans les albums sÊlectionnÊs sur Immich", + "sync_albums_manual_subtitle": "Synchroniser toutes les vidÊos et photos tÊlÊversÊes dans les albums sÊlectionnÊs", + "sync_upload_album_setting_subtitle": "CrÊe et tÊlÊverse vos photos et vidÊos dans les albums sÊlectionnÊs sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les mÊdias", "tag_created": "Étiquette crÊÊe : {tag}", @@ -1749,7 +1764,7 @@ "theme_selection": "SÊlection du thème", "theme_selection_description": "Ajuster automatiquement le thème clair ou sombre via les prÊfÊrences système", "theme_setting_asset_list_storage_indicator_title": "Afficher l'indicateur de stockage sur les tuiles des ÊlÊments", - "theme_setting_asset_list_tiles_per_row_title": "Nombre d'ÊlÊments par ligne ({})", + "theme_setting_asset_list_tiles_per_row_title": "Nombre de mÊdias par ligne ({count})", "theme_setting_colorful_interface_subtitle": "Appliquer la couleur principale sur les surfaces d'arrière-plan.", "theme_setting_colorful_interface_title": "Interface colorÊe", "theme_setting_image_viewer_quality_subtitle": "Ajustez la qualitÊ de la visionneuse d'images dÊtaillÊes", @@ -1759,7 +1774,7 @@ "theme_setting_system_primary_color_title": "Utiliser la couleur du système", "theme_setting_system_theme_switch": "Automatique (suivre les paramètres du système)", "theme_setting_theme_subtitle": "Choisissez le thème de l'application", - "theme_setting_three_stage_loading_subtitle": "Le chargement en trois Êtapes peut amÊliorer les performances de chargement, mais entraÃŽne une augmentation significative de la charge du rÊseau.", + "theme_setting_three_stage_loading_subtitle": "Le chargement en trois Êtapes peut amÊliorer les performances de chargement, mais entraÃŽne une augmentation significative de la charge du rÊseau", "theme_setting_three_stage_loading_title": "Activer le chargement en trois Êtapes", "they_will_be_merged_together": "Elles seront fusionnÊes ensemble", "third_party_resources": "Ressources tierces", @@ -1774,7 +1789,6 @@ "to_trash": "Corbeille", "toggle_settings": "Inverser les paramètres", "toggle_theme": "Inverser le thème sombre", - "total": "Total", "total_usage": "Utilisation globale", "trash": "Corbeille", "trash_all": "Tout supprimer", @@ -1783,14 +1797,15 @@ "trash_emptied": "Corbeille vidÊe", "trash_no_results_message": "Les photos et vidÊos supprimÊes s'afficheront ici.", "trash_page_delete_all": "Tout supprimer", - "trash_page_empty_trash_dialog_content": "Voulez-vous vider les ÊlÊments de la corbeille? Ces objets seront dÊfinitivement retirÊs d'Immich", - "trash_page_info": "Les ÊlÊments mis à la corbeille seront dÊfinitivement supprimÊs au bout de {} jours.", + "trash_page_empty_trash_dialog_content": "Voulez-vous vider les mÊdias de la corbeille ? Ces objets seront dÊfinitivement retirÊs d'Immich", + "trash_page_info": "Les mÊdias mis à la corbeille seront dÊfinitivement supprimÊs au bout de {days} jours", "trash_page_no_assets": "Aucun ÊlÊment dans la corbeille", "trash_page_restore_all": "Tout restaurer", "trash_page_select_assets_btn": "SÊlectionner les ÊlÊments", - "trash_page_title": "Corbeille ({})", + "trash_page_title": "Corbeille ({count})", "trashed_items_will_be_permanently_deleted_after": "Les ÊlÊments dans la corbeille seront supprimÊs dÊfinitivement après {days, plural, one {# jour} other {# jours}}.", - "type": "Type", + "unable_to_change_pin_code": "Impossible de changer le code PIN", + "unable_to_setup_pin_code": "Impossible de dÊfinir le code PIN", "unarchive": "DÊsarchiver", "unarchived_count": "{count, plural, one {# supprimÊ} other {# supprimÊs}} de l'archive", "unfavorite": "Enlever des favoris", @@ -1812,29 +1827,33 @@ "unstack": "DÊsempiler", "unstacked_assets_count": "{count, plural, one {# mÊdia dÊpilÊ} other {# mÊdias dÊpilÊs}}", "untracked_files": "Fichiers non suivis", - "untracked_files_decription": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat de dÊplacements ÊchouÊs, d'envois interrompus ou laissÊs pour compte à cause d'un bug", + "untracked_files_decription": "Ces fichiers ne sont pas suivis par l'application. Ils peuvent ÃĒtre le rÊsultat de dÊplacements ÊchouÊs, de tÊlÊversements interrompus ou abandonnÊs pour cause de bug", "up_next": "Suite", + "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", - "upload": "Envoyer", - "upload_concurrency": "Envois simultanÊs", - "upload_dialog_info": "Voulez-vous sauvegarder la sÊlection vers le serveur?", - "upload_dialog_title": "TÊlÊcharger cet ÊlÊment ", - "upload_errors": "L'envoi s'est achevÊ avec {count, plural, one {# erreur} other {# erreurs}}. RafraÃŽchir la page pour voir les nouveaux mÊdias envoyÊs.", + "upload": "TÊlÊverser", + "upload_concurrency": "TÊlÊversements simultanÊs", + "upload_dialog_info": "Voulez-vous sauvegarder la sÊlection vers le serveur ?", + "upload_dialog_title": "TÊlÊverser le mÊdia", + "upload_errors": "Le tÊlÊversement s'est achevÊ avec {count, plural, one {# erreur} other {# erreurs}}. RafraÃŽchir la page pour voir les nouveaux mÊdias tÊlÊversÊs.", "upload_progress": "{remaining, number} restant(s) - {processed, number} traitÊ(s)/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# doublon ignorÊ} other {# doublons ignorÊs}}", "upload_status_duplicates": "Doublons", "upload_status_errors": "Erreurs", - "upload_status_uploaded": "EnvoyÊ", - "upload_success": "Envoi rÊussi. RafraÃŽchir la page pour voir les nouveaux mÊdias envoyÊs.", - "upload_to_immich": "TÊlÊverser vers Immich ({})", + "upload_status_uploaded": "TÊlÊversÊ", + "upload_success": "TÊlÊversement rÊussi. RafraÃŽchir la page pour voir les nouveaux mÊdias tÊlÊversÊs.", + "upload_to_immich": "TÊlÊverser vers Immich ({count})", "uploading": "TÊlÊversement en cours", - "url": "URL", "usage": "Utilisation", - "use_current_connection": "Utiliser le rÊseau actuel ", + "use_biometric": "Utiliser l'authentification biomÊtrique", + "use_current_connection": "Utiliser le rÊseau actuel", "use_custom_date_range": "Utilisez une plage de date personnalisÊe à la place", "user": "Utilisateur", + "user_has_been_deleted": "Cet utilisateur à ÊtÊ supprimÊ.", "user_id": "ID Utilisateur", "user_liked": "{user} a aimÊ {type, select, photo {cette photo} video {cette vidÊo} asset {ce mÊdia} other {ceci}}", + "user_pin_code_settings": "Code PIN", + "user_pin_code_settings_description": "GÊrer votre code PIN", "user_purchase_settings": "Achat", "user_purchase_settings_description": "GÊrer votre achat", "user_role_set": "DÊfinir {user} comme {role}", @@ -1846,8 +1865,6 @@ "utilities": "Utilitaires", "validate": "Valider", "validate_endpoint_error": "Merci d'entrer un lien valide", - "variables": "Variables", - "version": "Version", "version_announcement_closing": "Ton ami, Alex", "version_announcement_message": "Bonjour, il y a une nouvelle version de l'application. Prenez le temps de consulter les notes de version et assurez vous que votre installation est à jour pour Êviter toute erreur de configuration, surtout si vous utilisez WatchTower ou tout autre mÊcanisme qui gère automatiquement la mise à jour de votre application.", "version_announcement_overlay_release_notes": "notes de mise à jour", @@ -1859,7 +1876,7 @@ "version_history_item": "Version {version} installÊe le {date}", "video": "VidÊo", "video_hover_setting": "Lire la miniature des vidÊos au survol", - "video_hover_setting_description": "Jouer la prÊvisualisation vidÊo au survol. Si dÊsactivÊ, la lecture peut quand mÃĒme ÃĒtre dÊmarrÊe en survolant le bouton Play.", + "video_hover_setting_description": "Lancer la prÊvisualisation vidÊo au survol. Si dÊsactivÊ, la lecture peut quand mÃĒme ÃĒtre dÊmarrÊe en survolant le bouton Play.", "videos": "VidÊos", "videos_count": "{count, plural, one {# VidÊo} other {# VidÊos}}", "view": "Voir", @@ -1883,11 +1900,12 @@ "week": "Semaine", "welcome": "Bienvenue", "welcome_to_immich": "Bienvenue sur Immich", - "wifi_name": "Nom du rÊseau ", + "wifi_name": "Nom du rÊseau wifi", + "wrong_pin_code": "Code PIN erronÊ", "year": "AnnÊe", "years_ago": "Il y a {years, plural, one {# an} other {# ans}}", "yes": "Oui", "you_dont_have_any_shared_links": "Vous n'avez aucun lien partagÊ", - "your_wifi_name": "Nom du rÊseau Wi-Fi ", + "your_wifi_name": "Nom du rÊseau wifi", "zoom_image": "Zoomer" } diff --git a/i18n/gl.json b/i18n/gl.json index 72cf8d540a..af2841481d 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -4,576 +4,1880 @@ "account_settings": "ConfiguraciÃŗn da conta", "acknowledge": "De acordo", "action": "AcciÃŗn", - "action_common_update": "Update", + "action_common_update": "Actualizar", "actions": "AcciÃŗns", "active": "Activo", "activity": "Actividade", - "activity_changed": "A actividade estÃĄ {enabled, select, true {habilitada} other {deshabilitada}}", + "activity_changed": "A actividade estÃĄ {enabled, select, true {activada} other {desactivada}}", "add": "Engadir", "add_a_description": "Engadir unha descriciÃŗn", - "add_a_location": "Engadir unha localizaciÃŗn", + "add_a_location": "Engadir unha ubicaciÃŗn", "add_a_name": "Engadir un nome", "add_a_title": "Engadir un título", - "add_endpoint": "Add endpoint", + "add_endpoint": "Engadir endpoint", "add_exclusion_pattern": "Engadir patrÃŗn de exclusiÃŗn", "add_import_path": "Engadir ruta de importaciÃŗn", - "add_location": "Engadir localizaciÃŗn", + "add_location": "Engadir ubicaciÃŗn", "add_more_users": "Engadir mÃĄis usuarios", - "add_partner": "Engadir compaÃąeiro", + "add_partner": "Engadir compaÃąeiro/a", "add_path": "Engadir ruta", "add_photos": "Engadir fotos", "add_to": "Engadir aâ€Ļ", "add_to_album": "Engadir ao ÃĄlbum", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_added": "Engadido a {album}", + "add_to_album_bottom_sheet_already_exists": "Xa estÃĄ en {album}", + "add_to_locked_folder": "Engadir a carpeta", "add_to_shared_album": "Engadir ao ÃĄlbum compartido", "add_url": "Engadir URL", "added_to_archive": "Engadido ao arquivo", "added_to_favorites": "Engadido a favoritos", - "added_to_favorites_count": "Engadidos {count, number} a favoritos", + "added_to_favorites_count": "Engadido {count, number} a favoritos", "admin": { + "add_exclusion_pattern_description": "Engadir patrÃŗns de exclusiÃŗn. Admítense caracteres comodín usando *, ** e ?. Para ignorar todos os ficheiros en calquera directorio chamado \"Raw\", emprega \"**/Raw/**\". Para ignorar todos os ficheiros que rematen en \".tif\", usa \"**/*.tif\". Para ignorar unha ruta absoluta, emprega \"/ruta/a/ignorar/**\".", + "asset_offline_description": "Este activo da biblioteca externa xa non se atopa no disco e moveuse ao lixo. Se o ficheiro se moveu dentro da biblioteca, comproba a tÃēa liÃąa de tempo para o novo activo correspondente. Para restaurar este activo, asegÃērate de que Immich poida acceder ÃĄ ruta do ficheiro a continuaciÃŗn e escanee a biblioteca.", "authentication_settings": "ConfiguraciÃŗn de autenticaciÃŗn", - "authentication_settings_description": "Xestionar contrasinal, OAuth e outros parÃĄmetros de autenticaciÃŗn", - "authentication_settings_disable_all": "EstÃĄs seguro de deshabilitar todos os mÊtodos de inicio de sesiÃŗn? Iniciar a sesiÃŗn quedarÃĄ completamente deshabilitado.", - "authentication_settings_reenable": "Para rehabilitala, usa un Comando do servidor.", + "authentication_settings_description": "Xestionar contrasinal, OAuth e outras configuraciÃŗns de autenticaciÃŗn", + "authentication_settings_disable_all": "EstÃĄs seguro de que queres desactivar todos os mÊtodos de inicio de sesiÃŗn? O inicio de sesiÃŗn desactivarase completamente.", + "authentication_settings_reenable": "Para reactivalo, use un Comando de servidor.", "background_task_job": "Tarefas en segundo plano", - "backup_database": "Respaldo da base de datos", - "backup_database_enable_description": "Habilitar as copias de seguridade da base de datos", - "backup_keep_last_amount": "Cantidade de copias de seguridade previas a manter", - "backup_settings": "ConfiguraciÃŗn de copias de seguridade", - "backup_settings_description": "Xestionar a configuraciÃŗn das copias de seguridade da base de datos", - "check_all": "Comprobar todo", + "backup_database": "Copia de seguridade da base de datos", + "backup_database_enable_description": "Activar copias de seguridade da base de datos", + "backup_keep_last_amount": "Cantidade de copias de seguridade anteriores a conservar", + "backup_settings": "ConfiguraciÃŗn da copia de seguridade", + "backup_settings_description": "Xestionar a configuraciÃŗn da copia de seguridade da base de datos", + "check_all": "Marcar todo", + "cleanup": "Limpeza", "cleared_jobs": "Traballos borrados para: {job}", - "config_set_by_file": "As configuraciÃŗns estÃĄn actualmente seleccionadas por un ficheiro de configuraciÃŗns", + "config_set_by_file": "A configuraciÃŗn establÊcese actualmente mediante un ficheiro de configuraciÃŗn", "confirm_delete_library": "EstÃĄs seguro de que queres eliminar a biblioteca {library}?", - "exclusion_pattern_description": "Os patrÃŗns de exclusiÃŗn permítenche ignorar ficheiros e cartafoles ao escanear a tÃēa biblioteca. Isto Ê Ãētil se tes cartafoles que conteÃąen ficheiros que non queres importar, coma ficheiros RAW.", + "confirm_delete_library_assets": "EstÃĄs seguro de que queres eliminar esta biblioteca? Isto eliminarÃĄ {count, plural, one {# activo contido} other {todos os # activos contidos}} de Immich e non se pode desfacer. Os ficheiros permanecerÃĄn no disco.", + "confirm_email_below": "Para confirmar, escriba \"{email}\" a continuaciÃŗn", + "confirm_reprocess_all_faces": "EstÃĄs seguro de que queres reprocesar todas as caras? Isto tamÊn borrarÃĄ as persoas nomeadas.", + "confirm_user_password_reset": "EstÃĄs seguro de que queres restablecer o contrasinal de {user}?", + "confirm_user_pin_code_reset": "EstÃĄs seguro de que queres restablecer o PIN de {user}?", + "create_job": "Crear traballo", + "cron_expression": "ExpresiÃŗn Cron", + "cron_expression_description": "Estableza o intervalo de escaneo usando o formato cron. Para obter mÃĄis informaciÃŗn, consulte por exemplo Crontab Guru", + "cron_expression_presets": "Preaxustes de expresiÃŗn Cron", + "disable_login": "Desactivar inicio de sesiÃŗn", + "duplicate_detection_job_description": "Executar aprendizaxe automÃĄtica nos activos para detectar imaxes similares. Depende da Busca Intelixente", + "exclusion_pattern_description": "Os patrÃŗns de exclusiÃŗn permítenche ignorar ficheiros e cartafoles ao escanear a tÃēa biblioteca. Isto Ê Ãētil se tes cartafoles que conteÃąen ficheiros que non queres importar, como ficheiros RAW.", "external_library_created_at": "Biblioteca externa (creada o {date})", - "external_library_management": "XestiÃŗn de bibliotecas externas", + "external_library_management": "XestiÃŗn da biblioteca externa", "face_detection": "DetecciÃŗn de caras", - "job_settings": "ConfiguraciÃŗn de tarefas", - "job_settings_description": "Administrar tarefas simultÃĄneas", - "job_status": "Estado da tarefa", - "jobs_failed": "{jobCount, one {# errado}, plural, other {# errados}}", - "note_cannot_be_changed_later": "NOTA: Non editable posteriormente!", - "notification_email_host_description": "Host do servidor de correo electrÃŗnico (p. ex.: smtp.immich.app)", + "face_detection_description": "Detectar as caras nos activos usando aprendizaxe automÃĄtica. Para vídeos, sÃŗ se considera a miniatura. \"Actualizar\" (re)procesa todos os activos. \"Restablecer\" ademais borra todos os datos de caras actuais. \"Faltantes\" pon en cola os activos que aínda non foron procesados. As caras detectadas poranse en cola para o RecoÃąecemento Facial despois de completar a DetecciÃŗn de Caras, agrupÃĄndoas en persoas existentes ou novas.", + "facial_recognition_job_description": "Agrupar caras detectadas en persoas. Este paso execÃētase despois de completar a DetecciÃŗn de Caras. \"Restablecer\" (re)agrupa todas as caras. \"Faltantes\" pon en cola as caras que non teÃąen unha persoa asignada.", + "failed_job_command": "O comando {command} fallou para o traballo: {job}", + "force_delete_user_warning": "AVISO: Isto eliminarÃĄ inmediatamente o usuario e todos os activos. Isto non se pode desfacer e os ficheiros non se poden recuperar.", + "forcing_refresh_library_files": "Forzando a actualizaciÃŗn de todos os ficheiros da biblioteca", + "image_format": "Formato", + "image_format_description": "WebP produce ficheiros mÃĄis pequenos que JPEG, pero Ê mÃĄis lento de codificar.", + "image_fullsize_description": "Imaxe a tamaÃąo completo con metadatos eliminados, usada ao facer zoom", + "image_fullsize_enabled": "Activar a xeraciÃŗn de imaxes a tamaÃąo completo", + "image_fullsize_enabled_description": "Xerar imaxe a tamaÃąo completo para formatos non compatibles coa web. Cando \"Preferir vista previa incrustada\" estÃĄ activado, as vistas previas incrustadas utilízanse directamente sen conversiÃŗn. Non afecta a formatos compatibles coa web como JPEG.", + "image_fullsize_quality_description": "Calidade da imaxe a tamaÃąo completo de 1 a 100. MÃĄis alto Ê mellor, pero produce ficheiros mÃĄis grandes.", + "image_fullsize_title": "ConfiguraciÃŗn da imaxe a tamaÃąo completo", + "image_prefer_embedded_preview": "Preferir vista previa incrustada", + "image_prefer_embedded_preview_setting_description": "Usar vistas previas incrustadas en fotos RAW como entrada para o procesamento de imaxes e cando estean dispoÃąibles. Isto pode producir cores mÃĄis precisas para algunhas imaxes, pero a calidade da vista previa depende da cÃĄmara e a imaxe pode ter mÃĄis artefactos de compresiÃŗn.", + "image_prefer_wide_gamut": "Preferir gama ampla", + "image_prefer_wide_gamut_setting_description": "Usar Display P3 para as miniaturas. Isto preserva mellor a viveza das imaxes con espazos de cor amplos, pero as imaxes poden aparecer de forma diferente en dispositivos antigos cunha versiÃŗn de navegador antiga. As imaxes sRGB mantÃŠÃąense como sRGB para evitar cambios de cor.", + "image_preview_description": "Imaxe de tamaÃąo medio con metadatos eliminados, usada ao ver un Ãēnico activo e para aprendizaxe automÃĄtica", + "image_preview_quality_description": "Calidade da vista previa de 1 a 100. MÃĄis alto Ê mellor, pero produce ficheiros mÃĄis grandes e pode reducir a capacidade de resposta da aplicaciÃŗn. Establecer un valor baixo pode afectar ÃĄ calidade da aprendizaxe automÃĄtica.", + "image_preview_title": "ConfiguraciÃŗn da vista previa", + "image_quality": "Calidade", + "image_resolution": "ResoluciÃŗn", + "image_resolution_description": "ResoluciÃŗns mÃĄis altas poden preservar mÃĄis detalles pero tardan mÃĄis en codificarse, teÃąen tamaÃąos de ficheiro mÃĄis grandes e poden reducir a capacidade de resposta da aplicaciÃŗn.", + "image_settings": "ConfiguraciÃŗn da imaxe", + "image_settings_description": "Xestionar a calidade e resoluciÃŗn das imaxes xeradas", + "image_thumbnail_description": "Miniatura pequena con metadatos eliminados, usada ao ver grupos de fotos como a liÃąa de tempo principal", + "image_thumbnail_quality_description": "Calidade da miniatura de 1 a 100. MÃĄis alto Ê mellor, pero produce ficheiros mÃĄis grandes e pode reducir a capacidade de resposta da aplicaciÃŗn.", + "image_thumbnail_title": "ConfiguraciÃŗn da miniatura", + "job_concurrency": "concorrencia de {job}", + "job_created": "Traballo creado", + "job_not_concurrency_safe": "Este traballo non Ê seguro para concorrencia.", + "job_settings": "ConfiguraciÃŗn de traballos", + "job_settings_description": "Xestionar a concorrencia de traballos", + "job_status": "Estado do traballo", + "jobs_delayed": "{jobCount, plural, other {# atrasados}}", + "jobs_failed": "{jobCount, plural, other {# fallados}}", + "library_created": "Biblioteca creada: {library}", + "library_deleted": "Biblioteca eliminada", + "library_import_path_description": "Especifique un cartafol para importar. Este cartafol, incluídos os subcartafoles, escanearase en busca de imaxes e vídeos.", + "library_scanning": "Escaneo periÃŗdico", + "library_scanning_description": "Configurar o escaneo periÃŗdico da biblioteca", + "library_scanning_enable_description": "Activar o escaneo periÃŗdico da biblioteca", + "library_settings": "Biblioteca externa", + "library_settings_description": "Xestionar a configuraciÃŗn da biblioteca externa", + "library_tasks_description": "Escanear bibliotecas externas en busca de activos novos e/ou modificados", + "library_watching_enable_description": "Vixiar bibliotecas externas para cambios nos ficheiros", + "library_watching_settings": "Vixilancia da biblioteca (EXPERIMENTAL)", + "library_watching_settings_description": "Vixiar automaticamente os ficheiros modificados", + "logging_enable_description": "Activar rexistro", + "logging_level_description": "Cando estea activado, que nivel de rexistro usar.", + "logging_settings": "Rexistro", + "machine_learning_clip_model": "Modelo CLIP", + "machine_learning_clip_model_description": "O nome dun modelo CLIP listado aquí. Ten en conta que debe volver executar o traballo 'Busca Intelixente' para todas as imaxes ao cambiar un modelo.", + "machine_learning_duplicate_detection": "DetecciÃŗn de duplicados", + "machine_learning_duplicate_detection_enabled": "Activar detecciÃŗn de duplicados", + "machine_learning_duplicate_detection_enabled_description": "Se estÃĄ desactivado, os activos exactamente idÊnticos aínda se eliminarÃĄn duplicados.", + "machine_learning_duplicate_detection_setting_description": "Usar incrustaciÃŗns CLIP para atopar posibles duplicados", + "machine_learning_enabled": "Activar aprendizaxe automÃĄtica", + "machine_learning_enabled_description": "Se estÃĄ desactivado, todas as funciÃŗns de ML desactivaranse independentemente da configuraciÃŗn a continuaciÃŗn.", + "machine_learning_facial_recognition": "RecoÃąecemento facial", + "machine_learning_facial_recognition_description": "Detectar, recoÃąecer e agrupar caras en imaxes", + "machine_learning_facial_recognition_model": "Modelo de recoÃąecemento facial", + "machine_learning_facial_recognition_model_description": "Os modelos estÃĄn listados en orde descendente de tamaÃąo. Os modelos mÃĄis grandes son mÃĄis lentos e usan mÃĄis memoria, pero producen mellores resultados. TeÃąa en conta que debes volver executar o traballo de DetecciÃŗn de Caras para todas as imaxes ao cambiar un modelo.", + "machine_learning_facial_recognition_setting": "Activar recoÃąecemento facial", + "machine_learning_facial_recognition_setting_description": "Se estÃĄ desactivado, as imaxes non se codificarÃĄn para o recoÃąecemento facial e non encherÃĄn a secciÃŗn Persoas na pÃĄxina Explorar.", + "machine_learning_max_detection_distance": "Distancia mÃĄxima de detecciÃŗn", + "machine_learning_max_detection_distance_description": "Distancia mÃĄxima entre dÃēas imaxes para consideralas duplicadas, variando de 0.001 a 0.1. Valores mÃĄis altos detectarÃĄn mÃĄis duplicados, pero poden resultar en falsos positivos.", + "machine_learning_max_recognition_distance": "Distancia mÃĄxima de recoÃąecemento", + "machine_learning_max_recognition_distance_description": "Distancia mÃĄxima entre dÃēas caras para ser consideradas a mesma persoa, variando de 0 a 2. Baixar isto pode evitar etiquetar dÃēas persoas como a mesma persoa, mentres que subilo pode evitar etiquetar a mesma persoa como dÃēas persoas diferentes. TeÃąa en conta que Ê mÃĄis fÃĄcil fusionar dÃēas persoas que dividir unha persoa en dÃēas, así que erre polo lado dun limiar mÃĄis baixo cando sexa posible.", + "machine_learning_min_detection_score": "PuntuaciÃŗn mínima de detecciÃŗn", + "machine_learning_min_detection_score_description": "PuntuaciÃŗn mínima de confianza para que unha cara sexa detectada, de 0 a 1. Valores mÃĄis baixos detectarÃĄn mÃĄis caras pero poden resultar en falsos positivos.", + "machine_learning_min_recognized_faces": "Mínimo de caras recoÃąecidas", + "machine_learning_min_recognized_faces_description": "O nÃēmero mínimo de caras recoÃąecidas para que se cree unha persoa. Aumentar isto fai que o RecoÃąecemento Facial sexa mÃĄis preciso a costa de aumentar a posibilidade de que unha cara non se asigne a unha persoa.", + "machine_learning_settings": "ConfiguraciÃŗn da Aprendizaxe AutomÃĄtica", + "machine_learning_settings_description": "Xestionar funciÃŗns e configuraciÃŗns da aprendizaxe automÃĄtica", + "machine_learning_smart_search": "Busca Intelixente", + "machine_learning_smart_search_description": "Buscar imaxes semanticamente usando incrustaciÃŗns CLIP", + "machine_learning_smart_search_enabled": "Activar busca intelixente", + "machine_learning_smart_search_enabled_description": "Se estÃĄ desactivado, as imaxes non se codificarÃĄn para a busca intelixente.", + "machine_learning_url_description": "A URL do servidor de aprendizaxe automÃĄtica. Se se proporciona mÃĄis dunha URL, intentarase con cada servidor un por un ata que un responda correctamente, en orde do primeiro ao Ãēltimo. Os servidores que non respondan ignoraranse temporalmente ata que volvan estar en liÃąa.", + "manage_concurrency": "Xestionar Concorrencia", + "manage_log_settings": "Xestionar configuraciÃŗn de rexistro", + "map_dark_style": "Estilo escuro", + "map_enable_description": "Activar funciÃŗns do mapa", + "map_gps_settings": "ConfiguraciÃŗn do Mapa e GPS", + "map_gps_settings_description": "Xestionar a configuraciÃŗn do Mapa e GPS (XeocodificaciÃŗn Inversa)", + "map_implications": "A funciÃŗn do mapa depende dun servizo de teselas externo (tiles.immich.cloud)", + "map_light_style": "Estilo claro", + "map_manage_reverse_geocoding_settings": "Xestionar configuraciÃŗn de XeocodificaciÃŗn Inversa", + "map_reverse_geocoding": "XeocodificaciÃŗn Inversa", + "map_reverse_geocoding_enable_description": "Activar xeocodificaciÃŗn inversa", + "map_reverse_geocoding_settings": "ConfiguraciÃŗn da XeocodificaciÃŗn Inversa", + "map_settings": "Mapa", + "map_settings_description": "Xestionar a configuraciÃŗn do mapa", + "map_style_description": "URL a un tema de mapa style.json", + "memory_cleanup_job": "Limpeza de recordos", + "memory_generate_job": "XeraciÃŗn de recordos", + "metadata_extraction_job": "Extraer metadatos", + "metadata_extraction_job_description": "Extraer informaciÃŗn de metadatos de cada activo, como GPS, caras e resoluciÃŗn", + "metadata_faces_import_setting": "Activar importaciÃŗn de caras", + "metadata_faces_import_setting_description": "Importar caras dos datos EXIF da imaxe e ficheiros sidecar", + "metadata_settings": "ConfiguraciÃŗn de Metadatos", + "metadata_settings_description": "Xestionar a configuraciÃŗn de metadatos", + "migration_job": "MigraciÃŗn", + "migration_job_description": "Migrar miniaturas de activos e caras ÃĄ Ãēltima estrutura de cartafoles", + "no_paths_added": "Non se engadiron rutas", + "no_pattern_added": "Non se engadiu ningÃēn padrÃŗn", + "note_apply_storage_label_previous_assets": "Nota: Para aplicar a Etiqueta de Almacenamento a activos cargados previamente, execute o", + "note_cannot_be_changed_later": "NOTA: Isto non se pode cambiar mÃĄis tarde!", + "notification_email_from_address": "Enderezo do remitente", + "notification_email_from_address_description": "Enderezo de correo electrÃŗnico do remitente, por exemplo: \"Servidor de Fotos Immich \"", + "notification_email_host_description": "Host do servidor de correo electrÃŗnico (p. ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorar erros de certificado", - "notification_email_ignore_certificate_errors_description": "Ignorar erros de validaciÃŗn de certificados TLS (non recomendado)", - "notification_settings": "ConfiguraciÃŗn de notificaciÃŗns" + "notification_email_ignore_certificate_errors_description": "Ignorar erros de validaciÃŗn do certificado TLS (non recomendado)", + "notification_email_password_description": "Contrasinal a usar ao autenticarse co servidor de correo electrÃŗnico", + "notification_email_port_description": "Porto do servidor de correo electrÃŗnico (p. ex. 25, 465 ou 587)", + "notification_email_sent_test_email_button": "Enviar correo de proba e gardar", + "notification_email_setting_description": "ConfiguraciÃŗn para enviar notificaciÃŗns por correo electrÃŗnico", + "notification_email_test_email": "Enviar correo de proba", + "notification_email_test_email_failed": "Erro ao enviar correo de proba, comproba os teus valores", + "notification_email_test_email_sent": "Enviouse un correo electrÃŗnico de proba a {email}. Por favor, comproba a tÃēa caixa de entrada.", + "notification_email_username_description": "Nome de usuario a usar ao autenticarse co servidor de correo electrÃŗnico", + "notification_enable_email_notifications": "Activar notificaciÃŗns por correo electrÃŗnico", + "notification_settings": "ConfiguraciÃŗn de NotificaciÃŗns", + "notification_settings_description": "Xestionar a configuraciÃŗn de notificaciÃŗns, incluído o correo electrÃŗnico", + "oauth_auto_launch": "Lanzamento automÃĄtico", + "oauth_auto_launch_description": "Iniciar o fluxo de inicio de sesiÃŗn OAuth automaticamente ao navegar ÃĄ pÃĄxina de inicio de sesiÃŗn", + "oauth_auto_register": "Rexistro automÃĄtico", + "oauth_auto_register_description": "Rexistrar automaticamente novos usuarios despois de iniciar sesiÃŗn con OAuth", + "oauth_button_text": "Texto do botÃŗn", + "oauth_enable_description": "Iniciar sesiÃŗn con OAuth", + "oauth_mobile_redirect_uri": "URI de redirecciÃŗn mÃŗbil", + "oauth_mobile_redirect_uri_override": "SubstituciÃŗn de URI de redirecciÃŗn mÃŗbil", + "oauth_mobile_redirect_uri_override_description": "Activar cando o provedor OAuth non permite un URI mÃŗbil, como '{callback}'", + "oauth_settings": "OAuth", + "oauth_settings_description": "Xestionar a configuraciÃŗn de inicio de sesiÃŗn OAuth", + "oauth_settings_more_details": "Para mÃĄis detalles sobre esta funciÃŗn, consulte a documentaciÃŗn.", + "oauth_storage_label_claim": "DeclaraciÃŗn de etiqueta de almacenamento", + "oauth_storage_label_claim_description": "Establecer automaticamente a etiqueta de almacenamento do usuario ao valor desta declaraciÃŗn.", + "oauth_storage_quota_claim": "DeclaraciÃŗn de cota de almacenamento", + "oauth_storage_quota_claim_description": "Establecer automaticamente a cota de almacenamento do usuario ao valor desta declaraciÃŗn.", + "oauth_storage_quota_default": "Cota de almacenamento predeterminada (GiB)", + "oauth_storage_quota_default_description": "Cota en GiB a usar cando non se proporciona ningunha declaraciÃŗn (Introduza 0 para cota ilimitada).", + "offline_paths": "Rutas fÃŗra de liÃąa", + "offline_paths_description": "Estes resultados poden deberse ÃĄ eliminaciÃŗn manual de ficheiros que non forman parte dunha biblioteca externa.", + "password_enable_description": "Iniciar sesiÃŗn con correo electrÃŗnico e contrasinal", + "password_settings": "Inicio de sesiÃŗn con contrasinal", + "password_settings_description": "Xestionar a configuraciÃŗn de inicio de sesiÃŗn con contrasinal", + "paths_validated_successfully": "Todas as rutas validadas correctamente", + "person_cleanup_job": "Limpeza de persoas", + "quota_size_gib": "TamaÃąo da cota (GiB)", + "refreshing_all_libraries": "Actualizando todas as bibliotecas", + "registration": "Rexistro do administrador", + "registration_description": "Dado que ti es o primeiro usuario no sistema, asignarÃĄsete como Administrador e serÃĄs responsable das tarefas administrativas, e os usuarios adicionais serÃĄn creados por ti.", + "repair_all": "Reparar todo", + "repair_matched_items": "Coincidiron {count, plural, one {# elemento} other {# elementos}}", + "repaired_items": "ReparÃĄronse {count, plural, one {# elemento} other {# elementos}}", + "require_password_change_on_login": "Requirir que o usuario cambie o contrasinal no primeiro inicio de sesiÃŗn", + "reset_settings_to_default": "Restablecer a configuraciÃŗn aos valores predeterminados", + "reset_settings_to_recent_saved": "Restablecer a configuraciÃŗn ÃĄ configuraciÃŗn gardada recentemente", + "scanning_library": "Escaneando biblioteca", + "search_jobs": "Buscar traballosâ€Ļ", + "send_welcome_email": "Enviar correo electrÃŗnico de benvida", + "server_external_domain_settings": "Dominio externo", + "server_external_domain_settings_description": "Dominio para ligazÃŗns pÃēblicas compartidas, incluíndo http(s)://", + "server_public_users": "Usuarios pÃēblicos", + "server_public_users_description": "Todos os usuarios (nome e correo electrÃŗnico) listanse ao engadir un usuario a ÃĄlbums compartidos. Cando estÃĄ desactivado, a lista de usuarios sÃŗ estarÃĄ dispoÃąible para os usuarios administradores.", + "server_settings": "ConfiguraciÃŗn do servidor", + "server_settings_description": "Xestionar a configuraciÃŗn do servidor", + "server_welcome_message": "Mensaxe de benvida", + "server_welcome_message_description": "Unha mensaxe que se mostra na pÃĄxina de inicio de sesiÃŗn.", + "sidecar_job": "Metadatos Sidecar", + "sidecar_job_description": "Descubrir ou sincronizar metadatos sidecar desde o sistema de ficheiros", + "slideshow_duration_description": "NÃēmero de segundos para mostrar cada imaxe", + "smart_search_job_description": "Executar aprendizaxe automÃĄtica nos activos para soportar a busca intelixente", + "storage_template_date_time_description": "A marca de tempo de creaciÃŗn do activo Ãēsase para a informaciÃŗn de data e hora", + "storage_template_date_time_sample": "Tempo de mostra {date}", + "storage_template_enable_description": "Activar o motor de modelos de almacenamento", + "storage_template_hash_verification_enabled": "VerificaciÃŗn de hash activada", + "storage_template_hash_verification_enabled_description": "Activa a verificaciÃŗn de hash, non desactives isto a menos que esteas seguro das implicaciÃŗns", + "storage_template_migration": "MigraciÃŗn do modelo de almacenamento", + "storage_template_migration_description": "Aplicar o {template} actual aos activos cargados previamente", + "storage_template_migration_info": "O modelo de almacenamento converterÃĄ todas as extensiÃŗns a minÃēsculas. Os cambios no modelo sÃŗ se aplicarÃĄn aos activos novos. Para aplicar retroactivamente o modelo aos activos cargados previamente, execute o {job}.", + "storage_template_migration_job": "Traballo de MigraciÃŗn do Modelo de Almacenamento", + "storage_template_more_details": "Para mÃĄis detalles sobre esta funciÃŗn, consulte o Modelo de Almacenamento e as sÃēas implicaciÃŗns", + "storage_template_onboarding_description": "Cando estea activada, esta funciÃŗn autoorganizarÃĄ os ficheiros baseÃĄndose nun modelo definido polo usuario. Debido a problemas de estabilidade, a funciÃŗn desactivouse por defecto. Para obter mÃĄis informaciÃŗn, consulte a documentaciÃŗn.", + "storage_template_path_length": "Límite aproximado da lonxitude da ruta: {length, number}/{limit, number}", + "storage_template_settings": "Modelo de Almacenamento", + "storage_template_settings_description": "Xestionar a estrutura de cartafoles e o nome de ficheiro do activo cargado", + "storage_template_user_label": "{label} Ê a Etiqueta de Almacenamento do usuario", + "system_settings": "ConfiguraciÃŗn do Sistema", + "tag_cleanup_job": "Limpeza de etiquetas", + "template_email_available_tags": "Podes usar as seguintes variables no teu modelo: {tags}", + "template_email_if_empty": "Se o modelo estÃĄ baleiro, usarase o correo electrÃŗnico predeterminado.", + "template_email_invite_album": "Modelo de InvitaciÃŗn a Álbum", + "template_email_preview": "Vista previa", + "template_email_settings": "Modelos de Correo ElectrÃŗnico", + "template_email_settings_description": "Xestionar modelos personalizados de notificaciÃŗn por correo electrÃŗnico", + "template_email_update_album": "Modelo de ActualizaciÃŗn de Álbum", + "template_email_welcome": "Modelo de correo electrÃŗnico de benvida", + "template_settings": "Modelos de NotificaciÃŗn", + "template_settings_description": "Xestionar modelos personalizados para notificaciÃŗns.", + "theme_custom_css_settings": "CSS Personalizado", + "theme_custom_css_settings_description": "As Follas de Estilo en Cascada permiten personalizar o deseÃąo de Immich.", + "theme_settings": "ConfiguraciÃŗn do Tema", + "theme_settings_description": "Xestionar a personalizaciÃŗn da interface web de Immich", + "these_files_matched_by_checksum": "Estes ficheiros coinciden polas sÃēas sumas de verificaciÃŗn", + "thumbnail_generation_job": "Xerar Miniaturas", + "thumbnail_generation_job_description": "Xerar miniaturas grandes, pequenas e borrosas para cada activo, así como miniaturas para cada persoa", + "transcoding_acceleration_api": "API de aceleraciÃŗn", + "transcoding_acceleration_api_description": "A API que interactuarÃĄ co teu dispositivo para acelerar a transcodificaciÃŗn. Esta configuraciÃŗn Ê de 'mellor esforzo': recurrirÃĄ ÃĄ transcodificaciÃŗn por software en caso de fallo. VP9 pode funcionar ou non dependendo do teu hardware.", + "transcoding_acceleration_nvenc": "NVENC (require GPU NVIDIA)", + "transcoding_acceleration_qsv": "Quick Sync (require CPU Intel de 7ÂĒ xeraciÃŗn ou posterior)", + "transcoding_acceleration_rkmpp": "RKMPP (sÃŗ en SOCs Rockchip)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "CÃŗdecs de audio aceptados", + "transcoding_accepted_audio_codecs_description": "Seleccione que cÃŗdecs de audio non necesitan ser transcodificados. SÃŗ se usa para certas políticas de transcodificaciÃŗn.", + "transcoding_accepted_containers": "Contedores aceptados", + "transcoding_accepted_containers_description": "Seleccione que formatos de contedor non necesitan ser remuxados a MP4. SÃŗ se usa para certas políticas de transcodificaciÃŗn.", + "transcoding_accepted_video_codecs": "CÃŗdecs de vídeo aceptados", + "transcoding_accepted_video_codecs_description": "Seleccione que cÃŗdecs de vídeo non necesitan ser transcodificados. SÃŗ se usa para certas políticas de transcodificaciÃŗn.", + "transcoding_advanced_options_description": "OpciÃŗns que a maioría dos usuarios non deberían necesitar cambiar", + "transcoding_audio_codec": "CÃŗdec de audio", + "transcoding_audio_codec_description": "Opus Ê a opciÃŗn de maior calidade, pero ten menor compatibilidade con dispositivos ou software antigos.", + "transcoding_bitrate_description": "Vídeos cun bitrate superior ao mÃĄximo ou que non estean nun formato aceptado", + "transcoding_codecs_learn_more": "Para saber mÃĄis sobre a terminoloxía usada aquí, consulte a documentaciÃŗn de FFmpeg para cÃŗdec H.264, cÃŗdec HEVC e cÃŗdec VP9.", + "transcoding_constant_quality_mode": "Modo de calidade constante", + "transcoding_constant_quality_mode_description": "ICQ Ê mellor que CQP, pero algÃēns dispositivos de aceleraciÃŗn por hardware non admiten este modo. Establecer esta opciÃŗn preferirÃĄ o modo especificado ao usar codificaciÃŗn baseada na calidade. Ignorado por NVENC xa que non admite ICQ.", + "transcoding_constant_rate_factor": "Factor de taxa constante (-crf)", + "transcoding_constant_rate_factor_description": "Nivel de calidade do vídeo. Valores típicos son 23 para H.264, 28 para HEVC, 31 para VP9 e 35 para AV1. MÃĄis baixo Ê mellor, pero produce ficheiros mÃĄis grandes.", + "transcoding_disabled_description": "Non transcodificar ningÃēn vídeo, pode romper a reproduciÃŗn nalgÃēns clientes", + "transcoding_encoding_options": "OpciÃŗns de CodificaciÃŗn", + "transcoding_encoding_options_description": "Establecer cÃŗdecs, resoluciÃŗn, calidade e outras opciÃŗns para os vídeos codificados", + "transcoding_hardware_acceleration": "AceleraciÃŗn por Hardware", + "transcoding_hardware_acceleration_description": "Experimental; moito mÃĄis rÃĄpido, pero terÃĄ menor calidade co mesmo bitrate", + "transcoding_hardware_decoding": "DecodificaciÃŗn por hardware", + "transcoding_hardware_decoding_setting_description": "Activa a aceleraciÃŗn de extremo a extremo en lugar de sÃŗ acelerar a codificaciÃŗn. Pode non funcionar en todos os vídeos.", + "transcoding_hevc_codec": "CÃŗdec HEVC", + "transcoding_max_b_frames": "MÃĄximo de B-frames", + "transcoding_max_b_frames_description": "Valores mÃĄis altos melloran a eficiencia da compresiÃŗn, pero ralentizan a codificaciÃŗn. Pode non ser compatible coa aceleraciÃŗn por hardware en dispositivos mÃĄis antigos. 0 desactiva os B-frames, mentres que -1 establece este valor automaticamente.", + "transcoding_max_bitrate": "Bitrate mÃĄximo", + "transcoding_max_bitrate_description": "Establecer un bitrate mÃĄximo pode facer que os tamaÃąos dos ficheiros sexan mÃĄis predicibles a un custo menor para a calidade. A 720p, os valores típicos son 2600 kbit/s para VP9 ou HEVC, ou 4500 kbit/s para H.264. Desactivado se se establece en 0.", + "transcoding_max_keyframe_interval": "Intervalo mÃĄximo de fotogramas clave", + "transcoding_max_keyframe_interval_description": "Establece a distancia mÃĄxima de fotogramas entre fotogramas clave. Valores mÃĄis baixos empeoran a eficiencia da compresiÃŗn, pero melloran os tempos de busca e poden mellorar a calidade en escenas con movemento rÃĄpido. 0 establece este valor automaticamente.", + "transcoding_optimal_description": "Vídeos cunha resoluciÃŗn superior ÃĄ obxectivo ou que non estean nun formato aceptado", + "transcoding_policy": "Política de TranscodificaciÃŗn", + "transcoding_policy_description": "Establecer cando se transcodificarÃĄ un vídeo", + "transcoding_preferred_hardware_device": "Dispositivo de hardware preferido", + "transcoding_preferred_hardware_device_description": "Aplícase sÃŗ a VAAPI e QSV. Establece o nodo dri usado para a transcodificaciÃŗn por hardware.", + "transcoding_preset_preset": "Preaxuste (-preset)", + "transcoding_preset_preset_description": "Velocidade de compresiÃŗn. Preaxustes mÃĄis lentos producen ficheiros mÃĄis pequenos e aumentan a calidade ao apuntar a un certo bitrate. VP9 ignora velocidades superiores a 'faster'.", + "transcoding_reference_frames": "Fotogramas de referencia", + "transcoding_reference_frames_description": "O nÃēmero de fotogramas aos que facer referencia ao comprimir un fotograma dado. Valores mÃĄis altos melloran a eficiencia da compresiÃŗn, pero ralentizan a codificaciÃŗn. 0 establece este valor automaticamente.", + "transcoding_required_description": "SÃŗ vídeos que non estean nun formato aceptado", + "transcoding_settings": "ConfiguraciÃŗn da TranscodificaciÃŗn de Vídeo", + "transcoding_settings_description": "Xestionar que vídeos transcodificar e como procesalos", + "transcoding_target_resolution": "ResoluciÃŗn obxectivo", + "transcoding_target_resolution_description": "ResoluciÃŗns mÃĄis altas poden preservar mÃĄis detalles pero tardan mÃĄis en codificarse, teÃąen tamaÃąos de ficheiro mÃĄis grandes e poden reducir a capacidade de resposta da aplicaciÃŗn.", + "transcoding_temporal_aq": "AQ Temporal", + "transcoding_temporal_aq_description": "Aplícase sÃŗ a NVENC. Aumenta a calidade de escenas de alto detalle e baixo movemento. Pode non ser compatible con dispositivos mÃĄis antigos.", + "transcoding_threads": "Fíos", + "transcoding_threads_description": "Valores mÃĄis altos levan a unha codificaciÃŗn mÃĄis rÃĄpida, pero deixan menos marxe para que o servidor procese outras tarefas mentres estÃĄ activo. Este valor non debería ser maior que o nÃēmero de nÃēcleos da CPU. Maximiza a utilizaciÃŗn se se establece en 0.", + "transcoding_tone_mapping": "Mapeo de tons", + "transcoding_tone_mapping_description": "Intenta preservar a aparencia dos vídeos HDR cando se converten a SDR. Cada algoritmo fai diferentes compromisos para cor, detalle e brillo. Hable preserva o detalle, Mobius preserva a cor e Reinhard preserva o brillo.", + "transcoding_transcode_policy": "Política de transcodificaciÃŗn", + "transcoding_transcode_policy_description": "Política para cando un vídeo debe ser transcodificado. Os vídeos HDR sempre serÃĄn transcodificados (excepto se a transcodificaciÃŗn estÃĄ desactivada).", + "transcoding_two_pass_encoding": "CodificaciÃŗn en dous pasos", + "transcoding_two_pass_encoding_setting_description": "Transcodificar en dous pasos para producir vídeos codificados mellor. Cando o bitrate mÃĄximo estÃĄ activado (requirido para que funcione con H.264 e HEVC), este modo usa un rango de bitrate baseado no bitrate mÃĄximo e ignora CRF. Para VP9, pÃŗdese usar CRF se o bitrate mÃĄximo estÃĄ desactivado.", + "transcoding_video_codec": "CÃŗdec de vídeo", + "transcoding_video_codec_description": "VP9 ten alta eficiencia e compatibilidade web, pero tarda mÃĄis en transcodificarse. HEVC ten un rendemento similar, pero ten menor compatibilidade web. H.264 Ê amplamente compatible e rÃĄpido de transcodificar, pero produce ficheiros moito mÃĄis grandes. AV1 Ê o cÃŗdec mÃĄis eficiente pero carece de soporte en dispositivos mÃĄis antigos.", + "trash_enabled_description": "Activar funciÃŗns do Lixo", + "trash_number_of_days": "NÃēmero de días", + "trash_number_of_days_description": "NÃēmero de días para manter os activos no lixo antes de eliminalos permanentemente", + "trash_settings": "ConfiguraciÃŗn do Lixo", + "trash_settings_description": "Xestionar a configuraciÃŗn do lixo", + "untracked_files": "Ficheiros non rastrexados", + "untracked_files_description": "Estes ficheiros non son rastrexados pola aplicaciÃŗn. Poden ser o resultado de movementos fallidos, cargas interrompidas ou deixados atrÃĄs debido a un erro", + "user_cleanup_job": "Limpeza de usuarios", + "user_delete_delay": "A conta e os activos de {user} programaranse para a sÃēa eliminaciÃŗn permanente en {delay, plural, one {# día} other {# días}}.", + "user_delete_delay_settings": "Atraso na eliminaciÃŗn", + "user_delete_delay_settings_description": "NÃēmero de días despois da eliminaciÃŗn para eliminar permanentemente a conta e os activos dun usuario. O traballo de eliminaciÃŗn de usuarios execÃētase ÃĄ medianoite para comprobar os usuarios que estÃĄn listos para a eliminaciÃŗn. Os cambios nesta configuraciÃŗn avaliaranse na prÃŗxima execuciÃŗn.", + "user_delete_immediately": "A conta e os activos de {user} poranse en cola para a sÃēa eliminaciÃŗn permanente inmediatamente.", + "user_delete_immediately_checkbox": "PoÃąer en cola o usuario e os activos para a sÃēa eliminaciÃŗn inmediata", + "user_management": "XestiÃŗn de Usuarios", + "user_password_has_been_reset": "Restableceuse o contrasinal do usuario:", + "user_password_reset_description": "Proporcione o contrasinal temporal ao usuario e infÃŗrmelle de que necesitarÃĄ cambiar o contrasinal no seu prÃŗximo inicio de sesiÃŗn.", + "user_restore_description": "Restaurarase a conta de {user}.", + "user_restore_scheduled_removal": "Restaurar usuario - eliminaciÃŗn programada o {date, date, long}", + "user_settings": "ConfiguraciÃŗn do Usuario", + "user_settings_description": "Xestionar a configuraciÃŗn do usuario", + "user_successfully_removed": "Usuario {email} eliminado correctamente.", + "version_check_enabled_description": "Activar comprobaciÃŗn de versiÃŗn", + "version_check_implications": "A funciÃŗn de comprobaciÃŗn de versiÃŗn depende da comunicaciÃŗn periÃŗdica con github.com", + "version_check_settings": "ComprobaciÃŗn de VersiÃŗn", + "version_check_settings_description": "Activar/desactivar a notificaciÃŗn de nova versiÃŗn", + "video_conversion_job": "Transcodificar vídeos", + "video_conversion_job_description": "Transcodificar vídeos para unha maior compatibilidade con navegadores e dispositivos" }, - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates (EXPERIMENTAL)", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", - "album_viewer_appbar_share_to": "Share To", - "album_viewer_page_share_add_users": "Add users", - "albums": "Albums", - "all": "All", - "app_bar_signout_dialog_content": "Are you sure you want to sign out?", - "app_bar_signout_dialog_ok": "Yes", - "app_bar_signout_dialog_title": "Sign out", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", - "archived": "Archived", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", - "asset_restored_successfully": "Asset restored successfully", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", - "backup_controller_page_background_app_refresh_enable_button_text": "Go to settings", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", - "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "backup_controller_page_turn_on": "Turn on foreground backup", - "backup_controller_page_uploading_file_info": "Uploading file info", - "backup_err_only_album": "Cannot remove the only album", - "backup_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after some time", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", - "cache_settings_title": "Caching Settings", - "cancel": "Cancel", - "canceled": "Canceled", - "change_display_order": "Change display order", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate (EXPERIMENTAL)", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", - "create_album": "Create album", - "create_album_page_untitled": "Untitled", - "create_new": "CREATE NEW", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", - "crop": "Crop", - "curated_object_page_title": "Things", - "current_server_address": "Current server address", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", - "date_format": "E, LLL d, y â€ĸ h:mm a", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", - "delete_shared_link_dialog_title": "Delete Shared Link", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_notfound": "Download not found", - "download_paused": "Download paused", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", - "downloading": "Downloading...", - "downloading_media": "Downloading media", - "edit_location_dialog_title": "Location", - "end_date": "End date", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", - "error_change_sort_album": "Failed to change album sort order", - "error_saving_image": "Error: {}", - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", - "experimental_settings_title": "Experimental", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", - "failed": "Failed", - "favorites": "Favorites", - "favorites_page_no_favorites": "No favorite assets found", - "filter": "Filter", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", - "grant_permission": "Grant permission", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", - "home_page_building_timeline": "Building the timeline", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", - "library": "Library", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", - "monthly_title_text_date_format": "MMMM y", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", - "my_albums": "My albums", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", - "no_assets_to_show": "No assets to show", - "no_name": "No name", - "not_selected": "Not selected", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", - "notification_permission_list_tile_content": "Grant permission to enable notifications.", - "notification_permission_list_tile_enable_button": "Enable Notifications", - "notification_permission_list_tile_title": "Notification Permission", - "on_this_device": "On this device", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", - "partners": "Partners", - "paused": "Paused", - "people": "People", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", - "places": "Places", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", - "save": "Save", - "save_to_gallery": "Save to gallery", - "scaffold_body_error_occurred": "Error occurred", - "search_albums": "Search albums", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", - "search_result_page_new_search_hint": "New Search", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", - "select_user_for_sharing_page_err_album": "Failed to create album", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", - "server_info_box_server_url": "Server URL", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "setting_notifications_subtitle": "Adjust your notification preferences", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", - "settings_require_restart": "Please restart Immich to apply this setting", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", - "shared_links": "Shared links", - "shared_with_me": "Shared with me", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", - "start_date": "Start date", - "sync": "Sync", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", - "trash": "Trash", - "trash_emptied": "Emptied trash", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", - "upload": "Upload", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "use_current_connection": "use current connection", - "validate_endpoint_error": "Please enter a valid URL", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available 🎉", - "videos": "Videos", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", - "wifi_name": "WiFi Name", + "admin_email": "Correo electrÃŗnico do administrador", + "admin_password": "Contrasinal do administrador", + "administration": "AdministraciÃŗn", + "advanced": "Avanzado", + "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opciÃŗn para filtrar medios durante a sincronizaciÃŗn baseÃĄndose en criterios alternativos. SÃŗ proba isto se tes problemas coa aplicaciÃŗn detectando todos os ÃĄlbums.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronizaciÃŗn de ÃĄlbums do dispositivo", + "advanced_settings_log_level_title": "Nivel de rexistro: {level}", + "advanced_settings_prefer_remote_subtitle": "AlgÃēns dispositivos son extremadamente lentos para cargar miniaturas de activos no dispositivo. Active esta configuraciÃŗn para cargar imaxes remotas no seu lugar.", + "advanced_settings_prefer_remote_title": "Preferir imaxes remotas", + "advanced_settings_proxy_headers_subtitle": "Definir cabeceiras de proxy que Immich debería enviar con cada solicitude de rede", + "advanced_settings_proxy_headers_title": "Cabeceiras de Proxy", + "advanced_settings_self_signed_ssl_subtitle": "Omite a verificaciÃŗn do certificado SSL para o punto final do servidor. Requirido para certificados autofirmados.", + "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL autofirmados", + "advanced_settings_sync_remote_deletions_subtitle": "Eliminar ou restaurar automaticamente un activo neste dispositivo cando esa acciÃŗn se realiza na web", + "advanced_settings_sync_remote_deletions_title": "Sincronizar eliminaciÃŗns remotas [EXPERIMENTAL]", + "advanced_settings_tile_subtitle": "ConfiguraciÃŗn de usuario avanzado", + "advanced_settings_troubleshooting_subtitle": "Activar funciÃŗns adicionais para a resoluciÃŗn de problemas", + "advanced_settings_troubleshooting_title": "ResoluciÃŗn de problemas", + "age_months": "Idade {months, plural, one {# mes} other {# meses}}", + "age_year_months": "Idade 1 ano, {months, plural, one {# mes} other {# meses}}", + "age_years": "{years, plural, other {Idade #}}", + "album_added": "Álbum engadido", + "album_added_notification_setting_description": "Recibir unha notificaciÃŗn por correo electrÃŗnico cando sexas engadido a un ÃĄlbum compartido", + "album_cover_updated": "Portada do ÃĄlbum actualizada", + "album_delete_confirmation": "EstÃĄs seguro de que queres eliminar o ÃĄlbum {album}?", + "album_delete_confirmation_description": "Se este ÃĄlbum estÃĄ compartido, outros usuarios non poderÃĄn acceder a el.", + "album_info_card_backup_album_excluded": "EXCLUÍDO", + "album_info_card_backup_album_included": "INCLUÍDO", + "album_info_updated": "InformaciÃŗn do ÃĄlbum actualizada", + "album_leave": "Saír do ÃĄlbum?", + "album_leave_confirmation": "EstÃĄs seguro de que queres saír de {album}?", + "album_name": "Nome do Álbum", + "album_options": "OpciÃŗns do ÃĄlbum", + "album_remove_user": "Eliminar usuario?", + "album_remove_user_confirmation": "EstÃĄs seguro de que queres eliminar a {user}?", + "album_share_no_users": "Parece que compartiches este ÃĄlbum con todos os usuarios ou non tes ningÃēn usuario co que compartir.", + "album_thumbnail_card_item": "1 elemento", + "album_thumbnail_card_items": "{count} elementos", + "album_thumbnail_card_shared": " ¡ Compartido", + "album_thumbnail_shared_by": "Compartido por {user}", + "album_updated": "Álbum actualizado", + "album_updated_setting_description": "Recibir unha notificaciÃŗn por correo electrÃŗnico cando un ÃĄlbum compartido teÃąa novos activos", + "album_user_left": "Saíu de {album}", + "album_user_removed": "Eliminado {user}", + "album_viewer_appbar_delete_confirm": "EstÃĄs seguro de que queres eliminar este ÃĄlbum da tÃēa conta?", + "album_viewer_appbar_share_err_delete": "Erro ao eliminar o ÃĄlbum", + "album_viewer_appbar_share_err_leave": "Erro ao saír do ÃĄlbum", + "album_viewer_appbar_share_err_remove": "Hai problemas ao eliminar activos do ÃĄlbum", + "album_viewer_appbar_share_err_title": "Erro ao cambiar o título do ÃĄlbum", + "album_viewer_appbar_share_leave": "Saír do ÃĄlbum", + "album_viewer_appbar_share_to": "Compartir con", + "album_viewer_page_share_add_users": "Engadir usuarios", + "album_with_link_access": "Permitir que calquera persoa coa ligazÃŗn vexa fotos e persoas neste ÃĄlbum.", + "albums": "Álbums", + "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbums}}", + "all": "Todo", + "all_albums": "Todos os ÃĄlbums", + "all_people": "Todas as persoas", + "all_videos": "Todos os vídeos", + "allow_dark_mode": "Permitir modo escuro", + "allow_edits": "Permitir ediciÃŗns", + "allow_public_user_to_download": "Permitir que o usuario pÃēblico descargue", + "allow_public_user_to_upload": "Permitir que o usuario pÃēblico cargue", + "alt_text_qr_code": "Imaxe de cÃŗdigo QR", + "anti_clockwise": "Sentido antihorario", + "api_key": "Chave API", + "api_key_description": "Este valor sÃŗ se mostrarÃĄ unha vez. AsegÃērese de copialo antes de pechar a xanela.", + "api_key_empty": "O nome da sÃēa chave API non pode estar baleiro", + "api_keys": "Chaves API", + "app_bar_signout_dialog_content": "EstÃĄs seguro de que queres pechar sesiÃŗn?", + "app_bar_signout_dialog_ok": "Si", + "app_bar_signout_dialog_title": "Pechar sesiÃŗn", + "app_settings": "ConfiguraciÃŗn da AplicaciÃŗn", + "appears_in": "Aparece en", + "archive": "Arquivo", + "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", + "archive_page_no_archived_assets": "Non se atoparon activos arquivados", + "archive_page_title": "Arquivo ({count})", + "archive_size": "TamaÃąo do arquivo", + "archive_size_description": "Configurar o tamaÃąo do arquivo para descargas (en GiB)", + "archived": "Arquivado", + "archived_count": "{count, plural, other {Arquivados #}}", + "are_these_the_same_person": "Son estas a mesma persoa?", + "are_you_sure_to_do_this": "EstÃĄs seguro de que queres facer isto?", + "asset_action_delete_err_read_only": "Non se poden eliminar activo(s) de sÃŗ lectura, omitindo", + "asset_action_share_err_offline": "Non se poden obter activo(s) fÃŗra de liÃąa, omitindo", + "asset_added_to_album": "Engadido ao ÃĄlbum", + "asset_adding_to_album": "Engadindo ao ÃĄlbumâ€Ļ", + "asset_description_updated": "A descriciÃŗn do activo actualizouse", + "asset_filename_is_offline": "O activo {filename} estÃĄ fÃŗra de liÃąa", + "asset_has_unassigned_faces": "O activo ten caras non asignadas", + "asset_hashing": "Calculando hashâ€Ļ", + "asset_list_group_by_sub_title": "Agrupar por", + "asset_list_layout_settings_dynamic_layout_title": "DeseÃąo dinÃĄmico", + "asset_list_layout_settings_group_automatically": "AutomÃĄtico", + "asset_list_layout_settings_group_by": "Agrupar activos por", + "asset_list_layout_settings_group_by_month_day": "Mes + día", + "asset_list_layout_sub_title": "DeseÃąo", + "asset_list_settings_subtitle": "ConfiguraciÃŗn do deseÃąo da grella de fotos", + "asset_list_settings_title": "Grella de Fotos", + "asset_offline": "Activo FÃŗra de LiÃąa", + "asset_offline_description": "Este activo externo xa non se atopa no disco. Por favor, contacta co teu administrador de Immich para obter axuda.", + "asset_restored_successfully": "Activo restaurado correctamente", + "asset_skipped": "Omitido", + "asset_skipped_in_trash": "No lixo", + "asset_uploaded": "Subido", + "asset_uploading": "Subindoâ€Ļ", + "asset_viewer_settings_subtitle": "Xestionar a tÃēa configuraciÃŗn do visor da galería", + "asset_viewer_settings_title": "Visor de Activos", + "assets": "Activos", + "assets_added_count": "Engadido {count, plural, one {# activo} other {# activos}}", + "assets_added_to_album_count": "Engadido {count, plural, one {# activo} other {# activos}} ao ÃĄlbum", + "assets_added_to_name_count": "Engadido {count, plural, one {# activo} other {# activos}} a {hasName, select, true {{name}} other {novo ÃĄlbum}}", + "assets_count": "{count, plural, one {# activo} other {# activos}}", + "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", + "assets_moved_to_trash_count": "Movido {count, plural, one {# activo} other {# activos}} ao lixo", + "assets_permanently_deleted_count": "Eliminados permanentemente {count, plural, one {# activo} other {# activos}}", + "assets_removed_count": "Eliminados {count, plural, one {# activo} other {# activos}}", + "assets_removed_permanently_from_device": "{count} activo(s) eliminado(s) permanentemente do teu dispositivo", + "assets_restore_confirmation": "EstÃĄs seguro de que queres restaurar todos os seus activos no lixo? Non podes desfacer esta acciÃŗn! Ten en conta que calquera activo fÃŗra de liÃąa non pode ser restaurado desta maneira.", + "assets_restored_count": "Restaurados {count, plural, one {# activo} other {# activos}}", + "assets_restored_successfully": "{count} activo(s) restaurado(s) correctamente", + "assets_trashed": "{count} activo(s) movido(s) ao lixo", + "assets_trashed_count": "Movido {count, plural, one {# activo} other {# activos}} ao lixo", + "assets_trashed_from_server": "{count} activo(s) movido(s) ao lixo desde o servidor Immich", + "assets_were_part_of_album_count": "{count, plural, one {O activo xa era} other {Os activos xa eran}} parte do ÃĄlbum", + "authorized_devices": "Dispositivos Autorizados", + "automatic_endpoint_switching_subtitle": "Conectar localmente a travÊs da wifi designada cando estea dispoÃąible e usar conexiÃŗns alternativas noutros lugares", + "automatic_endpoint_switching_title": "Cambio automÃĄtico de URL", + "back": "AtrÃĄs", + "back_close_deselect": "AtrÃĄs, pechar ou deseleccionar", + "background_location_permission": "Permiso de ubicaciÃŗn en segundo plano", + "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso ÃĄ ubicaciÃŗn precisa para que a aplicaciÃŗn poida ler o nome da rede wifi", + "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", + "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", + "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios ÃĄlbums. Polo tanto, os ÃĄlbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", + "backup_album_selection_page_select_albums": "Seleccionar ÃĄlbums", + "backup_album_selection_page_selection_info": "InformaciÃŗn da selecciÃŗn", + "backup_album_selection_page_total_assets": "Total de activos Ãēnicos", + "backup_all": "Todo", + "backup_background_service_backup_failed_message": "Erro ao facer copia de seguridade dos activos. Reintentandoâ€Ļ", + "backup_background_service_connection_failed_message": "Erro ao conectar co servidor. Reintentandoâ€Ļ", + "backup_background_service_current_upload_notification": "Subindo {filename}", + "backup_background_service_default_notification": "Comprobando novos activosâ€Ļ", + "backup_background_service_error_title": "Erro na copia de seguridade", + "backup_background_service_in_progress_notification": "Facendo copia de seguridade dos teus activosâ€Ļ", + "backup_background_service_upload_failure_notification": "Erro ao subir {filename}", + "backup_controller_page_albums": "Álbums da Copia de Seguridade", + "backup_controller_page_background_app_refresh_disabled_content": "Active a actualizaciÃŗn de aplicaciÃŗns en segundo plano en Axustes > Xeral > ActualizaciÃŗn en segundo plano para usar a copia de seguridade en segundo plano.", + "backup_controller_page_background_app_refresh_disabled_title": "ActualizaciÃŗn de aplicaciÃŗns en segundo plano desactivada", + "backup_controller_page_background_app_refresh_enable_button_text": "Ir a axustes", + "backup_controller_page_background_battery_info_link": "MÃŗstrame como", + "backup_controller_page_background_battery_info_message": "Para a mellor experiencia de copia de seguridade en segundo plano, desactiva calquera optimizaciÃŗn de batería que restrinxa a actividade en segundo plano para Immich.\n\nDado que isto Ê específico do dispositivo, busque a informaciÃŗn requirida para o fabricante do teu dispositivo.", + "backup_controller_page_background_battery_info_ok": "Aceptar", + "backup_controller_page_background_battery_info_title": "OptimizaciÃŗns da batería", + "backup_controller_page_background_charging": "SÃŗ mentres se carga", + "backup_controller_page_background_configure_error": "Erro ao configurar o servizo en segundo plano", + "backup_controller_page_background_delay": "Atrasar copia de seguridade de novos activos: {duration}", + "backup_controller_page_background_description": "Active o servizo en segundo plano para facer copia de seguridade automaticamente de calquera activo novo sen necesidade de abrir a aplicaciÃŗn", + "backup_controller_page_background_is_off": "A copia de seguridade automÃĄtica en segundo plano estÃĄ desactivada", + "backup_controller_page_background_is_on": "A copia de seguridade automÃĄtica en segundo plano estÃĄ activada", + "backup_controller_page_background_turn_off": "Desactivar servizo en segundo plano", + "backup_controller_page_background_turn_on": "Activar servizo en segundo plano", + "backup_controller_page_background_wifi": "SÃŗ con wifi", + "backup_controller_page_backup": "Copia de Seguridade", + "backup_controller_page_backup_selected": "Seleccionado: ", + "backup_controller_page_backup_sub": "Fotos e vídeos con copia de seguridade", + "backup_controller_page_created": "Creado o: {date}", + "backup_controller_page_desc_backup": "Active a copia de seguridade en primeiro plano para cargar automaticamente novos activos ao servidor ao abrir a aplicaciÃŗn.", + "backup_controller_page_excluded": "Excluído: ", + "backup_controller_page_failed": "Fallado ({count})", + "backup_controller_page_filename": "Nome do ficheiro: {filename} [{size}]", + "backup_controller_page_info": "InformaciÃŗn da Copia de Seguridade", + "backup_controller_page_none_selected": "NingÃēn seleccionado", + "backup_controller_page_remainder": "Restante", + "backup_controller_page_remainder_sub": "Fotos e vídeos restantes para facer copia de seguridade da selecciÃŗn", + "backup_controller_page_server_storage": "Almacenamento do Servidor", + "backup_controller_page_start_backup": "Iniciar Copia de Seguridade", + "backup_controller_page_status_off": "A copia de seguridade automÃĄtica en primeiro plano estÃĄ desactivada", + "backup_controller_page_status_on": "A copia de seguridade automÃĄtica en primeiro plano estÃĄ activada", + "backup_controller_page_storage_format": "{used} de {total} usado", + "backup_controller_page_to_backup": "Álbums para facer copia de seguridade", + "backup_controller_page_total_sub": "Todas as fotos e vídeos Ãēnicos dos ÃĄlbums seleccionados", + "backup_controller_page_turn_off": "Desactivar copia de seguridade en primeiro plano", + "backup_controller_page_turn_on": "Activar copia de seguridade en primeiro plano", + "backup_controller_page_uploading_file_info": "Subindo informaciÃŗn do ficheiro", + "backup_err_only_album": "Non se pode eliminar o Ãēnico ÃĄlbum", + "backup_info_card_assets": "activos", + "backup_manual_cancelled": "Cancelado", + "backup_manual_in_progress": "Subida xa en progreso. Intenta despois dun tempo", + "backup_manual_success": "Éxito", + "backup_manual_title": "Estado da subida", + "backup_options_page_title": "OpciÃŗns da copia de seguridade", + "backup_setting_subtitle": "Xestionar a configuraciÃŗn de carga en segundo plano e primeiro plano", + "backward": "AtrÃĄs", + "birthdate_saved": "Data de nacemento gardada correctamente", + "birthdate_set_description": "A data de nacemento Ãēsase para calcular a idade desta persoa no momento dunha foto.", + "blurred_background": "Fondo borroso", + "bugs_and_feature_requests": "Erros e Solicitudes de FunciÃŗns", + "build": "CompilaciÃŗn", + "build_image": "Construír Imaxe", + "bulk_delete_duplicates_confirmation": "EstÃĄs seguro de que queres eliminar masivamente {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservarÃĄ o activo mÃĄis grande de cada grupo e eliminarÃĄ permanentemente todos os demais duplicados. Non pode desfacer esta acciÃŗn!", + "bulk_keep_duplicates_confirmation": "EstÃĄs seguro de que queres conservar {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto resolverÃĄ todos os grupos duplicados sen eliminar nada.", + "bulk_trash_duplicates_confirmation": "EstÃĄs seguro de que queres mover masivamente ao lixo {count, plural, one {# activo duplicado} other {# activos duplicados}}? Isto conservarÃĄ o activo mÃĄis grande de cada grupo e moverÃĄ ao lixo todos os demais duplicados.", + "buy": "Comprar Immich", + "cache_settings_album_thumbnails": "Miniaturas da pÃĄxina da biblioteca ({count} activos)", + "cache_settings_clear_cache_button": "Borrar cachÊ", + "cache_settings_clear_cache_button_title": "Borra a cachÊ da aplicaciÃŗn. Isto afectarÃĄ significativamente o rendemento da aplicaciÃŗn ata que a cachÊ se reconstruíu.", + "cache_settings_duplicated_assets_clear_button": "BORRAR", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estÃĄn na lista negra da aplicaciÃŗn", + "cache_settings_duplicated_assets_title": "Activos Duplicados ({count})", + "cache_settings_image_cache_size": "TamaÃąo da cachÊ de imaxes ({count} activos)", + "cache_settings_statistics_album": "Miniaturas da biblioteca", + "cache_settings_statistics_assets": "{count} activos ({size})", + "cache_settings_statistics_full": "Imaxes completas", + "cache_settings_statistics_shared": "Miniaturas de ÃĄlbums compartidos", + "cache_settings_statistics_thumbnail": "Miniaturas", + "cache_settings_statistics_title": "Uso da cachÊ", + "cache_settings_subtitle": "Controlar o comportamento da cachÊ da aplicaciÃŗn mÃŗbil Immich", + "cache_settings_thumbnail_size": "TamaÃąo da cachÊ de miniaturas ({count} activos)", + "cache_settings_tile_subtitle": "Controlar o comportamento do almacenamento local", + "cache_settings_tile_title": "Almacenamento Local", + "cache_settings_title": "ConfiguraciÃŗn da CachÊ", + "camera": "CÃĄmara", + "camera_brand": "Marca da cÃĄmara", + "camera_model": "Modelo da cÃĄmara", + "cancel": "Cancelar", + "cancel_search": "Cancelar busca", + "canceled": "Cancelado", + "cannot_merge_people": "Non se poden fusionar persoas", + "cannot_undo_this_action": "Non pode desfacer esta acciÃŗn!", + "cannot_update_the_description": "Non se pode actualizar a descriciÃŗn", + "change_date": "Cambiar data", + "change_display_order": "Cambiar orde de visualizaciÃŗn", + "change_expiration_time": "Cambiar hora de caducidade", + "change_location": "Cambiar ubicaciÃŗn", + "change_name": "Cambiar nome", + "change_name_successfully": "Nome cambiado correctamente", + "change_password": "Cambiar Contrasinal", + "change_password_description": "Esta Ê a primeira vez que inicias sesiÃŗn no sistema ou solicitouse un cambio do teu contrasinal. Introduza o novo contrasinal a continuaciÃŗn.", + "change_password_form_confirm_password": "Confirmar Contrasinal", + "change_password_form_description": "Ola {name},\n\nEsta Ê a primeira vez que inicias sesiÃŗn no sistema ou solicitouse un cambio do teu contrasinal. Introduza o novo contrasinal a continuaciÃŗn.", + "change_password_form_new_password": "Novo Contrasinal", + "change_password_form_password_mismatch": "Os contrasinais non coinciden", + "change_password_form_reenter_new_password": "Reintroducir Novo Contrasinal", + "change_your_password": "Cambiar o teu contrasinal", + "changed_visibility_successfully": "Visibilidade cambiada correctamente", + "check_all": "Marcar todo", + "check_corrupt_asset_backup": "Comprobar copias de seguridade de activos corruptos", + "check_corrupt_asset_backup_button": "Realizar comprobaciÃŗn", + "check_corrupt_asset_backup_description": "Execute esta comprobaciÃŗn sÃŗ a travÊs da wifi e unha vez que todos os activos teÃąan copia de seguridade. O procedemento pode tardar uns minutos.", + "check_logs": "Comprobar Rexistros", + "choose_matching_people_to_merge": "Elixir persoas coincidentes para fusionar", + "city": "Cidade", + "clear": "Limpar", + "clear_all": "Limpar todo", + "clear_all_recent_searches": "Limpar todas as buscas recentes", + "clear_message": "Limpar mensaxe", + "clear_value": "Limpar valor", + "client_cert_dialog_msg_confirm": "Aceptar", + "client_cert_enter_password": "Introducir Contrasinal", + "client_cert_import": "Importar", + "client_cert_import_success_msg": "Certificado de cliente importado", + "client_cert_invalid_msg": "Ficheiro de certificado invÃĄlido ou contrasinal incorrecto", + "client_cert_remove_msg": "Certificado de cliente eliminado", + "client_cert_subtitle": "SÃŗ admite o formato PKCS12 (.p12, .pfx). A importaciÃŗn/eliminaciÃŗn de certificados sÃŗ estÃĄ dispoÃąible antes de iniciar sesiÃŗn", + "client_cert_title": "Certificado de Cliente SSL", + "clockwise": "Sentido horario", + "close": "Pechar", + "collapse": "Contraer", + "collapse_all": "Contraer todo", + "color": "Cor", + "color_theme": "Tema de cor", + "comment_deleted": "Comentario eliminado", + "comment_options": "OpciÃŗns de comentario", + "comments_and_likes": "Comentarios e GÃēstames", + "comments_are_disabled": "Os comentarios estÃĄn desactivados", + "common_create_new_album": "Crear novo ÃĄlbum", + "common_server_error": "Por favor, comprobe a tÃēa conexiÃŗn de rede, asegÃērache de que o servidor sexa accesible e que as versiÃŗns da aplicaciÃŗn/servidor sexan compatibles.", + "completed": "Completado", + "confirm": "Confirmar", + "confirm_admin_password": "Confirmar Contrasinal do Administrador", + "confirm_delete_face": "EstÃĄs seguro de que queres eliminar a cara de {name} do activo?", + "confirm_delete_shared_link": "EstÃĄs seguro de que queres eliminar esta ligazÃŗn compartida?", + "confirm_keep_this_delete_others": "Todos os demais activos na pila eliminaranse excepto este activo. EstÃĄs seguro de que queres continuar?", + "confirm_password": "Confirmar contrasinal", + "contain": "Conter", + "context": "Contexto", + "continue": "Continuar", + "control_bottom_app_bar_album_info_shared": "{count} elementos ¡ Compartidos", + "control_bottom_app_bar_create_new_album": "Crear novo ÃĄlbum", + "control_bottom_app_bar_delete_from_immich": "Eliminar de Immich", + "control_bottom_app_bar_delete_from_local": "Eliminar do dispositivo", + "control_bottom_app_bar_edit_location": "Editar ubicaciÃŗn", + "control_bottom_app_bar_edit_time": "Editar Data e Hora", + "control_bottom_app_bar_share_link": "Compartir LigazÃŗn", + "control_bottom_app_bar_share_to": "Compartir Con", + "control_bottom_app_bar_trash_from_immich": "Mover ao Lixo", + "copied_image_to_clipboard": "Imaxe copiada ao portapapeis.", + "copied_to_clipboard": "Copiado ao portapapeis!", + "copy_error": "Erro ao copiar", + "copy_file_path": "Copiar ruta do ficheiro", + "copy_image": "Copiar Imaxe", + "copy_link": "Copiar ligazÃŗn", + "copy_link_to_clipboard": "Copiar ligazÃŗn ao portapapeis", + "copy_password": "Copiar contrasinal", + "copy_to_clipboard": "Copiar ao Portapapeis", + "country": "País", + "cover": "Portada", + "covers": "Portadas", + "create": "Crear", + "create_album": "Crear ÃĄlbum", + "create_album_page_untitled": "Sen título", + "create_library": "Crear Biblioteca", + "create_link": "Crear ligazÃŗn", + "create_link_to_share": "Crear ligazÃŗn para compartir", + "create_link_to_share_description": "Permitir que calquera persoa coa ligazÃŗn vexa a(s) foto(s) seleccionada(s)", + "create_new": "CREAR NOVO", + "create_new_person": "Crear nova persoa", + "create_new_person_hint": "Asignar activos seleccionados a unha nova persoa", + "create_new_user": "Crear novo usuario", + "create_shared_album_page_share_add_assets": "ENGADIR ACTIVOS", + "create_shared_album_page_share_select_photos": "Seleccionar Fotos", + "create_tag": "Crear etiqueta", + "create_tag_description": "Crear unha nova etiqueta. Para etiquetas aniÃąadas, introduza a ruta completa da etiqueta incluíndo barras inclinadas.", + "create_user": "Crear usuario", + "created": "Creado", + "crop": "Recortar", + "curated_object_page_title": "Cousas", + "current_device": "Dispositivo actual", + "current_server_address": "Enderezo do servidor actual", + "custom_locale": "ConfiguraciÃŗn Rexional Personalizada", + "custom_locale_description": "Formatar datas e nÃēmeros baseÃĄndose na lingua e a rexiÃŗn", + "daily_title_text_date": "E, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", + "dark": "Escuro", + "date_after": "Data posterior a", + "date_and_time": "Data e Hora", + "date_before": "Data anterior a", + "date_format": "E, d LLL, y â€ĸ H:mm", + "date_of_birth_saved": "Data de nacemento gardada correctamente", + "date_range": "Rango de datas", + "day": "Día", + "deduplicate_all": "Eliminar Duplicados Todos", + "deduplication_criteria_1": "TamaÃąo da imaxe en bytes", + "deduplication_criteria_2": "Reconto de datos EXIF", + "deduplication_info": "InformaciÃŗn de DeduplicaciÃŗn", + "deduplication_info_description": "Para preseleccionar automaticamente activos e eliminar duplicados masivamente, miramos:", + "default_locale": "ConfiguraciÃŗn Rexional Predeterminada", + "default_locale_description": "Formatar datas e nÃēmeros baseÃĄndose na configuraciÃŗn rexional do teu navegador", + "delete": "Eliminar", + "delete_album": "Eliminar ÃĄlbum", + "delete_api_key_prompt": "EstÃĄs seguro de que queres eliminar esta chave API?", + "delete_dialog_alert": "Estes elementos eliminaranse permanentemente de Immich e do teu dispositivo", + "delete_dialog_alert_local": "Estes elementos eliminaranse permanentemente do teu dispositivo pero aínda estarÃĄn dispoÃąibles no servidor Immich", + "delete_dialog_alert_local_non_backed_up": "AlgÃēns dos elementos non teÃąen copia de seguridade en Immich e eliminaranse permanentemente do teu dispositivo", + "delete_dialog_alert_remote": "Estes elementos eliminaranse permanentemente do servidor Immich", + "delete_dialog_ok_force": "Eliminar Igualmente", + "delete_dialog_title": "Eliminar Permanentemente", + "delete_duplicates_confirmation": "EstÃĄs seguro de que queres eliminar permanentemente estes duplicados?", + "delete_face": "Eliminar cara", + "delete_key": "Eliminar chave", + "delete_library": "Eliminar Biblioteca", + "delete_link": "Eliminar ligazÃŗn", + "delete_local_dialog_ok_backed_up_only": "Eliminar SÃŗ con Copia de Seguridade", + "delete_local_dialog_ok_force": "Eliminar Igualmente", + "delete_others": "Eliminar outros", + "delete_shared_link": "Eliminar ligazÃŗn compartida", + "delete_shared_link_dialog_title": "Eliminar LigazÃŗn Compartida", + "delete_tag": "Eliminar etiqueta", + "delete_tag_confirmation_prompt": "EstÃĄs seguro de que queres eliminar a etiqueta {tagName}?", + "delete_user": "Eliminar usuario", + "deleted_shared_link": "LigazÃŗn compartida eliminada", + "deletes_missing_assets": "Elimina activos que faltan no disco", + "description": "DescriciÃŗn", + "description_input_hint_text": "Engadir descriciÃŗn...", + "description_input_submit_error": "Erro ao actualizar a descriciÃŗn, comprobe o rexistro para mÃĄis detalles", + "details": "Detalles", + "direction": "DirecciÃŗn", + "disabled": "Desactivado", + "disallow_edits": "Non permitir ediciÃŗns", + "discover": "Descubrir", + "dismiss_all_errors": "Descartar todos os erros", + "dismiss_error": "Descartar erro", + "display_options": "OpciÃŗns de visualizaciÃŗn", + "display_order": "Orde de visualizaciÃŗn", + "display_original_photos": "Mostrar fotos orixinais", + "display_original_photos_setting_description": "Preferir mostrar a foto orixinal ao ver un activo en lugar de miniaturas cando o activo orixinal Ê compatible coa web. Isto pode resultar en velocidades de visualizaciÃŗn de fotos mÃĄis lentas.", + "do_not_show_again": "Non mostrar esta mensaxe de novo", + "documentation": "DocumentaciÃŗn", + "done": "Feito", + "download": "Descargar", + "download_canceled": "Descarga cancelada", + "download_complete": "Descarga completada", + "download_enqueue": "Descarga en cola", + "download_error": "Erro na Descarga", + "download_failed": "Descarga fallada", + "download_filename": "ficheiro: {filename}", + "download_finished": "Descarga finalizada", + "download_include_embedded_motion_videos": "Vídeos incrustados", + "download_include_embedded_motion_videos_description": "Incluír vídeos incrustados en fotos en movemento como un ficheiro separado", + "download_notfound": "Descarga non atopada", + "download_paused": "Descarga pausada", + "download_settings": "Descarga", + "download_settings_description": "Xestionar configuraciÃŗns relacionadas coa descarga de activos", + "download_started": "Descarga iniciada", + "download_sucess": "Descarga exitosa", + "download_sucess_android": "Os medios descargÃĄronse en DCIM/Immich", + "download_waiting_to_retry": "Agardando para reintentar", + "downloading": "Descargando", + "downloading_asset_filename": "Descargando activo {filename}", + "downloading_media": "Descargando medios", + "drop_files_to_upload": "Solte ficheiros en calquera lugar para cargar", + "duplicates": "Duplicados", + "duplicates_description": "Resolve cada grupo indicando cales, se os houber, son duplicados", + "duration": "DuraciÃŗn", + "edit": "Editar", + "edit_album": "Editar ÃĄlbum", + "edit_avatar": "Editar avatar", + "edit_date": "Editar data", + "edit_date_and_time": "Editar data e hora", + "edit_exclusion_pattern": "Editar padrÃŗn de exclusiÃŗn", + "edit_faces": "Editar caras", + "edit_import_path": "Editar ruta de importaciÃŗn", + "edit_import_paths": "Editar Rutas de ImportaciÃŗn", + "edit_key": "Editar chave", + "edit_link": "Editar ligazÃŗn", + "edit_location": "Editar ubicaciÃŗn", + "edit_location_dialog_title": "UbicaciÃŗn", + "edit_name": "Editar nome", + "edit_people": "Editar persoas", + "edit_tag": "Editar etiqueta", + "edit_title": "Editar Título", + "edit_user": "Editar usuario", + "edited": "Editado", + "editor_close_without_save_prompt": "Os cambios non se gardarÃĄn", + "editor_close_without_save_title": "Pechar editor?", + "editor_crop_tool_h2_aspect_ratios": "ProporciÃŗns de aspecto", + "editor_crop_tool_h2_rotation": "RotaciÃŗn", + "email": "Correo electrÃŗnico", + "empty_folder": "Este cartafol estÃĄ baleiro", + "empty_trash": "Baleirar lixo", + "empty_trash_confirmation": "EstÃĄs seguro de que queres baleirar o lixo? Isto eliminarÃĄ permanentemente todos os activos no lixo de Immich. Non podes desfacer esta acciÃŗn!", + "enable": "Activar", + "enabled": "Activado", + "end_date": "Data de fin", + "enqueued": "En cola", + "enter_wifi_name": "Introducir nome da wifi", + "error": "Erro", + "error_change_sort_album": "Erro ao cambiar a orde de clasificaciÃŗn do ÃĄlbum", + "error_delete_face": "Erro ao eliminar a cara do activo", + "error_loading_image": "Erro ao cargar a imaxe", + "error_saving_image": "Erro: {error}", + "error_title": "Erro - Algo saíu mal", + "errors": { + "cannot_navigate_next_asset": "Non se pode navegar ao seguinte activo", + "cannot_navigate_previous_asset": "Non se pode navegar ao activo anterior", + "cant_apply_changes": "Non se poden aplicar os cambios", + "cant_change_activity": "Non se pode {enabled, select, true {desactivar} other {activar}} a actividade", + "cant_change_asset_favorite": "Non se pode cambiar o favorito do activo", + "cant_change_metadata_assets_count": "Non se poden cambiar os metadatos de {count, plural, one {# activo} other {# activos}}", + "cant_get_faces": "Non se poden obter caras", + "cant_get_number_of_comments": "Non se pode obter o nÃēmero de comentarios", + "cant_search_people": "Non se poden buscar persoas", + "cant_search_places": "Non se poden buscar lugares", + "cleared_jobs": "Traballos borrados para: {job}", + "error_adding_assets_to_album": "Erro ao engadir activos ao ÃĄlbum", + "error_adding_users_to_album": "Erro ao engadir usuarios ao ÃĄlbum", + "error_deleting_shared_user": "Erro ao eliminar o usuario compartido", + "error_downloading": "Erro ao descargar {filename}", + "error_hiding_buy_button": "Erro ao ocultar o botÃŗn de compra", + "error_removing_assets_from_album": "Erro ao eliminar activos do ÃĄlbum, comprobe a consola para mÃĄis detalles", + "error_selecting_all_assets": "Erro ao seleccionar todos os activos", + "exclusion_pattern_already_exists": "Este padrÃŗn de exclusiÃŗn xa existe.", + "failed_job_command": "O comando {command} fallou para o traballo: {job}", + "failed_to_create_album": "Erro ao crear o ÃĄlbum", + "failed_to_create_shared_link": "Erro ao crear a ligazÃŗn compartida", + "failed_to_edit_shared_link": "Erro ao editar a ligazÃŗn compartida", + "failed_to_get_people": "Erro ao obter persoas", + "failed_to_keep_this_delete_others": "Erro ao conservar este activo e eliminar os outros activos", + "failed_to_load_asset": "Erro ao cargar o activo", + "failed_to_load_assets": "Erro ao cargar activos", + "failed_to_load_notifications": "Erro ao cargar as notificaciÃŗns", + "failed_to_load_people": "Erro ao cargar persoas", + "failed_to_remove_product_key": "Erro ao eliminar a chave do produto", + "failed_to_stack_assets": "Erro ao apilar activos", + "failed_to_unstack_assets": "Erro ao desapilar activos", + "failed_to_update_notification_status": "Erro ao actualizar o estado das notificaciÃŗns", + "import_path_already_exists": "Esta ruta de importaciÃŗn xa existe.", + "incorrect_email_or_password": "Correo electrÃŗnico ou contrasinal incorrectos", + "paths_validation_failed": "{paths, plural, one {# ruta fallou} other {# rutas fallaron}} na validaciÃŗn", + "profile_picture_transparent_pixels": "As imaxes de perfil non poden ter píxeles transparentes. Por favor, faga zoom e/ou mova a imaxe.", + "quota_higher_than_disk_size": "Estableceu unha cota superior ao tamaÃąo do disco", + "repair_unable_to_check_items": "Non se puideron comprobar {count, select, one {elemento} other {elementos}}", + "unable_to_add_album_users": "Non se puideron engadir usuarios ao ÃĄlbum", + "unable_to_add_assets_to_shared_link": "Non se puideron engadir activos ÃĄ ligazÃŗn compartida", + "unable_to_add_comment": "Non se puido engadir o comentario", + "unable_to_add_exclusion_pattern": "Non se puido engadir o padrÃŗn de exclusiÃŗn", + "unable_to_add_import_path": "Non se puido engadir a ruta de importaciÃŗn", + "unable_to_add_partners": "Non se puideron engadir compaÃąeiros/as", + "unable_to_add_remove_archive": "Non se puido {archived, select, true {eliminar activo do} other {engadir activo ao}} arquivo", + "unable_to_add_remove_favorites": "Non se puido {favorite, select, true {engadir activo a} other {eliminar activo de}} favoritos", + "unable_to_archive_unarchive": "Non se puido {archived, select, true {arquivar} other {desarquivar}}", + "unable_to_change_album_user_role": "Non se puido cambiar o rol do usuario do ÃĄlbum", + "unable_to_change_date": "Non se puido cambiar a data", + "unable_to_change_favorite": "Non se puido cambiar o favorito do activo", + "unable_to_change_location": "Non se puido cambiar a ubicaciÃŗn", + "unable_to_change_password": "Non se puido cambiar o contrasinal", + "unable_to_change_visibility": "Non se puido cambiar a visibilidade para {count, plural, one {# persoa} other {# persoas}}", + "unable_to_complete_oauth_login": "Non se puido completar o inicio de sesiÃŗn OAuth", + "unable_to_connect": "Non se puido conectar", + "unable_to_connect_to_server": "Non se puido conectar ao servidor", + "unable_to_copy_to_clipboard": "Non se puido copiar ao portapapeis, asegÃērate de acceder ÃĄ pÃĄxina a travÊs de https", + "unable_to_create_admin_account": "Non se puido crear a conta de administrador", + "unable_to_create_api_key": "Non se puido crear unha nova Chave API", + "unable_to_create_library": "Non se puido crear a biblioteca", + "unable_to_create_user": "Non se puido crear o usuario", + "unable_to_delete_album": "Non se puido eliminar o ÃĄlbum", + "unable_to_delete_asset": "Non se puido eliminar o activo", + "unable_to_delete_assets": "Erro ao eliminar activos", + "unable_to_delete_exclusion_pattern": "Non se puido eliminar o padrÃŗn de exclusiÃŗn", + "unable_to_delete_import_path": "Non se puido eliminar a ruta de importaciÃŗn", + "unable_to_delete_shared_link": "Non se puido eliminar a ligazÃŗn compartida", + "unable_to_delete_user": "Non se puido eliminar o usuario", + "unable_to_download_files": "Non se puideron descargar os ficheiros", + "unable_to_edit_exclusion_pattern": "Non se puido editar o padrÃŗn de exclusiÃŗn", + "unable_to_edit_import_path": "Non se puido editar a ruta de importaciÃŗn", + "unable_to_empty_trash": "Non se puido baleirar o lixo", + "unable_to_enter_fullscreen": "Non se puido entrar en pantalla completa", + "unable_to_exit_fullscreen": "Non se puido saír da pantalla completa", + "unable_to_get_comments_number": "Non se puido obter o nÃēmero de comentarios", + "unable_to_get_shared_link": "Erro ao obter a ligazÃŗn compartida", + "unable_to_hide_person": "Non se puido ocultar a persoa", + "unable_to_link_motion_video": "Non se puido ligar o vídeo en movemento", + "unable_to_link_oauth_account": "Non se puido ligar a conta OAuth", + "unable_to_load_album": "Non se puido cargar o ÃĄlbum", + "unable_to_load_asset_activity": "Non se puido cargar a actividade do activo", + "unable_to_load_items": "Non se puideron cargar os elementos", + "unable_to_load_liked_status": "Non se puido cargar o estado de gustar", + "unable_to_log_out_all_devices": "Non se puido pechar sesiÃŗn en todos os dispositivos", + "unable_to_log_out_device": "Non se puido pechar sesiÃŗn no dispositivo", + "unable_to_login_with_oauth": "Non se puido iniciar sesiÃŗn con OAuth", + "unable_to_play_video": "Non se puido reproducir o vídeo", + "unable_to_reassign_assets_existing_person": "Non se puideron reasignar activos a {name, select, null {unha persoa existente} other {{name}}}", + "unable_to_reassign_assets_new_person": "Non se puideron reasignar activos a unha nova persoa", + "unable_to_refresh_user": "Non se puido actualizar o usuario", + "unable_to_remove_album_users": "Non se puideron eliminar usuarios do ÃĄlbum", + "unable_to_remove_api_key": "Non se puido eliminar a Chave API", + "unable_to_remove_assets_from_shared_link": "Non se puideron eliminar activos da ligazÃŗn compartida", + "unable_to_remove_deleted_assets": "Non se puideron eliminar ficheiros fÃŗra de liÃąa", + "unable_to_remove_library": "Non se puido eliminar a biblioteca", + "unable_to_remove_partner": "Non se puido eliminar o/a compaÃąeiro/a", + "unable_to_remove_reaction": "Non se puido eliminar a reacciÃŗn", + "unable_to_repair_items": "Non se puideron reparar os elementos", + "unable_to_reset_password": "Non se puido restablecer o contrasinal", + "unable_to_resolve_duplicate": "Non se puido resolver o duplicado", + "unable_to_restore_assets": "Non se puideron restaurar os activos", + "unable_to_restore_trash": "Non se puido restaurar o lixo", + "unable_to_restore_user": "Non se puido restaurar o usuario", + "unable_to_save_album": "Non se puido gardar o ÃĄlbum", + "unable_to_save_api_key": "Non se puido gardar a Chave API", + "unable_to_save_date_of_birth": "Non se puido gardar a data de nacemento", + "unable_to_save_name": "Non se puido gardar o nome", + "unable_to_save_profile": "Non se puido gardar o perfil", + "unable_to_save_settings": "Non se puido gardar a configuraciÃŗn", + "unable_to_scan_libraries": "Non se puideron escanear as bibliotecas", + "unable_to_scan_library": "Non se puido escanear a biblioteca", + "unable_to_set_feature_photo": "Non se puido establecer a foto destacada", + "unable_to_set_profile_picture": "Non se puido establecer a imaxe de perfil", + "unable_to_submit_job": "Non se puido enviar o traballo", + "unable_to_trash_asset": "Non se puido mover o activo ao lixo", + "unable_to_unlink_account": "Non se puido desvincular a conta", + "unable_to_unlink_motion_video": "Non se puido desvincular o vídeo en movemento", + "unable_to_update_album_cover": "Non se puido actualizar a portada do ÃĄlbum", + "unable_to_update_album_info": "Non se puido actualizar a informaciÃŗn do ÃĄlbum", + "unable_to_update_library": "Non se puido actualizar a biblioteca", + "unable_to_update_location": "Non se puido actualizar a ubicaciÃŗn", + "unable_to_update_settings": "Non se puido actualizar a configuraciÃŗn", + "unable_to_update_timeline_display_status": "Non se puido actualizar o estado de visualizaciÃŗn da liÃąa de tempo", + "unable_to_update_user": "Non se puido actualizar o usuario", + "unable_to_upload_file": "Non se puido cargar o ficheiro" + }, + "exif_bottom_sheet_description": "Engadir DescriciÃŗn...", + "exif_bottom_sheet_details": "DETALLES", + "exif_bottom_sheet_location": "UBICACIÓN", + "exif_bottom_sheet_people": "PERSOAS", + "exif_bottom_sheet_person_add_person": "Engadir nome", + "exif_bottom_sheet_person_age": "Idade {age}", + "exif_bottom_sheet_person_age_months": "Idade {months} meses", + "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", + "exif_bottom_sheet_person_age_years": "Idade {years}", + "exit_slideshow": "Saír da PresentaciÃŗn", + "expand_all": "Expandir todo", + "experimental_settings_new_asset_list_subtitle": "Traballo en progreso", + "experimental_settings_new_asset_list_title": "Activar grella de fotos experimental", + "experimental_settings_subtitle": "Use baixo o teu propio risco!", + "expire_after": "Caduca despois de", + "expired": "Caducado", + "expires_date": "Caduca o {date}", + "explore": "Explorar", + "explorer": "Explorador", + "export": "Exportar", + "export_as_json": "Exportar como JSON", + "extension": "ExtensiÃŗn", + "external": "Externo", + "external_libraries": "Bibliotecas Externas", + "external_network": "Rede externa", + "external_network_sheet_info": "Cando non estea na rede wifi preferida, a aplicaciÃŗn conectarase ao servidor a travÊs da primeira das seguintes URLs que poida alcanzar, comezando de arriba a abaixo", + "face_unassigned": "Sen asignar", + "failed": "Fallado", + "failed_to_load_assets": "Erro ao cargar activos", + "failed_to_load_folder": "Erro ao cargar o cartafol", + "favorite": "Favorito", + "favorite_or_unfavorite_photo": "Marcar ou desmarcar como favorito", + "favorites": "Favoritos", + "favorites_page_no_favorites": "Non se atoparon activos favoritos", + "feature_photo_updated": "Foto destacada actualizada", + "features": "FunciÃŗns", + "features_setting_description": "Xestionar as funciÃŗns da aplicaciÃŗn", + "file_name": "Nome do ficheiro", + "file_name_or_extension": "Nome do ficheiro ou extensiÃŗn", + "filename": "Nome do ficheiro", + "filetype": "Tipo de ficheiro", + "filter": "Filtro", + "filter_people": "Filtrar persoas", + "filter_places": "Filtrar lugares", + "find_them_fast": "AtÃŗpaos rÃĄpido por nome coa busca", + "fix_incorrect_match": "Corrixir coincidencia incorrecta", + "folder": "Cartafol", + "folder_not_found": "Cartafol non atopado", + "folders": "Cartafoles", + "folders_feature_description": "Navegar pola vista de cartafoles para as fotos e vídeos no sistema de ficheiros", + "forward": "Adiante", + "general": "Xeral", + "get_help": "Obter Axuda", + "get_wifiname_error": "Non se puido obter o nome da wifi. AsegÃērate de que concedeu os permisos necesarios e estÃĄ conectado a unha rede wifi", + "getting_started": "Primeiros Pasos", + "go_back": "Volver", + "go_to_folder": "Ir ao cartafol", + "go_to_search": "Ir ÃĄ busca", + "grant_permission": "Conceder permiso", + "group_albums_by": "Agrupar ÃĄlbums por...", + "group_country": "Agrupar por país", + "group_no": "Sen agrupaciÃŗn", + "group_owner": "Agrupar por propietario", + "group_places_by": "Agrupar lugares por...", + "group_year": "Agrupar por ano", + "haptic_feedback_switch": "Activar resposta hÃĄptica", + "haptic_feedback_title": "Resposta HÃĄptica", + "has_quota": "Ten cota", + "header_settings_add_header_tip": "Engadir Cabeceira", + "header_settings_field_validator_msg": "O valor non pode estar baleiro", + "header_settings_header_name_input": "Nome da cabeceira", + "header_settings_header_value_input": "Valor da cabeceira", + "headers_settings_tile_subtitle": "Definir cabeceiras de proxy que a aplicaciÃŗn debería enviar con cada solicitude de rede", + "headers_settings_tile_title": "Cabeceiras de proxy personalizadas", + "hi_user": "Ola {name} ({email})", + "hide_all_people": "Ocultar todas as persoas", + "hide_gallery": "Ocultar galería", + "hide_named_person": "Ocultar persoa {name}", + "hide_password": "Ocultar contrasinal", + "hide_person": "Ocultar persoa", + "hide_unnamed_people": "Ocultar persoas sen nome", + "home_page_add_to_album_conflicts": "Engadidos {added} activos ao ÃĄlbum {album}. {failed} activos xa estÃĄn no ÃĄlbum.", + "home_page_add_to_album_err_local": "Non se poden engadir activos locais a ÃĄlbums aínda, omitindo", + "home_page_add_to_album_success": "Engadidos {added} activos ao ÃĄlbum {album}.", + "home_page_album_err_partner": "Non se poden engadir activos de compaÃąeiro/a a un ÃĄlbum aínda, omitindo", + "home_page_archive_err_local": "Non se poden arquivar activos locais aínda, omitindo", + "home_page_archive_err_partner": "Non se poden arquivar activos de compaÃąeiro/a, omitindo", + "home_page_building_timeline": "Construíndo a liÃąa de tempo", + "home_page_delete_err_partner": "Non se poden eliminar activos de compaÃąeiro/a, omitindo", + "home_page_delete_remote_err_local": "Activos locais na selecciÃŗn de eliminaciÃŗn remota, omitindo", + "home_page_favorite_err_local": "Non se poden marcar como favoritos activos locais aínda, omitindo", + "home_page_favorite_err_partner": "Non se poden marcar como favoritos activos de compaÃąeiro/a aínda, omitindo", + "home_page_first_time_notice": "Se esta Ê a primeira vez que usas a aplicaciÃŗn, asegÃērate de elixir un ÃĄlbum de copia de seguridade para que a liÃąa de tempo poida encherse con fotos e vídeos nel", + "home_page_share_err_local": "Non se poden compartir activos locais mediante ligazÃŗn, omitindo", + "home_page_upload_err_limit": "SÃŗ se pode cargar un mÃĄximo de 30 activos ÃĄ vez, omitindo", + "hour": "Hora", + "ignore_icloud_photos": "Ignorar fotos de iCloud", + "ignore_icloud_photos_description": "As fotos que estÃĄn almacenadas en iCloud non se cargarÃĄn ao servidor Immich", + "image": "Imaxe", + "image_alt_text_date": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a o {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a con {person1} o {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a con {person1} e {person2} o {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a con {person1}, {person2} e {person3} o {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a con {person1}, {person2} e {additionalCount, number} outros/as o {date}", + "image_alt_text_date_place": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a en {city}, {country} o {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a en {city}, {country} con {person1} o {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a en {city}, {country} con {person1} e {person2} o {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a en {city}, {country} con {person1}, {person2} e {person3} o {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Vídeo} other {Imaxe}} tomado/a en {city}, {country} con {person1}, {person2} e {additionalCount, number} outros/as o {date}", + "image_saved_successfully": "Imaxe gardada", + "image_viewer_page_state_provider_download_started": "Descarga Iniciada", + "image_viewer_page_state_provider_download_success": "Descarga Exitosa", + "image_viewer_page_state_provider_share_error": "Erro ao Compartir", + "immich_logo": "Logo de Immich", + "immich_web_interface": "Interface Web de Immich", + "import_from_json": "Importar desde JSON", + "import_path": "Ruta de importaciÃŗn", + "in_albums": "En {count, plural, one {# ÃĄlbum} other {# ÃĄlbums}}", + "in_archive": "No arquivo", + "include_archived": "Incluír arquivados", + "include_shared_albums": "Incluír ÃĄlbums compartidos", + "include_shared_partner_assets": "Incluír activos de compaÃąeiro/a compartidos", + "individual_share": "Compartir individual", + "individual_shares": "Compartires individuais", + "info": "InformaciÃŗn", + "interval": { + "day_at_onepm": "Todos os días ÃĄs 13:00", + "hours": "Cada {hours, plural, one {hora} other {{hours, number} horas}}", + "night_at_midnight": "Todas as noites ÃĄ medianoite", + "night_at_twoam": "Todas as noites ÃĄs 2:00" + }, + "invalid_date": "Data invÃĄlida", + "invalid_date_format": "Formato de data invÃĄlido", + "invite_people": "Invitar Persoas", + "invite_to_album": "Invitar ao ÃĄlbum", + "items_count": "{count, plural, one {# elemento} other {# elementos}}", + "jobs": "Traballos", + "keep": "Conservar", + "keep_all": "Conservar Todo", + "keep_this_delete_others": "Conservar este, eliminar outros", + "kept_this_deleted_others": "Conservouse este activo e eliminÃĄronse {count, plural, one {# activo} other {# activos}}", + "keyboard_shortcuts": "Atallos de teclado", + "language": "Lingua", + "language_setting_description": "Seleccione a tÃēa lingua preferida", + "last_seen": "Visto por Ãēltima vez", + "latest_version": "Última VersiÃŗn", + "leave": "Saír", + "lens_model": "Modelo da lente", + "let_others_respond": "Permitir que outros respondan", + "level": "Nivel", + "library": "Biblioteca", + "library_options": "OpciÃŗns da biblioteca", + "library_page_device_albums": "Álbums no Dispositivo", + "library_page_new_album": "Novo ÃĄlbum", + "library_page_sort_asset_count": "NÃēmero de activos", + "library_page_sort_created": "Data de creaciÃŗn", + "library_page_sort_last_modified": "Última modificaciÃŗn", + "library_page_sort_title": "Título do ÃĄlbum", + "light": "Claro", + "like_deleted": "GÃēstame eliminado", + "link_motion_video": "Ligar vídeo en movemento", + "link_options": "OpciÃŗns da ligazÃŗn", + "link_to_oauth": "Ligar a OAuth", + "linked_oauth_account": "Conta OAuth ligada", + "list": "Lista", + "loading": "Cargando", + "loading_search_results_failed": "Erro ao cargar os resultados da busca", + "local_network": "Rede local", + "local_network_sheet_info": "A aplicaciÃŗn conectarase ao servidor a travÊs desta URL cando use a rede wifi especificada", + "location_permission": "Permiso de ubicaciÃŗn", + "location_permission_content": "Para usar a funciÃŗn de cambio automÃĄtico, Immich necesita permiso de ubicaciÃŗn precisa para poder ler o nome da rede wifi actual", + "location_picker_choose_on_map": "Elixir no mapa", + "location_picker_latitude_error": "Introducir unha latitude vÃĄlida", + "location_picker_latitude_hint": "Introduza a tÃēa latitude aquí", + "location_picker_longitude_error": "Introducir unha lonxitude vÃĄlida", + "location_picker_longitude_hint": "Introduza a tÃēa lonxitude aquí", + "log_out": "Pechar sesiÃŗn", + "log_out_all_devices": "Pechar SesiÃŗn en Todos os Dispositivos", + "logged_out_all_devices": "Pechouse sesiÃŗn en todos os dispositivos", + "logged_out_device": "Pechouse sesiÃŗn no dispositivo", + "login": "Iniciar sesiÃŗn", + "login_disabled": "O inicio de sesiÃŗn foi desactivado", + "login_form_api_exception": "ExcepciÃŗn da API. Por favor, comprobe a URL do servidor e intÊnteo de novo.", + "login_form_back_button_text": "AtrÃĄs", + "login_form_email_hint": "oteuemail@email.com", + "login_form_endpoint_hint": "http://ip-do-teu-servidor:porto", + "login_form_endpoint_url": "URL do Punto Final do Servidor", + "login_form_err_http": "Por favor, especifique http:// ou https://", + "login_form_err_invalid_email": "Correo electrÃŗnico invÃĄlido", + "login_form_err_invalid_url": "URL invÃĄlida", + "login_form_err_leading_whitespace": "Espazo en branco inicial", + "login_form_err_trailing_whitespace": "Espazo en branco final", + "login_form_failed_get_oauth_server_config": "Erro ao iniciar sesiÃŗn usando OAuth, comprobe a URL do servidor", + "login_form_failed_get_oauth_server_disable": "A funciÃŗn OAuth non estÃĄ dispoÃąible neste servidor", + "login_form_failed_login": "Erro ao iniciar sesiÃŗn, comproba a URL do servidor, correo electrÃŗnico e contrasinal", + "login_form_handshake_exception": "Houbo unha ExcepciÃŗn de Handshake co servidor. Activa o soporte para certificados autofirmados nas configuraciÃŗns se estÃĄs a usar un certificado autofirmado.", + "login_form_password_hint": "contrasinal", + "login_form_save_login": "Manter sesiÃŗn iniciada", + "login_form_server_empty": "Introduza unha URL do servidor.", + "login_form_server_error": "Non se puido conectar co servidor.", + "login_has_been_disabled": "O inicio de sesiÃŗn foi desactivado.", + "login_password_changed_error": "Houbo un erro ao actualizar o teu contrasinal", + "login_password_changed_success": "Contrasinal actualizado correctamente", + "logout_all_device_confirmation": "EstÃĄs seguro de que queres pechar sesiÃŗn en todos os dispositivos?", + "logout_this_device_confirmation": "EstÃĄs seguro de que queres pechar sesiÃŗn neste dispositivo?", + "longitude": "Lonxitude", + "look": "Ollar", + "loop_videos": "Reproducir vídeos en bucle", + "loop_videos_description": "Activar para reproducir automaticamente un vídeo en bucle no visor de detalles.", + "main_branch_warning": "EstÃĄ a usar unha versiÃŗn de desenvolvemento; recomendamos encarecidamente usar unha versiÃŗn de lanzamento!", + "main_menu": "MenÃē principal", + "make": "Marca", + "manage_shared_links": "Xestionar ligazÃŗns compartidas", + "manage_sharing_with_partners": "Xestionar comparticiÃŗn con compaÃąeiros/as", + "manage_the_app_settings": "Xestionar a configuraciÃŗn da aplicaciÃŗn", + "manage_your_account": "Xestionar a tÃēa conta", + "manage_your_api_keys": "Xestionar as tÃēas claves API", + "manage_your_devices": "Xestionar os teus dispositivos con sesiÃŗn iniciada", + "manage_your_oauth_connection": "Xestionar a tÃēa conexiÃŗn OAuth", + "map": "Mapa", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotos", + "map_cannot_get_user_location": "Non se pode obter a ubicaciÃŗn do usuario", + "map_location_dialog_yes": "Si", + "map_location_picker_page_use_location": "Usar esta ubicaciÃŗn", + "map_location_service_disabled_content": "O servizo de ubicaciÃŗn debe estar activado para mostrar activos da tÃēa ubicaciÃŗn actual. Queres activalo agora?", + "map_location_service_disabled_title": "Servizo de ubicaciÃŗn deshabilitado", + "map_marker_for_images": "Marcador de mapa para imaxes tomadas en {city}, {country}", + "map_marker_with_image": "Marcador de mapa con imaxe", + "map_no_assets_in_bounds": "Non hai fotos nesta ÃĄrea", + "map_no_location_permission_content": "Necesítase permiso de ubicaciÃŗn para mostrar activos da sÃēa ubicaciÃŗn actual. Queres permitilo agora?", + "map_no_location_permission_title": "Permiso de ubicaciÃŗn denegado", + "map_settings": "ConfiguraciÃŗn do mapa", + "map_settings_dark_mode": "Modo escuro", + "map_settings_date_range_option_day": "Últimas 24 horas", + "map_settings_date_range_option_days": "Últimos {days} días", + "map_settings_date_range_option_year": "Último ano", + "map_settings_date_range_option_years": "Últimos {years} anos", + "map_settings_dialog_title": "ConfiguraciÃŗn do Mapa", + "map_settings_include_show_archived": "Incluír Arquivados", + "map_settings_include_show_partners": "Incluír CompaÃąeiros/as", + "map_settings_only_show_favorites": "Mostrar SÃŗ Favoritos", + "map_settings_theme_settings": "Tema do Mapa", + "map_zoom_to_see_photos": "Alonxe o zoom para ver fotos", + "mark_all_as_read": "Marcar todo como lido", + "mark_as_read": "Marcar como lido", + "marked_all_as_read": "Marcado todo como lido", + "matches": "Coincidencias", + "media_type": "Tipo de medio", + "memories": "Recordos", + "memories_all_caught_up": "Todo ao día", + "memories_check_back_tomorrow": "Volva maÃąÃĄ para mÃĄis recordos", + "memories_setting_description": "Xestionar o que ves nos teus recordos", + "memories_start_over": "Comezar de novo", + "memories_swipe_to_close": "Deslizar cara arriba para pechar", + "memory": "Recordo", + "memory_lane_title": "CamiÃąo dos Recordos {title}", + "menu": "MenÃē", + "merge": "Fusionar", + "merge_people": "Fusionar persoas", + "merge_people_limit": "SÃŗ pode fusionar ata 5 caras ÃĄ vez", + "merge_people_prompt": "Queres fusionar estas persoas? Esta acciÃŗn Ê irreversible.", + "merge_people_successfully": "Persoas fusionadas correctamente", + "merged_people_count": "Fusionadas {count, plural, one {# persoa} other {# persoas}}", + "minimize": "Minimizar", + "minute": "Minuto", + "missing": "Faltantes", + "model": "Modelo", + "month": "Mes", + "more": "MÃĄis", + "moved_to_trash": "Movido ao lixo", + "multiselect_grid_edit_date_time_err_read_only": "Non se pode editar a data de activo(s) de sÃŗ lectura, omitindo", + "multiselect_grid_edit_gps_err_read_only": "Non se pode editar a ubicaciÃŗn de activo(s) de sÃŗ lectura, omitindo", + "mute_memories": "Silenciar Recordos", + "my_albums": "Os meus ÃĄlbums", + "name": "Nome", + "name_or_nickname": "Nome ou alcume", + "networking_settings": "Rede", + "networking_subtitle": "Xestionar a configuraciÃŗn do punto final do servidor", + "never": "Nunca", + "new_album": "Novo Álbum", + "new_api_key": "Nova Chave API", + "new_password": "Novo contrasinal", + "new_person": "Nova persoa", + "new_user_created": "Novo usuario creado", + "new_version_available": "NOVA VERSIÓN DISPOÑIBLE", + "newest_first": "MÃĄis recentes primeiro", + "next": "Seguinte", + "next_memory": "Seguinte recordo", + "no": "Non", + "no_albums_message": "Crea un ÃĄlbum para organizar as tÃēas fotos e vídeos", + "no_albums_with_name_yet": "Parece que aínda non tes ningÃēn ÃĄlbum con este nome.", + "no_albums_yet": "Parece que aínda non tes ningÃēn ÃĄlbum.", + "no_archived_assets_message": "Arquiva fotos e vídeos para ocultalos da tÃēa vista de Fotos", + "no_assets_message": "PREMA PARA CARGAR A SÚA PRIMEIRA FOTO", + "no_assets_to_show": "Non hai activos para mostrar", + "no_duplicates_found": "Non se atoparon duplicados.", + "no_exif_info_available": "Non hai informaciÃŗn exif dispoÃąible", + "no_explore_results_message": "Suba mÃĄis fotos para explorar a tÃēa colecciÃŗn.", + "no_favorites_message": "Engade favoritos para atopar rapidamente as tÃēas mellores fotos e vídeos", + "no_libraries_message": "Crea unha biblioteca externa para ver as tÃēas fotos e vídeos", + "no_name": "Sen Nome", + "no_notifications": "Sen notificaciÃŗns", + "no_places": "Sen lugares", + "no_results": "Sen resultados", + "no_results_description": "Proba cun sinÃŗnimo ou palabra chave mÃĄis xeral", + "no_shared_albums_message": "Crea un ÃĄlbum para compartir fotos e vídeos con persoas na tÃēa rede", + "not_in_any_album": "Non estÃĄ en ningÃēn ÃĄlbum", + "not_selected": "Non seleccionado", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar a Etiqueta de Almacenamento a activos cargados previamente, execute o", + "notes": "Notas", + "notification_permission_dialog_content": "Para activar as notificaciÃŗns, vaia a Axustes e seleccione permitir.", + "notification_permission_list_tile_content": "Conceda permiso para activar as notificaciÃŗns.", + "notification_permission_list_tile_enable_button": "Activar NotificaciÃŗns", + "notification_permission_list_tile_title": "Permiso de NotificaciÃŗn", + "notification_toggle_setting_description": "Activar notificaciÃŗns por correo electrÃŗnico", + "notifications": "NotificaciÃŗns", + "notifications_setting_description": "Xestionar notificaciÃŗns", + "official_immich_resources": "Recursos Oficiais de Immich", + "offline": "FÃŗra de liÃąa", + "offline_paths": "Rutas fÃŗra de liÃąa", + "offline_paths_description": "Estes resultados poden deberse ÃĄ eliminaciÃŗn manual de ficheiros que non forman parte dunha biblioteca externa.", + "ok": "Aceptar", + "oldest_first": "MÃĄis antigos primeiro", + "on_this_device": "Neste dispositivo", + "onboarding": "IncorporaciÃŗn", + "onboarding_privacy_description": "As seguintes funciÃŗns (opcionais) dependen de servizos externos e poden desactivarse en calquera momento na configuraciÃŗn da administraciÃŗn.", + "onboarding_theme_description": "Elixe un tema de cor para a tÃēa instancia. Podes cambialo mÃĄis tarde na tÃēa configuraciÃŗn.", + "onboarding_welcome_description": "Imos configurar a tÃēa instancia con algunhas configuraciÃŗns comÃēns.", + "onboarding_welcome_user": "Benvido/a, {user}", + "online": "En liÃąa", + "only_favorites": "SÃŗ favoritos", + "open": "Abrir", + "open_in_map_view": "Abrir na vista de mapa", + "open_in_openstreetmap": "Abrir en OpenStreetMap", + "open_the_search_filters": "Abrir os filtros de busca", + "options": "OpciÃŗns", + "or": "ou", + "organize_your_library": "Organizar a tÃēa biblioteca", + "original": "orixinal", + "other": "Outro", + "other_devices": "Outros dispositivos", + "other_variables": "Outras variables", + "owned": "Propio", + "owner": "Propietario", + "partner": "CompaÃąeiro/a", + "partner_can_access": "{partner} pode acceder a", + "partner_can_access_assets": "Todas as tÃēas fotos e vídeos excepto os de Arquivo e Eliminados", + "partner_can_access_location": "A ubicaciÃŗn onde se tomaron as tÃēas fotos", + "partner_list_user_photos": "Fotos de {user}", + "partner_list_view_all": "Ver todo", + "partner_page_empty_message": "As sÃēas fotos aínda non estÃĄn compartidas con ningÃēn compaÃąeiro/a.", + "partner_page_no_more_users": "Non hai mÃĄis usuarios para engadir", + "partner_page_partner_add_failed": "Erro ao engadir compaÃąeiro/a", + "partner_page_select_partner": "Seleccionar compaÃąeiro/a", + "partner_page_shared_to_title": "Compartido con", + "partner_page_stop_sharing_content": "{partner} xa non poderÃĄ acceder ÃĄs tÃēas fotos.", + "partner_sharing": "ComparticiÃŗn con CompaÃąeiro/a", + "partners": "CompaÃąeiros/as", + "password": "Contrasinal", + "password_does_not_match": "O contrasinal non coincide", + "password_required": "Requírese Contrasinal", + "password_reset_success": "Contrasinal restablecido correctamente", + "past_durations": { + "days": "Últimos {days, plural, one {día} other {# días}}", + "hours": "Últimas {hours, plural, one {hora} other {# horas}}", + "years": "Últimos {years, plural, one {ano} other {# anos}}" + }, + "path": "Ruta", + "pattern": "PadrÃŗn", + "pause": "Pausa", + "pause_memories": "Pausar recordos", + "paused": "Pausado", + "pending": "Pendente", + "people": "Persoas", + "people_edits_count": "Editadas {count, plural, one {# persoa} other {# persoas}}", + "people_feature_description": "Navegar por fotos e vídeos agrupados por persoas", + "people_sidebar_description": "Mostrar unha ligazÃŗn a Persoas na barra lateral", + "permanent_deletion_warning": "Aviso de eliminaciÃŗn permanente", + "permanent_deletion_warning_setting_description": "Mostrar un aviso ao eliminar permanentemente activos", + "permanently_delete": "Eliminar permanentemente", + "permanently_delete_assets_count": "Eliminar permanentemente {count, plural, one {activo} other {activos}}", + "permanently_delete_assets_prompt": "EstÃĄs seguro de que queres eliminar permanentemente {count, plural, one {este activo?} other {estes # activos?}} Isto tamÊn {count, plural, one {o eliminarÃĄ do teu} other {os eliminarÃĄ dos teus}} ÃĄlbum(s).", + "permanently_deleted_asset": "Activo eliminado permanentemente", + "permanently_deleted_assets_count": "Eliminados permanentemente {count, plural, one {# activo} other {# activos}}", + "permission_onboarding_back": "AtrÃĄs", + "permission_onboarding_continue_anyway": "Continuar de todos os xeitos", + "permission_onboarding_get_started": "Comezar", + "permission_onboarding_go_to_settings": "Ir a axustes", + "permission_onboarding_permission_denied": "Permiso denegado. Para usar Immich, conceda permisos de fotos e vídeos en Axustes.", + "permission_onboarding_permission_granted": "Permiso concedido! Xa estÃĄ todo listo.", + "permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich faga copia de seguridade e xestione toda a tÃēa colecciÃŗn da galería, conceda permisos de fotos e vídeos en ConfiguraciÃŗn.", + "permission_onboarding_request": "Immich require permiso para ver as tÃēas fotos e vídeos.", + "person": "Persoa", + "person_birthdate": "Nacido/a o {date}", + "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", + "photo_shared_all_users": "Parece que compartiches as tÃēas fotos con todos os usuarios ou non tes ningÃēn usuario co que compartir.", + "photos": "Fotos", + "photos_and_videos": "Fotos e Vídeos", + "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", + "photos_from_previous_years": "Fotos de anos anteriores", + "pick_a_location": "Elixir unha ubicaciÃŗn", + "place": "Lugar", + "places": "Lugares", + "places_count": "{count, plural, one {{count, number} Lugar} other {{count, number} Lugares}}", + "play": "Reproducir", + "play_memories": "Reproducir recordos", + "play_motion_photo": "Reproducir Foto en Movemento", + "play_or_pause_video": "Reproducir ou pausar vídeo", + "port": "Porto", + "preferences_settings_subtitle": "Xestionar as preferencias da aplicaciÃŗn", + "preferences_settings_title": "Preferencias", + "preset": "Preaxuste", + "preview": "Vista previa", + "previous": "Anterior", + "previous_memory": "Recordo anterior", + "previous_or_next_photo": "Foto anterior ou seguinte", + "primary": "Principal", + "privacy": "Privacidade", + "profile_drawer_app_logs": "Rexistros", + "profile_drawer_client_out_of_date_major": "A aplicaciÃŗn mÃŗbil estÃĄ desactualizada. Por favor, actualice ÃĄ Ãēltima versiÃŗn maior.", + "profile_drawer_client_out_of_date_minor": "A aplicaciÃŗn mÃŗbil estÃĄ desactualizada. Por favor, actualice ÃĄ Ãēltima versiÃŗn menor.", + "profile_drawer_client_server_up_to_date": "Cliente e Servidor estÃĄn actualizados", + "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desactualizado. Por favor, actualice ÃĄ Ãēltima versiÃŗn maior.", + "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desactualizado. Por favor, actualice ÃĄ Ãēltima versiÃŗn menor.", + "profile_image_of_user": "Imaxe de perfil de {user}", + "profile_picture_set": "Imaxe de perfil establecida.", + "public_album": "Álbum pÃēblico", + "public_share": "Compartir PÃēblico", + "purchase_account_info": "Seguidor/a", + "purchase_activated_subtitle": "Grazas por apoiar Immich e o software de cÃŗdigo aberto", + "purchase_activated_time": "Activado o {date}", + "purchase_activated_title": "A sÃēa chave activouse correctamente", + "purchase_button_activate": "Activar", + "purchase_button_buy": "Comprar", + "purchase_button_buy_immich": "Comprar Immich", + "purchase_button_never_show_again": "Non mostrar nunca mÃĄis", + "purchase_button_reminder": "Lembrarme en 30 días", + "purchase_button_remove_key": "Eliminar chave", + "purchase_button_select": "Seleccionar", + "purchase_failed_activation": "Erro ao activar! Por favor, comproba o teu correo electrÃŗnico para a chave do produto correcta!", + "purchase_individual_description_1": "Para un individuo", + "purchase_individual_description_2": "Estado de seguidor/a", + "purchase_input_suggestion": "Ten unha chave de produto? Introduza a chave a continuaciÃŗn", + "purchase_license_subtitle": "Compre Immich para apoiar o desenvolvemento continuado do servizo", + "purchase_lifetime_description": "Compra vitalicia", + "purchase_option_title": "OPCIÓNS DE COMPRA", + "purchase_panel_info_1": "Construír Immich leva moito tempo e esforzo, e temos enxeÃąeiros a tempo completo traballando nel para facelo o mellor posible. A nosa misiÃŗn Ê que o software de cÃŗdigo aberto e as prÃĄcticas comerciais Êticas se convertan nunha fonte de ingresos sostible para os desenvolvedores e crear un ecosistema respectuoso coa privacidade con alternativas reais aos servizos na nube explotadores.", + "purchase_panel_info_2": "Como estamos comprometidos a non engadir muros de pago, esta compra non che outorgarÃĄ ningunha funciÃŗn adicional en Immich. Dependemos de usuarios coma ti para apoiar o desenvolvemento continuo de Immich.", + "purchase_panel_title": "Apoiar o proxecto", + "purchase_per_server": "Por servidor", + "purchase_per_user": "Por usuario", + "purchase_remove_product_key": "Eliminar Chave do Produto", + "purchase_remove_product_key_prompt": "EstÃĄs seguro de que queres eliminar a chave do produto?", + "purchase_remove_server_product_key": "Eliminar chave do produto do Servidor", + "purchase_remove_server_product_key_prompt": "EstÃĄs seguro de que queres eliminar a chave do produto do Servidor?", + "purchase_server_description_1": "Para todo o servidor", + "purchase_server_description_2": "Estado de seguidor/a", + "purchase_server_title": "Servidor", + "purchase_settings_server_activated": "A chave do produto do servidor Ê xestionada polo administrador", + "rating": "ClasificaciÃŗn por estrelas", + "rating_clear": "Borrar clasificaciÃŗn", + "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", + "rating_description": "Mostrar a clasificaciÃŗn EXIF no panel de informaciÃŗn", + "reaction_options": "OpciÃŗns de reacciÃŗn", + "read_changelog": "Ler Rexistro de Cambios", + "reassign": "Reasignar", + "reassigned_assets_to_existing_person": "Reasignados {count, plural, one {# activo} other {# activos}} a {name, select, null {unha persoa existente} other {{name}}}", + "reassigned_assets_to_new_person": "Reasignados {count, plural, one {# activo} other {# activos}} a unha nova persoa", + "reassing_hint": "Asignar activos seleccionados a unha persoa existente", + "recent": "Recente", + "recent-albums": "Álbums recentes", + "recent_searches": "Buscas recentes", + "recently_added": "Engadido recentemente", + "recently_added_page_title": "Engadido Recentemente", + "recently_taken": "Recentemente tomado", + "recently_taken_page_title": "Recentemente Tomado", + "refresh": "Actualizar", + "refresh_encoded_videos": "Actualizar vídeos codificados", + "refresh_faces": "Actualizar caras", + "refresh_metadata": "Actualizar metadatos", + "refresh_thumbnails": "Actualizar miniaturas", + "refreshed": "Actualizado", + "refreshes_every_file": "Volve ler todos os ficheiros existentes e novos", + "refreshing_encoded_video": "Actualizando vídeo codificado", + "refreshing_faces": "Actualizando caras", + "refreshing_metadata": "Actualizando metadatos", + "regenerating_thumbnails": "Rexenerando miniaturas", + "remove": "Eliminar", + "remove_assets_album_confirmation": "EstÃĄs seguro de que queres eliminar {count, plural, one {# activo} other {# activos}} do ÃĄlbum?", + "remove_assets_shared_link_confirmation": "EstÃĄs seguro de que queres eliminar {count, plural, one {# activo} other {# activos}} desta ligazÃŗn compartida?", + "remove_assets_title": "Eliminar activos?", + "remove_custom_date_range": "Eliminar rango de datas personalizado", + "remove_deleted_assets": "Eliminar Activos Eliminados", + "remove_from_album": "Eliminar do ÃĄlbum", + "remove_from_favorites": "Eliminar de favoritos", + "remove_from_shared_link": "Eliminar da ligazÃŗn compartida", + "remove_memory": "Eliminar recordo", + "remove_photo_from_memory": "Eliminar foto deste recordo", + "remove_url": "Eliminar URL", + "remove_user": "Eliminar usuario", + "removed_api_key": "Chave API eliminada: {name}", + "removed_from_archive": "Eliminado do arquivo", + "removed_from_favorites": "Eliminado de favoritos", + "removed_from_favorites_count": "{count, plural, other {Eliminados #}} de favoritos", + "removed_memory": "Recordo eliminado", + "removed_photo_from_memory": "Foto eliminada do recordo", + "removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# activo} other {# activos}}", + "rename": "Renomear", + "repair": "Reparar", + "repair_no_results_message": "Os ficheiros non rastrexados e faltantes aparecerÃĄn aquí", + "replace_with_upload": "Substituír con carga", + "repository": "Repositorio", + "require_password": "Requirir contrasinal", + "require_user_to_change_password_on_first_login": "Requirir que o usuario cambie o contrasinal no primeiro inicio de sesiÃŗn", + "rescan": "Volver escanear", + "reset": "Restablecer", + "reset_password": "Restablecer contrasinal", + "reset_people_visibility": "Restablecer visibilidade das persoas", + "reset_to_default": "Restablecer ao predeterminado", + "resolve_duplicates": "Resolver duplicados", + "resolved_all_duplicates": "ResolvÊronse todos os duplicados", + "restore": "Restaurar", + "restore_all": "Restaurar todo", + "restore_user": "Restaurar usuario", + "restored_asset": "Activo restaurado", + "resume": "Reanudar", + "retry_upload": "Reintentar carga", + "review_duplicates": "Revisar duplicados", + "role": "Rol", + "role_viewer": "Visor", + "save": "Gardar", + "save_to_gallery": "Gardar na galería", + "saved_api_key": "Chave API gardada", + "saved_profile": "Perfil gardado", + "saved_settings": "ConfiguraciÃŗn gardada", + "say_something": "Dicir algo", + "scaffold_body_error_occurred": "Ocorreu un erro", + "scan_all_libraries": "Escanear Todas as Bibliotecas", + "scan_library": "Escanear", + "scan_settings": "ConfiguraciÃŗn de Escaneo", + "scanning_for_album": "Escaneando ÃĄlbum...", + "search": "Buscar", + "search_albums": "Buscar ÃĄlbums", + "search_by_context": "Buscar por contexto", + "search_by_description": "Buscar por descriciÃŗn", + "search_by_description_example": "Día de sendeirismo en Sapa", + "search_by_filename": "Buscar por nome de ficheiro ou extensiÃŗn", + "search_by_filename_example": "p. ex. IMG_1234.JPG ou PNG", + "search_camera_make": "Buscar marca de cÃĄmara...", + "search_camera_model": "Buscar modelo de cÃĄmara...", + "search_city": "Buscar cidade...", + "search_country": "Buscar país...", + "search_filter_apply": "Aplicar filtro", + "search_filter_camera_title": "Seleccionar tipo de cÃĄmara", + "search_filter_date": "Data", + "search_filter_date_interval": "{start} a {end}", + "search_filter_date_title": "Seleccionar un rango de datas", + "search_filter_display_option_not_in_album": "Non nun ÃĄlbum", + "search_filter_display_options": "OpciÃŗns de VisualizaciÃŗn", + "search_filter_filename": "Buscar por nome de ficheiro", + "search_filter_location": "UbicaciÃŗn", + "search_filter_location_title": "Seleccionar ubicaciÃŗn", + "search_filter_media_type": "Tipo de Medio", + "search_filter_media_type_title": "Seleccionar tipo de medio", + "search_filter_people_title": "Seleccionar persoas", + "search_for": "Buscar por", + "search_for_existing_person": "Buscar persoa existente", + "search_no_more_result": "Non hai mÃĄis resultados", + "search_no_people": "Sen persoas", + "search_no_people_named": "Sen persoas chamadas \"{name}\"", + "search_no_result": "Non se atoparon resultados, probe cun termo de busca ou combinaciÃŗn diferente", + "search_options": "OpciÃŗns de busca", + "search_page_categories": "Categorías", + "search_page_motion_photos": "Fotos en Movemento", + "search_page_no_objects": "Non hai InformaciÃŗn de Obxectos DispoÃąible", + "search_page_no_places": "Non hai InformaciÃŗn de Lugares DispoÃąible", + "search_page_screenshots": "Capturas de pantalla", + "search_page_search_photos_videos": "Busca as tÃēas fotos e vídeos", + "search_page_things": "Cousas", + "search_page_view_all_button": "Ver todo", + "search_page_your_activity": "A tÃēa actividade", + "search_page_your_map": "O teu Mapa", + "search_people": "Buscar persoas", + "search_places": "Buscar lugares", + "search_rating": "Buscar por clasificaciÃŗn...", + "search_result_page_new_search_hint": "Nova Busca", + "search_settings": "ConfiguraciÃŗn da busca", + "search_state": "Buscar estado...", + "search_suggestion_list_smart_search_hint_1": "A busca intelixente estÃĄ activada por defecto, para buscar metadatos use a sintaxe ", + "search_suggestion_list_smart_search_hint_2": "m:o-teu-termo-de-busca", + "search_tags": "Buscar etiquetas...", + "search_timezone": "Buscar fuso horario...", + "search_type": "Tipo de busca", + "search_your_photos": "Buscar as tÃēas fotos", + "searching_locales": "Buscando configuraciÃŗns rexionais...", + "second": "Segundo", + "see_all_people": "Ver todas as persoas", + "select": "Seleccionar", + "select_album_cover": "Seleccionar portada do ÃĄlbum", + "select_all": "Seleccionar todo", + "select_all_duplicates": "Seleccionar todos os duplicados", + "select_avatar_color": "Seleccionar cor do avatar", + "select_face": "Seleccionar cara", + "select_featured_photo": "Seleccionar foto destacada", + "select_from_computer": "Seleccionar do ordenador", + "select_keep_all": "Seleccionar conservar todo", + "select_library_owner": "Seleccionar propietario da biblioteca", + "select_new_face": "Seleccionar nova cara", + "select_photos": "Seleccionar fotos", + "select_trash_all": "Seleccionar mover todo ao lixo", + "select_user_for_sharing_page_err_album": "Erro ao crear o ÃĄlbum", + "selected": "Seleccionado", + "selected_count": "{count, plural, other {# seleccionados}}", + "send_message": "Enviar mensaxe", + "send_welcome_email": "Enviar correo electrÃŗnico de benvida", + "server_endpoint": "Punto Final do Servidor", + "server_info_box_app_version": "VersiÃŗn da AplicaciÃŗn", + "server_info_box_server_url": "URL do Servidor", + "server_offline": "Servidor FÃŗra de LiÃąa", + "server_online": "Servidor En LiÃąa", + "server_stats": "Estatísticas do Servidor", + "server_version": "VersiÃŗn do Servidor", + "set": "Establecer", + "set_as_album_cover": "Establecer como portada do ÃĄlbum", + "set_as_featured_photo": "Establecer como foto destacada", + "set_as_profile_picture": "Establecer como imaxe de perfil", + "set_date_of_birth": "Establecer data de nacemento", + "set_profile_picture": "Establecer imaxe de perfil", + "set_slideshow_to_fullscreen": "PoÃąer PresentaciÃŗn a pantalla completa", + "setting_image_viewer_help": "O visor de detalles carga primeiro a miniatura pequena, despois carga a vista previa de tamaÃąo medio (se estÃĄ activada), finalmente carga o orixinal (se estÃĄ activado).", + "setting_image_viewer_original_subtitle": "Activar para cargar a imaxe orixinal a resoluciÃŗn completa (grande!). Desactivar para reducir o uso de datos (tanto na rede como na cachÊ do dispositivo).", + "setting_image_viewer_original_title": "Cargar imaxe orixinal", + "setting_image_viewer_preview_subtitle": "Activar para cargar unha imaxe de resoluciÃŗn media. Desactivar para cargar directamente o orixinal ou usar sÃŗ a miniatura.", + "setting_image_viewer_preview_title": "Cargar imaxe de vista previa", + "setting_image_viewer_title": "Imaxes", + "setting_languages_apply": "Aplicar", + "setting_languages_subtitle": "Cambiar a lingua da aplicaciÃŗn", + "setting_languages_title": "Linguas", + "setting_notifications_notify_failures_grace_period": "Notificar fallos da copia de seguridade en segundo plano: {duration}", + "setting_notifications_notify_hours": "{count} horas", + "setting_notifications_notify_immediately": "inmediatamente", + "setting_notifications_notify_minutes": "{count} minutos", + "setting_notifications_notify_never": "nunca", + "setting_notifications_notify_seconds": "{count} segundos", + "setting_notifications_single_progress_subtitle": "InformaciÃŗn detallada do progreso da carga por activo", + "setting_notifications_single_progress_title": "Mostrar progreso detallado da copia de seguridade en segundo plano", + "setting_notifications_subtitle": "Axustar as tÃēas preferencias de notificaciÃŗn", + "setting_notifications_total_progress_subtitle": "Progreso xeral da carga (feitos/total activos)", + "setting_notifications_total_progress_title": "Mostrar progreso total da copia de seguridade en segundo plano", + "setting_video_viewer_looping_title": "Bucle", + "setting_video_viewer_original_video_subtitle": "Ao transmitir un vídeo desde o servidor, reproducir o orixinal aínda que haxa unha transcodificaciÃŗn dispoÃąible. Pode provocar buffering. Os vídeos dispoÃąibles localmente reprÃŗdÃēcense en calidade orixinal independentemente desta configuraciÃŗn.", + "setting_video_viewer_original_video_title": "Forzar vídeo orixinal", + "settings": "ConfiguraciÃŗn", + "settings_require_restart": "Por favor, reinicie Immich para aplicar esta configuraciÃŗn", + "settings_saved": "ConfiguraciÃŗn gardada", + "share": "Compartir", + "share_add_photos": "Engadir fotos", + "share_assets_selected": "{count} seleccionados", + "share_dialog_preparing": "Preparando...", + "shared": "Compartido", + "shared_album_activities_input_disable": "O comentario estÃĄ desactivado", + "shared_album_activity_remove_content": "Queres eliminar esta actividade?", + "shared_album_activity_remove_title": "Eliminar Actividade", + "shared_album_section_people_action_error": "Erro ao saír/eliminar do ÃĄlbum", + "shared_album_section_people_action_leave": "Eliminar usuario do ÃĄlbum", + "shared_album_section_people_action_remove_user": "Eliminar usuario do ÃĄlbum", + "shared_album_section_people_title": "PERSOAS", + "shared_by": "Compartido por", + "shared_by_user": "Compartido por {user}", + "shared_by_you": "Compartido por ti", + "shared_from_partner": "Fotos de {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} Subidos", + "shared_link_app_bar_title": "LigazÃŗns Compartidas", + "shared_link_clipboard_copied_massage": "Copiado ao portapapeis", + "shared_link_clipboard_text": "LigazÃŗn: {link}\nContrasinal: {password}", + "shared_link_create_error": "Erro ao crear ligazÃŗn compartida", + "shared_link_edit_description_hint": "Introduza a descriciÃŗn da comparticiÃŗn", + "shared_link_edit_expire_after_option_day": "1 día", + "shared_link_edit_expire_after_option_days": "{count} días", + "shared_link_edit_expire_after_option_hour": "1 hora", + "shared_link_edit_expire_after_option_hours": "{count} horas", + "shared_link_edit_expire_after_option_minute": "1 minuto", + "shared_link_edit_expire_after_option_minutes": "{count} minutos", + "shared_link_edit_expire_after_option_months": "{count} meses", + "shared_link_edit_expire_after_option_year": "{count} ano", + "shared_link_edit_password_hint": "Introduza o contrasinal da comparticiÃŗn", + "shared_link_edit_submit_button": "Actualizar ligazÃŗn", + "shared_link_error_server_url_fetch": "Non se pode obter a url do servidor", + "shared_link_expires_day": "Caduca en {count} día", + "shared_link_expires_days": "Caduca en {count} días", + "shared_link_expires_hour": "Caduca en {count} hora", + "shared_link_expires_hours": "Caduca en {count} horas", + "shared_link_expires_minute": "Caduca en {count} minuto", + "shared_link_expires_minutes": "Caduca en {count} minutos", + "shared_link_expires_never": "Caduca ∞", + "shared_link_expires_second": "Caduca en {count} segundo", + "shared_link_expires_seconds": "Caduca en {count} segundos", + "shared_link_individual_shared": "Compartido individualmente", + "shared_link_manage_links": "Xestionar ligazÃŗns Compartidas", + "shared_link_options": "OpciÃŗns da ligazÃŗn compartida", + "shared_links": "LigazÃŗns compartidas", + "shared_links_description": "Compartir fotos e vídeos cunha ligazÃŗn", + "shared_photos_and_videos_count": "{assetCount, plural, other {# fotos e vídeos compartidos.}}", + "shared_with_me": "Compartido comigo", + "shared_with_partner": "Compartido con {partner}", + "sharing": "Compartir", + "sharing_enter_password": "Por favor, introduza o contrasinal para ver esta pÃĄxina.", + "sharing_page_album": "Álbums compartidos", + "sharing_page_description": "Crea ÃĄlbums compartidos para compartir fotos e vídeos con persoas na tÃēa rede.", + "sharing_page_empty_list": "LISTA BALEIRA", + "sharing_sidebar_description": "Mostrar unha ligazÃŗn a Compartir na barra lateral", + "sharing_silver_appbar_create_shared_album": "Novo ÃĄlbum compartido", + "sharing_silver_appbar_share_partner": "Compartir con compaÃąeiro/a", + "shift_to_permanent_delete": "prema ⇧ para eliminar permanentemente o activo", + "show_album_options": "Mostrar opciÃŗns do ÃĄlbum", + "show_albums": "Mostrar ÃĄlbums", + "show_all_people": "Mostrar todas as persoas", + "show_and_hide_people": "Mostrar e ocultar persoas", + "show_file_location": "Mostrar ubicaciÃŗn do ficheiro", + "show_gallery": "Mostrar galería", + "show_hidden_people": "Mostrar persoas ocultas", + "show_in_timeline": "Mostrar na liÃąa de tempo", + "show_in_timeline_setting_description": "Mostrar fotos e vídeos deste usuario na tÃēa liÃąa de tempo", + "show_keyboard_shortcuts": "Mostrar atallos de teclado", + "show_metadata": "Mostrar metadatos", + "show_or_hide_info": "Mostrar ou ocultar informaciÃŗn", + "show_password": "Mostrar contrasinal", + "show_person_options": "Mostrar opciÃŗns da persoa", + "show_progress_bar": "Mostrar Barra de Progreso", + "show_search_options": "Mostrar opciÃŗns de busca", + "show_shared_links": "Mostrar ligazÃŗns compartidas", + "show_slideshow_transition": "Mostrar transiciÃŗn da presentaciÃŗn", + "show_supporter_badge": "Insignia de seguidor/a", + "show_supporter_badge_description": "Mostrar unha insignia de seguidor/a", + "shuffle": "Aleatorio", + "sidebar": "Barra lateral", + "sidebar_display_description": "Mostrar unha ligazÃŗn ÃĄ vista na barra lateral", + "sign_out": "Pechar SesiÃŗn", + "sign_up": "Rexistrarse", + "size": "TamaÃąo", + "skip_to_content": "Saltar ao contido", + "skip_to_folders": "Saltar a cartafoles", + "skip_to_tags": "Saltar a etiquetas", + "slideshow": "PresentaciÃŗn", + "slideshow_settings": "ConfiguraciÃŗn da presentaciÃŗn", + "sort_albums_by": "Ordenar ÃĄlbums por...", + "sort_created": "Data de creaciÃŗn", + "sort_items": "NÃēmero de elementos", + "sort_modified": "Data de modificaciÃŗn", + "sort_oldest": "Foto mÃĄis antiga", + "sort_people_by_similarity": "Ordenar persoas por similitude", + "sort_recent": "Foto mÃĄis recente", + "sort_title": "Título", + "source": "Fonte", + "stack": "Apilar", + "stack_duplicates": "Apilar duplicados", + "stack_select_one_photo": "Seleccionar unha foto principal para a pila", + "stack_selected_photos": "Apilar fotos seleccionadas", + "stacked_assets_count": "Apilados {count, plural, one {# activo} other {# activos}}", + "stacktrace": "Rastro da Pila", + "start": "Iniciar", + "start_date": "Data de inicio", + "state": "Estado", + "status": "Estado", + "stop_motion_photo": "Deter Foto en Movemento", + "stop_photo_sharing": "Deixar de compartir as tÃēas fotos?", + "stop_photo_sharing_description": "{partner} xa non poderÃĄ acceder ÃĄs tÃēas fotos.", + "stop_sharing_photos_with_user": "Deixar de compartir as tÃēas fotos con este usuario", + "storage": "Espazo de almacenamento", + "storage_label": "Etiqueta de almacenamento", + "storage_usage": "{used} de {available} usado", + "submit": "Enviar", + "suggestions": "SuxestiÃŗns", + "sunrise_on_the_beach": "Amencer na praia", + "support": "Soporte", + "support_and_feedback": "Soporte e Comentarios", + "support_third_party_description": "A tÃēa instalaciÃŗn de Immich foi empaquetada por un terceiro. Os problemas que experimente poden ser causados por ese paquete, así que por favor, comunica os problemas con eles en primeira instancia usando as ligazÃŗns a continuaciÃŗn.", + "swap_merge_direction": "Intercambiar direcciÃŗn de fusiÃŗn", + "sync": "Sincronizar", + "sync_albums": "Sincronizar ÃĄlbums", + "sync_albums_manual_subtitle": "Sincronizar todos os vídeos e fotos cargados aos ÃĄlbums de copia de seguridade seleccionados", + "sync_upload_album_setting_subtitle": "Crear e suba as tÃēas fotos e vídeos aos ÃĄlbums seleccionados en Immich", + "tag": "Etiqueta", + "tag_assets": "Etiquetar activos", + "tag_created": "Etiqueta creada: {tag}", + "tag_feature_description": "Navegar por fotos e vídeos agrupados por temas de etiquetas lÃŗxicas", + "tag_not_found_question": "Non atopa unha etiqueta? Crear unha nova etiqueta.", + "tag_people": "Etiquetar Persoas", + "tag_updated": "Etiqueta actualizada: {tag}", + "tagged_assets": "Etiquetados {count, plural, one {# activo} other {# activos}}", + "tags": "Etiquetas", + "template": "Modelo", + "theme": "Tema", + "theme_selection": "SelecciÃŗn de tema", + "theme_selection_description": "Establecer automaticamente o tema a claro ou escuro baseÃĄndose na preferencia do sistema do teu navegador", + "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de almacenamento nas tellas de activos", + "theme_setting_asset_list_tiles_per_row_title": "NÃēmero de activos por fila ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicar cor primaria ÃĄs superficies de fondo.", + "theme_setting_colorful_interface_title": "Interface colorida", + "theme_setting_image_viewer_quality_subtitle": "Axustar a calidade do visor de imaxes de detalle", + "theme_setting_image_viewer_quality_title": "Calidade do visor de imaxes", + "theme_setting_primary_color_subtitle": "Elixa unha cor para acciÃŗns primarias e acentos.", + "theme_setting_primary_color_title": "Cor primaria", + "theme_setting_system_primary_color_title": "Usar cor do sistema", + "theme_setting_system_theme_switch": "AutomÃĄtico (Seguir configuraciÃŗn do sistema)", + "theme_setting_theme_subtitle": "Elixir a configuraciÃŗn do tema da aplicaciÃŗn", + "theme_setting_three_stage_loading_subtitle": "A carga en tres etapas pode aumentar o rendemento da carga pero causa unha carga de rede significativamente maior", + "theme_setting_three_stage_loading_title": "Activar carga en tres etapas", + "they_will_be_merged_together": "Fusionaranse xuntos", + "third_party_resources": "Recursos de Terceiros", + "time_based_memories": "Recordos baseados no tempo", + "timeline": "LiÃąa de tempo", + "timezone": "Fuso horario", + "to_archive": "Arquivar", + "to_change_password": "Cambiar contrasinal", + "to_favorite": "Favorito", + "to_login": "Iniciar sesiÃŗn", + "to_parent": "Ir ao pai", + "to_trash": "Lixo", + "toggle_settings": "Alternar configuraciÃŗn", + "toggle_theme": "Alternar tema escuro", + "total_usage": "Uso total", + "trash": "Lixo", + "trash_all": "Mover Todo ao Lixo", + "trash_count": "Lixo {count, number}", + "trash_delete_asset": "Mover ao Lixo/Eliminar Activo", + "trash_emptied": "Lixo baleirado", + "trash_no_results_message": "As fotos e vídeos movidos ao lixo aparecerÃĄn aquí.", + "trash_page_delete_all": "Eliminar Todo", + "trash_page_empty_trash_dialog_content": "Queres baleirar os teus activos no lixo? Estes elementos eliminaranse permanentemente de Immich", + "trash_page_info": "Os elementos no lixo eliminaranse permanentemente despois de {days} días", + "trash_page_no_assets": "Non hai activos no lixo", + "trash_page_restore_all": "Restaurar Todo", + "trash_page_select_assets_btn": "Seleccionar activos", + "trash_page_title": "Lixo ({count})", + "trashed_items_will_be_permanently_deleted_after": "Os elementos no lixo eliminaranse permanentemente despois de {days, plural, one {# día} other {# días}}.", + "type": "Tipo", + "unarchive": "Desarquivar", + "unarchived_count": "{count, plural, other {Desarquivados #}}", + "unfavorite": "Desmarcar como favorito", + "unhide_person": "Mostrar persoa", + "unknown": "DescoÃąecido", + "unknown_country": "País DescoÃąecido", + "unknown_year": "Ano DescoÃąecido", + "unlimited": "Ilimitado", + "unlink_motion_video": "Desvincular vídeo en movemento", + "unlink_oauth": "Desvincular OAuth", + "unlinked_oauth_account": "Conta OAuth desvinculada", + "unmute_memories": "Desilenciar Recordos", + "unnamed_album": "Álbum Sen Nome", + "unnamed_album_delete_confirmation": "EstÃĄs seguro de que queres eliminar este ÃĄlbum?", + "unnamed_share": "Compartir Sen Nome", + "unsaved_change": "Cambio sen gardar", + "unselect_all": "Deseleccionar todo", + "unselect_all_duplicates": "Deseleccionar todos os duplicados", + "unstack": "Desapilar", + "unstacked_assets_count": "Desapilados {count, plural, one {# activo} other {# activos}}", + "untracked_files": "Ficheiros non rastrexados", + "untracked_files_decription": "Estes ficheiros non son rastrexados pola aplicaciÃŗn. Poden ser o resultado de movementos fallidos, cargas interrompidas ou deixados atrÃĄs debido a un erro", + "up_next": "A continuaciÃŗn", + "updated_password": "Contrasinal actualizado", + "upload": "Subir", + "upload_concurrency": "Concorrencia de subida", + "upload_dialog_info": "Queres facer copia de seguridade do(s) Activo(s) seleccionado(s) no servidor?", + "upload_dialog_title": "Subir Activo", + "upload_errors": "Subida completada con {count, plural, one {# erro} other {# erros}}, actualice a pÃĄxina para ver os novos activos subidos.", + "upload_progress": "Restantes {remaining, number} - Procesados {processed, number}/{total, number}", + "upload_skipped_duplicates": "Omitidos {count, plural, one {# activo duplicado} other {# activos duplicados}}", + "upload_status_duplicates": "Duplicados", + "upload_status_errors": "Erros", + "upload_status_uploaded": "Subido", + "upload_success": "Subida exitosa, actualice a pÃĄxina para ver os novos activos subidos.", + "upload_to_immich": "Subir a Immich ({count})", + "uploading": "Subindo", + "usage": "Uso", + "use_current_connection": "usar conexiÃŗn actual", + "use_custom_date_range": "Usar rango de datas personalizado no seu lugar", + "user": "Usuario", + "user_id": "ID de Usuario", + "user_liked": "A {user} gustoulle {type, select, photo {esta foto} video {este vídeo} asset {este activo} other {isto}}", + "user_purchase_settings": "Compra", + "user_purchase_settings_description": "Xestionar a tÃēa compra", + "user_role_set": "Establecer {user} como {role}", + "user_usage_detail": "Detalle de uso do usuario", + "user_usage_stats": "Estatísticas de uso da conta", + "user_usage_stats_description": "Ver estatísticas de uso da conta", + "username": "Nome de usuario", + "users": "Usuarios", + "utilities": "Utilidades", + "validate": "Validar", + "validate_endpoint_error": "Por favor, introduza unha URL vÃĄlida", + "version": "VersiÃŗn", + "version_announcement_closing": "O seu amigo, Alex", + "version_announcement_message": "Ola! Unha nova versiÃŗn de Immich estÃĄ dispoÃąible. Por favor, toma un tempo para ler as notas de lanzamento para asegurarse de que a tÃēa configuraciÃŗn estÃĄ actualizada para evitar calquera configuraciÃŗn incorrecta, especialmente se usas WatchTower ou calquera mecanismo que xestione a actualizaciÃŗn automÃĄtica da tÃēa instancia de Immich.", + "version_announcement_overlay_release_notes": "notas de lanzamento", + "version_announcement_overlay_text_1": "Ola amigo/a, hai unha nova versiÃŗn de", + "version_announcement_overlay_text_2": "por favor, toma o teu tempo para visitar as ", + "version_announcement_overlay_text_3": " e asegÃērate de que a tÃēa configuraciÃŗn de docker-compose e .env estÃĄ actualizada para evitar calquera configuraciÃŗn incorrecta, especialmente se usa WatchTower ou calquera mecanismo que xestione a actualizaciÃŗn automÃĄtica da tÃēa aplicaciÃŗn de servidor.", + "version_announcement_overlay_title": "Nova VersiÃŗn do Servidor DispoÃąible 🎉", + "version_history": "Historial de VersiÃŗns", + "version_history_item": "Instalado {version} o {date}", + "video": "Vídeo", + "video_hover_setting": "Reproducir miniatura do vídeo ao pasar o rato por riba", + "video_hover_setting_description": "Reproducir miniatura do vídeo cando o rato estÃĄ sobre o elemento. Mesmo cando estÃĄ desactivado, a reproduciÃŗn pode iniciarse pasando o rato sobre a icona de reproduciÃŗn.", + "videos": "Vídeos", + "videos_count": "{count, plural, one {# Vídeo} other {# Vídeos}}", + "view": "Ver", + "view_album": "Ver Álbum", + "view_all": "Ver Todo", + "view_all_users": "Ver todos os usuarios", + "view_in_timeline": "Ver na liÃąa de tempo", + "view_link": "Ver ligazÃŗn", + "view_links": "Ver ligazÃŗns", + "view_name": "Vista", + "view_next_asset": "Ver seguinte activo", + "view_previous_asset": "Ver activo anterior", + "view_qr_code": "Ver cÃŗdigo QR", + "view_stack": "Ver Pila", + "viewer_remove_from_stack": "Eliminar da Pila", + "viewer_stack_use_as_main_asset": "Usar como Activo Principal", + "viewer_unstack": "Desapilar", + "visibility_changed": "Visibilidade cambiada para {count, plural, one {# persoa} other {# persoas}}", + "waiting": "Agardando", + "warning": "Aviso", + "week": "Semana", + "welcome": "Benvido/a", + "welcome_to_immich": "Benvido/a a Immich", + "wifi_name": "Nome da wifi", "year": "Ano", + "years_ago": "Hai {years, plural, one {# ano} other {# anos}}", "yes": "Si", - "your_wifi_name": "Your WiFi name", - "zoom_image": "Acercar imaxe" + "you_dont_have_any_shared_links": "Non tes ningunha ligazÃŗn compartida", + "your_wifi_name": "O nome da tÃēa wifi", + "zoom_image": "Ampliar Imaxe" } diff --git a/i18n/he.json b/i18n/he.json index 6a743ad6f7..a6fc7fcbfd 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -33,7 +33,7 @@ "added_to_favorites_count": "{count, number} נוספו למו×ĸדפים", "admin": { "add_exclusion_pattern_description": "הוספ×Ē ×“×¤×•×Ą×™ החרגה. × ×Ēמכ×Ē ×”×Ēאמ×Ē ×“×¤×•×Ą×™× באמ×Ļ×ĸו×Ē *, ** ו-?. כדי לה×Ē×ĸלם מכל הקב×Ļים ב×Ēיקיה כלשהי בשם \"Raw\", יש להש×Ēמ׊ ב \"**/Raw/**\". כדי לה×Ē×ĸלם מכל הקב×Ļים המס×Ēיימים ב \"tif.\", יש להש×Ēמ׊ ב \"tif.*/**\". כדי לה×Ē×ĸלם מנ×Ēיב מוחלט, יש להש×Ēמ׊ ב \"**/× ×Ēיב/לה×Ē×ĸלמו×Ē\".", - "asset_offline_description": "נכס ספרייה חי×Ļוני×Ē ×–×” לא נמ×Ļא יו×Ēר בדיסק והו×ĸבר לאשפה. אם הקוב×Ĩ הו×ĸבר מ×Ēוך הספרייה, נא לבדוק א×Ē ×Ļיר הזמן שלך ×ĸבור הנכס המקביל החדש. כדי לשחזר נכס זה, נא לוודא ׊-Immich יכול לגש×Ē ××œ × ×Ēיב הקוב×Ĩ למטה ולסרוק מחדש א×Ē ×”×Ą×¤×¨×™×™×”.", + "asset_offline_description": "×Ēמונה מספרייה חי×Ļוני×Ē ×–×• לא נמ×Ļא×Ē ×™×•×Ēר בדיסק והו×ĸברה לאשפה. אם הקוב×Ĩ הו×ĸבר מ×Ēוך הספרייה, נא לבדוק א×Ē ×Ļיר הזמן שלך ×ĸבור ה×Ēמונה המקבילה החדש. כדי לשחזר ×Ēמונה זו, נא לוודא ׊-Immich יכול לגש×Ē ××œ × ×Ēיב הקוב×Ĩ למטה ולסרוק מחדש א×Ē ×”×Ą×¤×¨×™×™×”.", "authentication_settings": "הגדרו×Ē ×”×Ēחברו×Ē", "authentication_settings_description": "ניהול סיסמה, OAuth, והגדרו×Ē ×”×Ēחברו×Ē ××—×¨×•×Ē", "authentication_settings_disable_all": "האם בר×Ļונך להשבי×Ē ××Ē ×›×œ שיטו×Ē ×”×”×Ēחברו×Ē? כניסה למ×ĸרכ×Ē ×Ēהיה מושב×Ē×Ē ×œ×—×œ×•×˜×™×Ÿ.", @@ -49,24 +49,25 @@ "cleared_jobs": "נוקו משימו×Ē ×ĸבור: {job}", "config_set_by_file": "ה×Ē×Ļורה מוגדר×Ē ×›×ĸ×Ē ×ĸל ידי קוב×Ĩ ×Ē×Ļורה", "confirm_delete_library": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×Ą×¤×¨×™×™×” {library}?", - "confirm_delete_library_assets": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×Ą×¤×¨×™×™×” הזו? זה ימחק א×Ē {count, plural, one {נכס # המוכל} other {כל # הנכסים המוכלים}} בה מ-Immich ואינו ני×Ēן לביטול. קב×Ļים יישארו בדיסק.", + "confirm_delete_library_assets": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×Ą×¤×¨×™×™×” הזו? זה ימחק א×Ē {count, plural, one {×Ēמונה # המוכל×Ē} other {כל # ×Ēמונו×Ē ×”×ž×•×›×œ×™×}} בה מ-Immich ואינו ני×Ēן לביטול. קב×Ļים יישארו בדיסק.", "confirm_email_below": "כדי לאשר, יש להקליד \"{email}\" למטה", "confirm_reprocess_all_faces": "האם באמ×Ē ×‘×¨×Ļונך ל×ĸבד מחדש א×Ē ×›×œ הפנים? זה גם ינקה אנשים ב×ĸלי ׊ם.", "confirm_user_password_reset": "האם באמ×Ē ×‘×¨×Ļונך לאפס א×Ē ×”×Ą×™×Ą×ž×” של המש×Ēמ׊ {user}?", + "confirm_user_pin_code_reset": "האם א×Ēה בטוח שבר×Ļונך לאפס א×Ē ×§×•×“ ה PIN של {user}?", "create_job": "×Ļור ×ĸבודה", "cron_expression": "ביטוי cron", "cron_expression_description": "הגדר א×Ē ×ž×¨×•×•×— הסריקה באמ×Ļ×ĸו×Ē ×Ēבני×Ē ×”- cron. למיד×ĸ × ×•×Ą×Ŗ נא לפנו×Ē ×œ×ž×Š×œ אל Crontab Guru", "cron_expression_presets": "הגדרו×Ē ×§×‘×•×ĸו×Ē ×ž×¨××Š של ביטוי cron", "disable_login": "השב×Ē ×›× ×™×Ą×”", - "duplicate_detection_job_description": "הפ×ĸל למיד×Ē ×ž×›×•× ×” ×ĸל נכסים כדי לזהו×Ē ×Ēמונו×Ē ×“×•×ž×•×Ē. נ׊×ĸן ×ĸל חיפוש חכם", + "duplicate_detection_job_description": "הפ×ĸל למיד×Ē ×ž×›×•× ×” ×ĸל ×Ēמונו×Ē ×›×“×™ לזהו×Ē ×Ēמונו×Ē ×“×•×ž×•×Ē. נ׊×ĸן ×ĸל חיפוש חכם", "exclusion_pattern_description": "דפוסי החרגה מאפשרים לך לה×Ē×ĸלם מקב×Ļים ומ×Ēיקיו×Ē ×‘×ĸ×Ē ×Ą×¨×™×§×Ē ×”×Ą×¤×¨×™×™×” שלך. זה שימושי אם יש לך ×Ēיקיו×Ē ×”×ž×›×™×œ×•×Ē ×§×‘×Ļים שאינך רו×Ļה לייבא, כגון קוב×Ļי RAW.", "external_library_created_at": "ספרייה חי×Ļוני×Ē (נו×Ļרה ב-{date})", "external_library_management": "ניהול ספרייה חי×Ļוני×Ē", "face_detection": "אי×Ēור פנים", - "face_detection_description": "א×Ēר א×Ē ×”×¤× ×™× בנכסים באמ×Ļ×ĸו×Ē ×œ×ž×™×“×Ē ×ž×›×•× ×”. ×ĸבור סרטונים, רק ה×Ēמונה הממוז×ĸר×Ē × ×œ×§×—×Ē ×‘×—×Š×‘×•×Ÿ. \"ר×ĸנון\" מ×ĸבד (מחדש) א×Ē ×›×œ הנכסים. \"איפוס\" מנקה ×‘× ×•×Ą×Ŗ א×Ē ×›×œ × ×Ēוני הפנים הנוכחיים. \"חסרים\" ×ž×•×Ą×™×Ŗ ל×Ēור נכסים שלא ×ĸובדו ×ĸדיין. לאחר שאי×Ēור הפנים הושלם, פנים שאו×Ēרו י×ĸמדו ב×Ēור לזיהוי פנים המשייך או×Ēן לאנשים קיימים או חדשים.", + "face_detection_description": "א×Ēר א×Ē ×”×¤× ×™× ב×Ēמונו×Ē ×‘××ž×Ļ×ĸו×Ē ×œ×ž×™×“×Ē ×ž×›×•× ×”. ×ĸבור סרטונים, רק ה×Ēמונה הממוז×ĸר×Ē × ×œ×§×—×Ē ×‘×—×Š×‘×•×Ÿ. \"ר×ĸנון\" מ×ĸבד (מחדש) א×Ē ×›×œ ה×Ēמונו×Ē. \"איפוס\" מנקה ×‘× ×•×Ą×Ŗ א×Ē ×›×œ × ×Ēוני הפנים הנוכחיים. \"חסרים\" ×ž×•×Ą×™×Ŗ ל×Ēור ×Ēמונו×Ē ×Š×œ× ×ĸובדו ×ĸדיין. לאחר שאי×Ēור הפנים הושלם, פנים שאו×Ēרו י×ĸמדו ב×Ēור לזיהוי פנים המשייך או×Ēן לאנשים קיימים או חדשים.", "facial_recognition_job_description": "קב×Ĩ פנים שאו×Ēרו ל×Ēוך אנשים. שלב זה מור×Ĩ לאחר השלמ×Ē ××™×Ēור פנים. \"איפוס\" מקב×Ĩ (מחדש) א×Ē ×›×œ הפר×Ļופים. \"חסרים\" ×ž×•×Ą×™×Ŗ ל×Ēור פנים שלא הוק×Ļה להם אדם.", "failed_job_command": "הפקודה {command} נכשלה ×ĸבור המשימה: {job}", - "force_delete_user_warning": "אזהרה: פ×ĸולה זו ×Ēסיר מיד א×Ē ×”×ž×Š×Ēמ׊ וא×Ē ×›×œ הנכסים. לא ני×Ēן לבטל פ×ĸולה זו והקב×Ļים לא ני×Ēנים לשחזור.", + "force_delete_user_warning": "אזהרה: פ×ĸולה זו ×Ēסיר מיד א×Ē ×”×ž×Š×Ēמ׊ וא×Ē ×›×œ ה×Ēמונו×Ē. לא ני×Ēן לבטל פ×ĸולה זו והקב×Ļים לא ני×Ēנים לשחזור.", "forcing_refresh_library_files": "כפיי×Ē ×¨×ĸנון של כל קב×Ļי הספרייה", "image_format": "פורמט", "image_format_description": "WebP מפיק קב×Ļים קטנים יו×Ēר מ JPEG, אך הוא איטי יו×Ēר לקידוד.", @@ -79,7 +80,7 @@ "image_prefer_embedded_preview_setting_description": "הש×Ēמ׊ ב×Ē×Ļוגו×Ē ×ž×§×“×™×ž×•×Ē ×ž×•×˜×ž×ĸו×Ē ×‘×Ēמונו×Ē RAW כקלט ל×ĸיבוד ×Ēמונה וכאשר זמינו×Ē. זה יכול להפיק ×Ļב×ĸים מדויקים יו×Ēר ×ĸבור ×Ēמונו×Ē ×ž×Ą×•×™×ž×•×Ē, אבל האיכו×Ē ×Š×œ ה×Ē×Ļוגה המקדימה היא ×Ēלוי×Ē ×ž×Ļלמה ול×Ēמונה ×ĸשויים להיו×Ē ×™×•×Ēר פגמי דחיסה.", "image_prefer_wide_gamut": "ה×ĸדת סולם ×Ļב×ĸים רחב", "image_prefer_wide_gamut_setting_description": "הש×Ēמ׊ ב-Display P3 ל×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē. זה מ׊מר טוב יו×Ēר א×Ē ×”×—×™×•× ×™×•×Ē ×Š×œ ×Ēמונו×Ē ×ĸם מרחבי ×Ļב×ĸ רחבים, אבל ×Ēמונו×Ē ×ĸשויו×Ē ×œ×”×•×¤×™×ĸ אחר×Ē ×‘×ž×›×Š×™×¨×™× ישנים ×ĸם גרס×Ē ×“×¤×“×¤×Ÿ ישנה. ×Ēמונו×Ē sRGB נשמרו×Ē ×›-sRGB כדי למנו×ĸ שינויי ×Ļב×ĸ.", - "image_preview_description": "×Ēמונה בגודל בינוני ×ĸם מטא-× ×Ēונים שהוסרו, מ׊מ׊×Ē ×‘×ĸ×Ē ×Ļפייה בנכס בודד ו×ĸבור למיד×Ē ×ž×›×•× ×”", + "image_preview_description": "×Ēמונה בגודל בינוני ×ĸם מטא-× ×Ēונים שהוסרו, מ׊מ׊×Ē ×‘×ĸ×Ē ×Ļפייה ב×Ēמונה בודד×Ē ×•×ĸבור למיד×Ē ×ž×›×•× ×”", "image_preview_quality_description": "איכו×Ē ×Ē×Ļוגה מקדימה מ-1 ×ĸד 100. איכו×Ē ×’×‘×•×”×” יו×Ēר היא טובה יו×Ēר, אבל מיי×Ļר×Ē ×§×‘×Ļים גדולים יו×Ēר ויכולה להפחי×Ē ××Ē ×Ēגוב×Ēיו×Ē ×”×™×™×Š×•×. הגדר×Ē ×ĸרך נמוך ×ĸשויה להשפי×ĸ ×ĸל איכו×Ē ×Ēו×Ļאו×Ē ×Š×œ למיד×Ē ×ž×›×•× ×”.", "image_preview_title": "הגדרו×Ē ×Ē×Ļוגה מקדימה", "image_quality": "איכו×Ē", @@ -106,7 +107,7 @@ "library_scanning_enable_description": "אפ׊ר סריק×Ē ×Ą×¤×¨×™×™×” ×Ēקופ×Ēי×Ē", "library_settings": "ספרייה חי×Ļוני×Ē", "library_settings_description": "ניהול הגדרו×Ē ×Ą×¤×¨×™×™×” חי×Ļוני×Ē", - "library_tasks_description": "סרוק ספריו×Ē ×—×™×Ļוניו×Ē ×ĸבור נכסים חדשים ו/או שהש×Ēנו", + "library_tasks_description": "סרוק ספריו×Ē ×—×™×Ļוניו×Ē ×ĸבור ×Ēמונו×Ē ×—×“×Š×•×Ē ×•/או שהש×Ēנו", "library_watching_enable_description": "×ĸקוב אחר שינויי קב×Ļים בספריו×Ē ×—×™×Ļוניו×Ē", "library_watching_settings": "×Ļפיי×Ē ×Ą×¤×¨×™×™×” (ניסיוני)", "library_watching_settings_description": "×ĸקוב אוטומטי×Ē ××—×¨ שינויי קב×Ļים", @@ -117,7 +118,7 @@ "machine_learning_clip_model_description": "שמו של מודל CLIP רשום כאן. שים לב ׊×ĸליך להפ×ĸיל מחדש א×Ē ×”×ž×Š×™×ž×” 'חיפוש חכם' ×ĸבור כל ה×Ēמונו×Ē ×‘×ĸ×Ē ×Š×™× ×•×™ מודל.", "machine_learning_duplicate_detection": "אי×Ēור כפילויו×Ē", "machine_learning_duplicate_detection_enabled": "אפ׊ר אי×Ēור כפילויו×Ē", - "machine_learning_duplicate_detection_enabled_description": "אם מושב×Ē, נכסים זהים בדיוק ×ĸדיין י×ĸברו ביטול כפילויו×Ē.", + "machine_learning_duplicate_detection_enabled_description": "אם מושב×Ē, ×Ēמונו×Ē ×–×”×•×Ē ×‘×“×™×•×§ ×ĸדיין י×ĸברו ביטול כפילויו×Ē.", "machine_learning_duplicate_detection_setting_description": "הש×Ēמ׊ בהטמ×ĸו×Ē ×Š×œ CLIP כדי למ×Ļוא כפילויו×Ē ××¤×Š×¨×™×•×Ē", "machine_learning_enabled": "אפ׊ר למיד×Ē ×ž×›×•× ×”", "machine_learning_enabled_description": "אם מושב×Ē, כל ×Ēכונו×Ē ×œ×ž×™×“×Ē ×ž×›×•× ×” יהיו מושב×Ēו×Ē ×œ×œ× ק׊ר להגדרו×Ē ×Š×œ×”×œ×Ÿ.", @@ -160,16 +161,16 @@ "memory_cleanup_job": "ניקוי זיכרון", "memory_generate_job": "י×Ļיר×Ē ×–×™×›×¨×•×Ÿ", "metadata_extraction_job": "חל×Ĩ מטא-× ×Ēונים", - "metadata_extraction_job_description": "חל×Ĩ מיד×ĸ מטא-× ×Ēונים מכל נכס, כגון GPS, פנים ורזולו×Ļיה", + "metadata_extraction_job_description": "חל×Ĩ מטא-× ×Ēונים מכל ×Ēמונה, כגון GPS, פנים ורזולו×Ļיה", "metadata_faces_import_setting": "אפ׊ר יבוא פנים", "metadata_faces_import_setting_description": "יבא פנים מנ×Ēוני EXIF של ×Ēמונה ומקב×Ļים נלווים", "metadata_settings": "הגדרו×Ē ×ž×˜×-× ×Ēונים", "metadata_settings_description": "ניהול הגדרו×Ē ×ž×˜×-× ×Ēונים", "migration_job": "ה×ĸברה", - "migration_job_description": "ה×ĸבר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ נכסים ופנים למבנה ה×Ēיקיו×Ē ×”×ĸדכני ביו×Ēר", + "migration_job_description": "ה×ĸבר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ ×Ēמונו×Ē ×•×¤× ×™× למבנה ה×Ēיקיו×Ē ×”×ĸדכני ביו×Ēר", "no_paths_added": "לא נוספו × ×Ēיבים", "no_pattern_added": "לא נוספה ×Ēבני×Ē", - "note_apply_storage_label_previous_assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל נכסים שהו×ĸלו ב×ĸבר, הפ×ĸל א×Ē", + "note_apply_storage_label_previous_assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē", "note_cannot_be_changed_later": "ה×ĸרה: אי אפ׊ר לשנו×Ē ×–××Ē ×ž××•×—×¨ יו×Ēר!", "notification_email_from_address": "מכ×Ēוב×Ē", "notification_email_from_address_description": "כ×Ēוב×Ē ×“×•×\"ל של השולח, לדוגמה: \"Immich ׊ר×Ē ×Ēמונו×Ē \"", @@ -192,26 +193,22 @@ "oauth_auto_register": "רישום אוטומטי", "oauth_auto_register_description": "רשום אוטומטי×Ē ×ž×Š×Ēמשים חדשים לאחר כניסה ×ĸם OAuth", "oauth_button_text": "טקץט לח×Ļן", - "oauth_client_id": "מזהה לקוח", - "oauth_client_secret": "סוד לקוח", + "oauth_client_secret_description": "נדרש כאשר ץפק ה־OAuth אינו ×Ēומך ב־PKCE (מפ×Ēח הוכחה להחלפ×Ē ×§×•×“)", "oauth_enable_description": "ה×Ēחבר ×ĸם OAuth", - "oauth_issuer_url": "כ×Ēוב×Ē ××Ēר המנפיק", "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "×ĸקיפ×Ē URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override_description": "אפ׊ר כאשר ץפק OAuth לא מאפ׊ר כ×Ēוב×Ē URI לנייד, כמו '{callback}'", - "oauth_profile_signing_algorithm": "אלגורי×Ēם ח×Ēימ×Ē ×¤×¨×•×¤×™×œ", - "oauth_profile_signing_algorithm_description": "אלגורי×Ēם המשמש לח×Ēימה ×ĸל פרופיל המש×Ēמ׊.", - "oauth_scope": "רמ×Ē ×”×¨×Š××”", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרו×Ē ×”×Ēחברו×Ē ×ĸם OAuth", "oauth_settings_more_details": "למיד×ĸ × ×•×Ą×Ŗ אודו×Ē ×Ēכונה זו, בדוק א×Ē ×”×Ēי×ĸוד.", - "oauth_signing_algorithm": "אלגורי×Ēם ח×Ēימה", "oauth_storage_label_claim": "דריש×Ē ×Ēווי×Ē ××—×Ą×•×Ÿ", "oauth_storage_label_claim_description": "הגדר אוטומטי×Ē ××Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ של המש×Ēמ׊ ל×ĸרך של דרישה זו.", "oauth_storage_quota_claim": "דריש×Ē ×ž×›×Ą×Ē ××—×Ą×•×Ÿ", "oauth_storage_quota_claim_description": "הגדר אוטומטי×Ē ××Ē ×ž×›×Ą×Ē ×”××—×Ą×•×Ÿ של המש×Ēמ׊ ל×ĸרך של דרישה זו.", "oauth_storage_quota_default": "מכס×Ē ××—×Ą×•×Ÿ בריר×Ē ×ž×—×“×œ (GiB)", "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופק×Ē ×“×¨×™×Š×” (הזן 0 ×ĸבור מכסה בל×Ēי מוגבל×Ē).", + "oauth_timeout": "הבקשה נכשלה – הזמן הק×Ļוב הס×Ēיים", + "oauth_timeout_description": "זמן ×§×Ļוב לבקשו×Ē (במילישניו×Ē)", "offline_paths": "× ×Ēיבים לא מקוונים", "offline_paths_description": "×Ēו×Ļאו×Ē ××œ×• ×ĸשויו×Ē ×œ×”×™×•×Ē ×ĸקב מחיקה ידני×Ē ×Š×œ קב×Ļים שאינם חלק מספרייה חי×Ļוני×Ē.", "password_enable_description": "ה×Ēחבר ×ĸם דוא\"ל וסיסמה", @@ -243,21 +240,21 @@ "sidecar_job": "מטא-× ×Ēונים נלווים", "sidecar_job_description": "גלה או סנכרן מטא-× ×Ēונים נלווים ממ×ĸרכ×Ē ×”×§×‘×Ļים", "slideshow_duration_description": "מספר שניו×Ē ×œ×”×Ļג×Ē ×›×œ ×Ēמונה", - "smart_search_job_description": "הפ×ĸל למיד×Ē ×ž×›×•× ×” ×ĸל נכסים כדי ל×Ēמוך בחיפוש חכם", - "storage_template_date_time_description": "חו×Ēמ×Ē ×–×ž×Ÿ י×Ļיר×Ē ×”× ×›×Ą מ׊מ׊×Ē ×œ×ž×™×“×ĸ ×ĸל ה×Ēאריך והש×ĸה", + "smart_search_job_description": "הפ×ĸל למיד×Ē ×ž×›×•× ×” ×ĸל ×Ēמונו×Ē ×›×“×™ ל×Ēמוך בחיפוש חכם", + "storage_template_date_time_description": "חו×Ēמ×Ē ×–×ž×Ÿ י×Ļיר×Ē ×”×Ēמונה מ׊מ׊×Ē ×œ×ž×™×“×ĸ ×ĸל ה×Ēאריך והש×ĸה", "storage_template_date_time_sample": "זמן לדוגמא {date}", "storage_template_enable_description": "הפ×ĸל מנו×ĸ ×Ēבני×Ē ××—×Ą×•×Ÿ", "storage_template_hash_verification_enabled": "אימו×Ē ×’×™×‘×•×‘ מופ×ĸל", "storage_template_hash_verification_enabled_description": "מאפ׊ר אימו×Ē ×’×™×‘×•×‘, אין להשבי×Ē ×–××Ē ××œ× אם יש לך ודאו×Ē ×œ×’×‘×™ ההשלכו×Ē", "storage_template_migration": "ה×ĸבר×Ē ×Ēבני×Ē ××—×Ą×•×Ÿ", - "storage_template_migration_description": "החל א×Ē ×”{template} הנוכחי×Ē ×ĸל נכסים שהו×ĸלו ב×ĸבר", - "storage_template_migration_info": "×Ēבני×Ē ×”××—×Ą×•×Ÿ ×Ēמיר א×Ē ×›×œ ההרחבו×Ē ×œ××•×Ēיו×Ē ×§×˜× ×•×Ē. שינויים ב×Ēבני×Ē ×™×—×•×œ×• רק ×ĸל נכסים חדשים. כדי להחיל באופן רטרואקטיבי א×Ē ×”×Ēבני×Ē ×ĸל נכסים שהו×ĸלו ב×ĸבר, הפ×ĸל א×Ē {job}.", + "storage_template_migration_description": "החל א×Ē ×”{template} הנוכחי×Ē ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר", + "storage_template_migration_info": "×Ēבני×Ē ×”××—×Ą×•×Ÿ ×Ēמיר א×Ē ×›×œ ההרחבו×Ē ×œ××•×Ēיו×Ē ×§×˜× ×•×Ē. שינויים ב×Ēבני×Ē ×™×—×•×œ×• רק ×ĸל ×Ēמונו×Ē ×—×“×Š×•×Ē. כדי להחיל באופן רטרואקטיבי א×Ē ×”×Ēבני×Ē ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē {job}.", "storage_template_migration_job": "משימ×Ē ×”×ĸבר×Ē ×Ēבני×Ē ××—×Ą×•×Ÿ", "storage_template_more_details": "לפרטים נוספים אודו×Ē ×Ēכונה זו, ×ĸיין ב×Ēבני×Ē ×”××—×Ą×•×Ÿ ובהשלכו×Ēיה", "storage_template_onboarding_description": "כאשר מופ×ĸל×Ē, ×Ēכונה זו ×Ēארגן אוטומטי×Ē ×§×‘×Ļים בה×Ēבסס ×ĸל ×Ēבני×Ē ×Š×”×ž×Š×Ēמ׊ הגדיר. ×ĸקב ב×ĸיו×Ē ×™×Ļיבו×Ē ×”×Ēכונה כבויה כבריר×Ē ×ž×—×“×œ. למיד×ĸ × ×•×Ą×Ŗ, נא לראו×Ē ××Ē ×”×Ēי×ĸוד.", "storage_template_path_length": "מגבל×Ē ××•×¨×š × ×Ēיב משו×ĸר×Ē: {length, number}/{limit, number}", "storage_template_settings": "×Ēבני×Ē ××—×Ą×•×Ÿ", - "storage_template_settings_description": "ניהול מבנה ה×Ēיקיו×Ē ×•××Ē ×Š× הקוב×Ĩ של נכס הה×ĸלאה", + "storage_template_settings_description": "ניהול מבנה ה×Ēיקיו×Ē ×•××Ē ×Š× הקוב×Ĩ של ה×Ēמונה שהו×ĸל×Ēה", "storage_template_user_label": "{label} היא ×Ēווי×Ē ×”××—×Ą×•×Ÿ של המש×Ēמ׊", "system_settings": "הגדרו×Ē ×ž×ĸרכ×Ē", "tag_cleanup_job": "ניקוי ×Ēגים", @@ -277,7 +274,7 @@ "theme_settings_description": "ניהול ה×Ēאמה אישי×Ē ×Š×œ ממ׊ק האינטרנט של Immich", "these_files_matched_by_checksum": "קב×Ļים אלה ×Ēואמים לפי סיכומי הביקור×Ē ×Š×œ×”×", "thumbnail_generation_job": "×Ļור ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", - "thumbnail_generation_job_description": "יו×Ļר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×’×“×•×œ×•×Ē, קטנו×Ē ×•×ž×˜×•×Š×˜×Š×•×Ē ×ĸבור כל נכס, כמו גם ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ĸבור כל אדם", + "thumbnail_generation_job_description": "יו×Ļר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×’×“×•×œ×•×Ē, קטנו×Ē ×•×ž×˜×•×Š×˜×Š×•×Ē ×ĸבור כל ×Ēמונה, כמו גם ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ĸבור כל אדם", "transcoding_acceleration_api": "API הא×Ļה", "transcoding_acceleration_api_description": "ה-API שיי×Ļור אינטראק×Ļיה ×ĸם המכשיר שלך כדי להאי×Ĩ א×Ē ×”×ž×¨×Ē ×”×§×™×“×•×“. הגדרה זו היא 'המאמ×Ĩ הטוב ביו×Ēר': היא ×Ēחזור לקידוד ×Ēוכנה במקרה של כשל. VP9 ×ĸשוי ל×ĸבוד או לא, ×Ēלוי בחומרה שלך.", "transcoding_acceleration_nvenc": "NVENC (דורש כרטיס מסך של NVIDIA)", @@ -341,17 +338,17 @@ "transcoding_video_codec_description": "ל-VP9 יש י×ĸילו×Ē ×’×‘×•×”×” ו×Ēאימו×Ē ×¨×Š×Ē, אבל לוקח יו×Ēר זמן להמיר א×Ē ×”×§×™×“×•×“ ×ĸבורו. HEVC מ×Ēפקד באופן דומה, אך ב×ĸל ×Ēאימו×Ē ×¨×Š×Ē × ×ž×•×›×” יו×Ēר. H.264 ×Ēואם באופן נרחב ומהיר להמיר א×Ē ×§×™×“×•×“×•, אבל הוא מיי×Ļר קב×Ļים גדולים בהרבה. AV1 הוא הקידוד הי×ĸיל ביו×Ēר אך לוקה ב×Ēמיכה במכשירים ישנים יו×Ēר.", "trash_enabled_description": "הפ×ĸל א×Ē ×Ēכונו×Ē ×”××Š×¤×”", "trash_number_of_days": "מספר הימים", - "trash_number_of_days_description": "מספר הימים לשמירה ×ĸל הנכסים באשפה לפני הסר×Ēם ל×Ļמי×Ēו×Ē", + "trash_number_of_days_description": "מספר הימים לשמירה של ×Ēמונו×Ē ×‘××Š×¤×” לפני הסר×Ēם ל×Ļמי×Ēו×Ē", "trash_settings": "הגדרו×Ē ×”××Š×¤×”", "trash_settings_description": "ניהול הגדרו×Ē ×”××Š×¤×”", "untracked_files": "קב×Ļים ללא מ×ĸקב", "untracked_files_description": "קב×Ļים אלה אינם נמ×Ļאים במ×ĸקב של היישום. הם יכולים להיו×Ē ×Ēו×Ļאו×Ē ×Š×œ ה×ĸברו×Ē ×›×•×Š×œ×•×Ē, ה×ĸלאו×Ē ×Š× ×§×˜×ĸו, או שנו×Ēרו מאחור בגלל שיבוש ב×Ēוכנה", "user_cleanup_job": "ניקוי מ׊×Ēמשים", - "user_delete_delay": "החשבון והנכסים של {user} י×Ēוזמנו למחיקה ל×Ļמי×Ēו×Ē ×‘×ĸוד {delay, plural, one {יום #} other {# ימים}}.", + "user_delete_delay": "החשבון וה×Ēמונו×Ē ×Š×œ {user} י×Ēוזמנו למחיקה ל×Ļמי×Ēו×Ē ×‘×ĸוד {delay, plural, one {יום #} other {# ימים}}.", "user_delete_delay_settings": "×ĸיכוב מחיקה", - "user_delete_delay_settings_description": "מספר הימים לאחר ההסרה ×ĸד מחיקה ל×Ļמי×Ēו×Ē ×Š×œ החשבון והנכסים של המש×Ēמ׊. משימ×Ē ×ž×—×™×§×Ē ×”×ž×Š×Ēמ׊ פו×ĸל×Ē ×‘×—×Ļו×Ē ×›×“×™ לבדוק אם יש מ׊×Ēמשים שמוכנים למחיקה. שינויים בהגדרה זו יו×ĸרכו בבי×Ļו×ĸ הבא.", - "user_delete_immediately": "החשבון והנכסים של {user} י×ĸמדו ב×Ēור למחיקה ל×Ļמי×Ēו×Ē ×‘××•×¤×Ÿ מיידי.", - "user_delete_immediately_checkbox": "ה×ĸמד מ׊×Ēמ׊ ונכסים ב×Ēור למחיקה מיידי×Ē", + "user_delete_delay_settings_description": "מספר הימים לאחר ההסרה ×ĸד מחיקה ל×Ļמי×Ēו×Ē ×Š×œ החשבון וה×Ēמונו×Ē ×Š×œ המש×Ēמ׊. משימ×Ē ×ž×—×™×§×Ē ×”×ž×Š×Ēמ׊ פו×ĸל×Ē ×‘×—×Ļו×Ē ×›×“×™ לבדוק אם יש מ׊×Ēמשים שמוכנים למחיקה. שינויים בהגדרה זו יו×ĸרכו בבי×Ļו×ĸ הבא.", + "user_delete_immediately": "החשבון וה×Ēמונו×Ē ×Š×œ {user} י×ĸמדו ב×Ēור למחיקה ל×Ļמי×Ēו×Ē ×‘××•×¤×Ÿ מיידי.", + "user_delete_immediately_checkbox": "ה×Ļב מ׊×Ēמ׊ ו×Ēמונו×Ē ×‘×Ēור למחיקה מיידי×Ē", "user_management": "ניהול מ׊×Ēמשים", "user_password_has_been_reset": "סיסמ×Ē ×”×ž×Š×Ēמ׊ אופסה:", "user_password_reset_description": "אנא ץפק א×Ē ×”×Ą×™×Ą×ž×” הזמני×Ē ×œ×ž×Š×Ēמ׊ והוד×ĸ לו שיש ×Ļורך לשנו×Ē ××Ē ×”×Ą×™×Ą×ž×” בכניסה הבאה שלו.", @@ -371,13 +368,17 @@ "admin_password": "סיסמ×Ē ×ž× ×”×œ", "administration": "ניהול", "advanced": "מ×Ēקדם", - "advanced_settings_log_level_title": "רמ×Ē ×Ēי×ĸוד אירו×ĸים: {}", - "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לט×ĸון ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ž× ×›×Ą×™× שבמכשיר. הפ×ĸל הגדרה זו כדי לט×ĸון ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē ×‘×ž×§×•×", + "advanced_settings_enable_alternate_media_filter_subtitle": "הש×Ēמ׊ באפשרו×Ē ×–×• כדי לסנן מדיה במהלך הסנכרון לפי קריטריונים חלופיים. מומל×Ĩ להש×Ēמ׊ בזה רק אם יש ב×ĸיה בזיהוי כל האלבומים באפליק×Ļיה.", + "advanced_settings_enable_alternate_media_filter_title": "[ניסיוני] הש×Ēמ׊ במסנן סנכרון אלבום חלופי שמבכשיר", + "advanced_settings_log_level_title": "רמ×Ē ×¨×™×Š×•× ביומן: {level}", + "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לט×ĸינה של ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ž×Ēמונו×Ē ×Š×‘×ž×›×Š×™×¨. הפ×ĸל הגדרה זו כדי לט×ĸון ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē ×‘×ž×§×•×.", "advanced_settings_prefer_remote_title": "ה×ĸדת ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē", - "advanced_settings_proxy_headers_subtitle": "הגדר כו×Ēרו×Ē ×¤×¨×•×§×Ą×™ שהיישום ×Ļריך לשלוח ×ĸם כל בקש×Ē ×¨×Š×Ē", + "advanced_settings_proxy_headers_subtitle": "הגדר proxy headers שהיישום ×Ļריך לשלוח ×ĸם כל בקש×Ē ×¨×Š×Ē", "advanced_settings_proxy_headers_title": "כו×Ēרו×Ē ×¤×¨×•×§×Ą×™", - "advanced_settings_self_signed_ssl_subtitle": "מדלג ×ĸל אימו×Ē ×Ē×ĸוד×Ē SSL ×ĸבור נקוד×Ē ×”×§×Ļה של השר×Ē. דרוש ×ĸבור ×Ē×ĸודו×Ē ×‘×—×Ēימה ×ĸ×Ļמי×Ē", + "advanced_settings_self_signed_ssl_subtitle": "מדלג ×ĸל אימו×Ē ×Ē×ĸוד×Ē SSL ×ĸבור נקוד×Ē ×”×§×Ļה של השר×Ē. דרוש ×ĸבור ×Ē×ĸודו×Ē ×‘×—×Ēימה ×ĸ×Ļמי×Ē.", "advanced_settings_self_signed_ssl_title": "ה×Ēר ×Ē×ĸודו×Ē SSL בח×Ēימה ×ĸ×Ļמי×Ē", + "advanced_settings_sync_remote_deletions_subtitle": "מחק או שחזר ×Ēמונה במכשיר זה באופן אוטומטי כאשר פ×ĸולה זו × ×ĸשי×Ē ×‘×“×¤×“×¤×Ÿ", + "advanced_settings_sync_remote_deletions_title": "סנכרן מחיקו×Ē ×Š×‘×•×Ļ×ĸו במכשירים אחרים [נסיוני]", "advanced_settings_tile_subtitle": "הגדרו×Ē ×ž×Š×Ēמ׊ מ×Ēקדם", "advanced_settings_troubleshooting_subtitle": "אפ׊ר ×Ēכונו×Ē × ×•×Ą×¤×•×Ē ×œ×¤×Ēרון ב×ĸיו×Ē", "advanced_settings_troubleshooting_title": "פ×Ēרון ב×ĸיו×Ē", @@ -400,17 +401,17 @@ "album_remove_user_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר א×Ē {user}?", "album_share_no_users": "נראה ששי×Ēפ×Ē ××Ē ×”××œ×‘×•× הזה ×ĸם כל המש×Ēמשים או שאין לך את מ׊×Ēמ׊ לש×Ē×Ŗ אי×Ēו.", "album_thumbnail_card_item": "פריט 1", - "album_thumbnail_card_items": "{} פריטים", + "album_thumbnail_card_items": "{count} פריטים", "album_thumbnail_card_shared": " ¡ משו×Ē×Ŗ", - "album_thumbnail_shared_by": "משו×Ē×Ŗ ×ĸל ידי {}", + "album_thumbnail_shared_by": "שו×Ē×Ŗ ×ĸל ידי {user}", "album_updated": "אלבום ×ĸודכן", - "album_updated_setting_description": "קבל הוד×ĸ×Ē ×“×•×\"ל כאשר לאלבום משו×Ē×Ŗ יש נכסים חדשים", + "album_updated_setting_description": "קבל הוד×ĸ×Ē ×“×•×\"ל כאשר לאלבום משו×Ē×Ŗ יש ×Ēמונו×Ē ×—×“×Š×•×Ē", "album_user_left": "×ĸזב א×Ē {album}", "album_user_removed": "{user} הוסר", "album_viewer_appbar_delete_confirm": "האם א×Ē/ה בטוח/ה שבר×Ļונך למחוק א×Ē ×”××œ×‘×•× הזה מהחשבון שלך?", "album_viewer_appbar_share_err_delete": "מחיק×Ē ××œ×‘×•× נכשלה", "album_viewer_appbar_share_err_leave": "×ĸזיב×Ē ×”××œ×‘×•× נכשלה", - "album_viewer_appbar_share_err_remove": "יש ב×ĸיו×Ē ×‘×”×Ą×¨×Ē ×”× ×›×Ą×™× מהאלבום", + "album_viewer_appbar_share_err_remove": "יש ב×ĸיו×Ē ×‘×”×Ą×¨×Ē ×”×Ēמונו×Ē ×ž×”××œ×‘×•×", "album_viewer_appbar_share_err_title": "נכשל בשינוי כו×Ēר×Ē ×”××œ×‘×•×", "album_viewer_appbar_share_leave": "×ĸזוב אלבום", "album_viewer_appbar_share_to": "׊×Ē×Ŗ ×ĸם", @@ -439,57 +440,57 @@ "appears_in": "מופי×ĸ ב", "archive": "ארכיון", "archive_or_unarchive_photo": "ה×ĸבר ×Ēמונה לארכיון או הו×Ļא או×Ēה מ׊ם", - "archive_page_no_archived_assets": "לא נמ×Ļאו נכסים בארכיון", - "archive_page_title": "ארכיון ({})", + "archive_page_no_archived_assets": "לא נמ×Ļאו ×Ēמונו×Ē ×‘××¨×›×™×•×Ÿ", + "archive_page_title": "בארכיון ({count})", "archive_size": "גודל הארכיון", "archive_size_description": "הגדר א×Ē ×’×•×“×œ הארכיון להורדו×Ē (ב-GiB)", "archived": "בארכיון", "archived_count": "{count, plural, other {# הו×ĸברו לארכיון}}", "are_these_the_same_person": "האם אלה או×Ēו האדם?", "are_you_sure_to_do_this": "האם באמ×Ē ×‘×¨×Ļונך ל×ĸשו×Ē ××Ē ×–×”?", - "asset_action_delete_err_read_only": "לא ני×Ēן למחוק נכס(ים) לקריאה בלבד, מדלג", - "asset_action_share_err_offline": "לא ני×Ēן להשיג נכס(ים) לא מקוונ(ים), מדלג ", + "asset_action_delete_err_read_only": "לא ני×Ēן למחוק ×Ēמונו×Ē ×œ×§×¨×™××” בלבד, מדלג", + "asset_action_share_err_offline": "לא ני×Ēן להשיג ×Ēמונו×Ē ×œ× מקוונו×Ē, מדלג", "asset_added_to_album": "× ×•×Ą×Ŗ לאלבום", "asset_adding_to_album": "×ž×•×Ą×™×Ŗ לאלבוםâ€Ļ", - "asset_description_updated": "×Ēיאור הנכס ×ĸודכן", - "asset_filename_is_offline": "הנכס {filename} אינו מקוון", - "asset_has_unassigned_faces": "לנכס יש פנים שלא הוק×Ļו", + "asset_description_updated": "×Ēיאור ה×Ēמונה ×ĸודכן", + "asset_filename_is_offline": "ה×Ēמונה {filename} אינה מקוונ×Ē", + "asset_has_unassigned_faces": "ל×Ēמונה יש פנים שלא הוק×Ļו", "asset_hashing": "מגבבâ€Ļ", "asset_list_group_by_sub_title": "קב×Ĩ לפי", "asset_list_layout_settings_dynamic_layout_title": "פריסה דינמי×Ē", "asset_list_layout_settings_group_automatically": "אוטומטי", - "asset_list_layout_settings_group_by": "קב×Ĩ נכסים לפי", + "asset_list_layout_settings_group_by": "קב×Ĩ ×Ēמונו×Ē ×œ×¤×™", "asset_list_layout_settings_group_by_month_day": "חודש + יום", "asset_list_layout_sub_title": "פריסה", "asset_list_settings_subtitle": "הגדרו×Ē ×Ēבני×Ē ×¨×Š×Ē ×Ēמונו×Ē", "asset_list_settings_title": "ר׊×Ē ×Ēמונו×Ē", - "asset_offline": "נכס לא מקוון", - "asset_offline_description": "הנכס החי×Ļוני הזה כבר לא נמ×Ļא בדיסק. נא לי×Ļור ק׊ר ×ĸם מנהל Immich שלך לקבל×Ē ×ĸזרה.", - "asset_restored_successfully": "נכס שוחזר בה×Ļלחה", + "asset_offline": "×Ēמונה לא מקוונ×Ē", + "asset_offline_description": "ה×Ēמונה החי×Ļוני×Ē ×”×–××Ē ×›×‘×¨ לא נמ×Ļא×Ē ×‘×“×™×Ą×§. נא לי×Ļור ק׊ר ×ĸם מנהל Immich שלך לקבל×Ē ×ĸזרה.", + "asset_restored_successfully": "×Ēמונה שוחזרה בה×Ļלחה", "asset_skipped": "דילג", "asset_skipped_in_trash": "באשפה", "asset_uploaded": "הו×ĸלה", "asset_uploading": "מ×ĸלהâ€Ļ", "asset_viewer_settings_subtitle": "ניהול הגדרו×Ē ×ž×Ļיג הגלריה שלך", - "asset_viewer_settings_title": "מ×Ļיג הנכסים", - "assets": "נכסים", - "assets_added_count": "{count, plural, one {× ×•×Ą×Ŗ נכס #} other {נוספו # נכסים}}", - "assets_added_to_album_count": "{count, plural, one {× ×•×Ą×Ŗ נכס #} other {נוספו # נכסים}} לאלבום", - "assets_added_to_name_count": "{count, plural, one {נכס # × ×•×Ą×Ŗ} other {# נכסים נוספו}} אל {hasName, select, true {{name}} other {אלבום חדש}}", - "assets_count": "{count, plural, one {נכס #} other {# נכסים}}", - "assets_deleted_permanently": "{} נכס(ים) נמחקו ל×Ļמי×Ēו×Ē", - "assets_deleted_permanently_from_server": "{} נכס(ים) נמחקו ל×Ļמי×Ēו×Ē ×ž×Š×¨×Ē ×”-Immich", - "assets_moved_to_trash_count": "{count, plural, one {נכס # הו×ĸבר} other {# נכסים הו×ĸברו}} לאשפה", - "assets_permanently_deleted_count": "{count, plural, one {נכס # נמחק} other {# נכסים נמחקו}} ל×Ļמי×Ēו×Ē", - "assets_removed_count": "{count, plural, one {נכס # הוסר} other {# נכסים הוסרו}}", - "assets_removed_permanently_from_device": "{} נכס(ים) נמחקו ל×Ļמי×Ēו×Ē ×ž×”×ž×›×Š×™×¨ שלך", - "assets_restore_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לשחזר א×Ē ×›×œ הנכסים שבאשפה? אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו! יש לשים לב שלא ני×Ēן לשחזר נכסים לא מקוונים בדרך זו.", - "assets_restored_count": "{count, plural, one {נכס # שוחזר} other {# נכסים שוחזרו}}", - "assets_restored_successfully": "{} נכס(ים) שוחזרו בה×Ļלחה", - "assets_trashed": "{} נכס(ים) הו×ĸברו לאשפה", - "assets_trashed_count": "{count, plural, one {נכס # הושלך} other {# נכסים הושלכו}} לאשפה", - "assets_trashed_from_server": "{} נכס(ים) הו×ĸברו לאשפה מ׊ר×Ē ×”-Immich", - "assets_were_part_of_album_count": "{count, plural, one {נכס היה} other {נכסים היו}} כבר חלק מהאלבום", + "asset_viewer_settings_title": "מ×Ļיג ה×Ēמונו×Ē", + "assets": "×Ēמונו×Ē", + "assets_added_count": "{count, plural, one {נוספה ×Ēומנה #} other {נוספו # ×Ēמונו×Ē}}", + "assets_added_to_album_count": "{count, plural, one {נוספה ×Ēמונה #} other {נוספו # ×Ēמונו×Ē}} לאלבום", + "assets_added_to_name_count": "{count, plural, one {×Ēמונה # נוספה} other {# ×Ēמונו×Ē × ×•×Ą×¤×•}} אל {hasName, select, true {{name}} other {אלבום חדש}}", + "assets_count": "{count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", + "assets_deleted_permanently": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē", + "assets_deleted_permanently_from_server": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē ×ž×Š×¨×Ē ×”-Immich", + "assets_moved_to_trash_count": "{count, plural, one {×Ēמונה # הו×ĸברה} other {# ×Ēמונו×Ē ×”×•×ĸברו}} לאשפה", + "assets_permanently_deleted_count": "{count, plural, one {×Ēמונה # נמחקה} other {# ×Ēמונו×Ē × ×ž×—×§×•}} ל×Ļמי×Ēו×Ē", + "assets_removed_count": "{count, plural, one {×Ēמונה # הוסרה} other {# ×Ēמונו×Ē ×”×•×Ą×¨×•}}", + "assets_removed_permanently_from_device": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē ×ž×”×ž×›×Š×™×¨ שלך", + "assets_restore_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לשחזר א×Ē ×›×œ ה×Ēמונו×Ē ×Š×‘××Š×¤×”? אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו! יש לשים לב שלא ני×Ēן לשחזר ×Ēמונו×Ē ×œ× מקוונו×Ē ×‘×“×¨×š זו.", + "assets_restored_count": "{count, plural, one {×Ēמונה # שוחזרה} other {# ×Ēמונו×Ē ×Š×•×—×–×¨×•}}", + "assets_restored_successfully": "{count} ×Ēמונו×Ē ×Š×•×—×–×¨×• בה×Ļלחה", + "assets_trashed": "{count} ×Ēמונו×Ē ×”×•×ĸברו לאשפה", + "assets_trashed_count": "{count, plural, one {×Ēמונה # הושלכה} other {# ×Ēמונו×Ē ×”×•×Š×œ×›×•}} לאשפה", + "assets_trashed_from_server": "{count} ×Ēמונו×Ē ×”×•×ĸברו לאשפה מהשר×Ē", + "assets_were_part_of_album_count": "{count, plural, one {×Ēמונה היי×Ēה} other {×Ēמונו×Ē ×”×™×•}} כבר חלק מהאלבום", "authorized_devices": "מכשירים מורשים", "automatic_endpoint_switching_subtitle": "ה×Ēחבר מקומי×Ē ×“×¨×š אינטרנט אלחוטי יי×ĸודי כאשר זמין והש×Ēמ׊ בחיבורים חלופיים במקומו×Ē ××—×¨×™×", "automatic_endpoint_switching_title": "החלפ×Ē ×›×Ēוב×Ē ××•×˜×•×ž×˜×™×Ē", @@ -497,22 +498,22 @@ "back_close_deselect": "חזור, סגור, או בטל בחירה", "background_location_permission": "הרשא×Ē ×ž×™×§×•× ברק×ĸ", "background_location_permission_content": "כדי ×œ×”×—×œ×™×Ŗ ר׊×Ēו×Ē ×‘×ĸ×Ē ×¨×™×Ļה ברק×ĸ, היישום ×Ļריך *×Ēמיד* גישה למיקום מדויק ×ĸל מנ×Ē ×œ×§×¨×•× א×Ē ×”×Š× של ר׊×Ē ×”××™× ×˜×¨× ×˜ האלחוטי", - "backup_album_selection_page_albums_device": "אלבומים במכשיר ({})", + "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פ×ĸמיים כדי להחריג", - "backup_album_selection_page_assets_scatter": "נכסים יכולים לה×Ēפזר ×ĸל פני אלבומים מרובים. לפיכך, ני×Ēן לכלול או להחריג אלבומים במהלך ×Ēהליך הגיבוי", + "backup_album_selection_page_assets_scatter": "×Ēמונו×Ē ×™×›×•×œ×•×Ē ×œ×”×Ēפזר ×ĸל פני אלבומים מרובים. לפיכך, ני×Ēן לכלול או להחריג אלבומים במהלך ×Ēהליך הגיבוי.", "backup_album_selection_page_select_albums": "בחיר×Ē ××œ×‘×•×ž×™×", "backup_album_selection_page_selection_info": "פרטי בחירה", - "backup_album_selection_page_total_assets": "סה״כ נכסים ייחודיים", + "backup_album_selection_page_total_assets": "סה״כ ×Ēמונו×Ē ×™×™×—×•×“×™×•×Ē", "backup_all": "הכל", - "backup_background_service_backup_failed_message": "נכשל בגיבוי נכסים. מנסה שוב...", - "backup_background_service_connection_failed_message": "נכשל בה×Ēחברו×Ē ×œ×Š×¨×Ē. מנסה שוב...", - "backup_background_service_current_upload_notification": "מ×ĸלה {}", - "backup_background_service_default_notification": "מחפש נכסים חדשים...", + "backup_background_service_backup_failed_message": "נכשל בגיבוי ×Ēמונו×Ē. מנסה שובâ€Ļ", + "backup_background_service_connection_failed_message": "נכשל בה×Ēחברו×Ē ×œ×Š×¨×Ē. מנסה שובâ€Ļ", + "backup_background_service_current_upload_notification": "מ×ĸלה {filename}", + "backup_background_service_default_notification": "מחפש ×Ēמונו×Ē ×—×“×Š×•×Ēâ€Ļ", "backup_background_service_error_title": "שגיא×Ē ×’×™×‘×•×™", - "backup_background_service_in_progress_notification": "מגבה א×Ē ×”× ×›×Ą×™× שלך...", - "backup_background_service_upload_failure_notification": "נכשל לה×ĸלו×Ē {}", + "backup_background_service_in_progress_notification": "מגבה א×Ē ×”×Ēמונו×Ē ×Š×œ×šâ€Ļ", + "backup_background_service_upload_failure_notification": "{filename} נכשל בה×ĸלאה", "backup_controller_page_albums": "אלבומים לגיבוי", - "backup_controller_page_background_app_refresh_disabled_content": "אפ׊ר ר×ĸנון אפליק×Ļיה ברק×ĸ בהגדרו×Ē > כללי > ר×ĸנון אפליק×Ļיה ברק×ĸ כדי להש×Ēמ׊ בגיבוי ברק×ĸ", + "backup_controller_page_background_app_refresh_disabled_content": "אפ׊ר ר×ĸנון אפליק×Ļיה ברק×ĸ בהגדרו×Ē > כללי > ר×ĸנון אפליק×Ļיה ברק×ĸ כדי להש×Ēמ׊ בגיבוי ברק×ĸ.", "backup_controller_page_background_app_refresh_disabled_title": "ר×ĸנון אפליק×Ļיה ברק×ĸ מושב×Ē", "backup_controller_page_background_app_refresh_enable_button_text": "לך להגדרו×Ē", "backup_controller_page_background_battery_info_link": "הראה לי איך", @@ -521,8 +522,8 @@ "backup_controller_page_background_battery_info_title": "מיטובי סוללה", "backup_controller_page_background_charging": "רק בט×ĸינה", "backup_controller_page_background_configure_error": "נכשל בהגדר×Ē ×Ē×Ļור×Ē ×Š×™×¨×•×Ē ×”×¨×§×ĸ", - "backup_controller_page_background_delay": "דחה גיבוי נכסים חדשים: {}", - "backup_controller_page_background_description": "הפ×ĸל א×Ē ×”×Š×™×¨×•×Ē ×¨×§×ĸ כדי לגבו×Ē ×‘××•×¤×Ÿ אוטומטי כל נכס חדש מבלי לה×Ļטרך לפ×Ēוח א×Ē ×”×™×™×Š×•×", + "backup_controller_page_background_delay": "השהה גיבוי של ×Ēמונו×Ē ×—×“×Š×•×Ē: {duration}", + "backup_controller_page_background_description": "הפ×ĸל א×Ē ×”×Š×™×¨×•×Ē ×¨×§×ĸ כדי לגבו×Ē ×‘××•×¤×Ÿ אוטומטי כל ×Ēמונה חדשה מבלי לה×Ļטרך לפ×Ēוח א×Ē ×”×™×™×Š×•×", "backup_controller_page_background_is_off": "גיבוי אוטומטי ברק×ĸ כבוי", "backup_controller_page_background_is_on": "גיבוי אוטומטי ברק×ĸ מופ×ĸל", "backup_controller_page_background_turn_off": "כבה שירו×Ē ×’×™×‘×•×™ ברק×ĸ", @@ -531,12 +532,12 @@ "backup_controller_page_backup": "גיבוי", "backup_controller_page_backup_selected": "נבחרו: ", "backup_controller_page_backup_sub": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× מגובים", - "backup_controller_page_created": "נו×Ļר ב: {}", - "backup_controller_page_desc_backup": "הפ×ĸל גיבוי חזי×Ē ×›×“×™ לה×ĸלו×Ē ×‘××•×¤×Ÿ אוטומטי נכסים חדשים לשר×Ē ×›×Š×¤×•×Ēחים א×Ē ×”×™×™×Š×•×", + "backup_controller_page_created": "נו×Ļר ב: {date}", + "backup_controller_page_desc_backup": "הפ×ĸל גיבוי חזי×Ē ×›×“×™ לה×ĸלו×Ē ×‘××•×¤×Ÿ אוטומטי ×Ēמונו×Ē ×—×“×Š×•×Ē ×œ×Š×¨×Ē ×›×Š×¤×•×Ēחים א×Ē ×”×™×™×Š×•×.", "backup_controller_page_excluded": "הוחרגו: ", - "backup_controller_page_failed": "נכשל ({})", - "backup_controller_page_filename": "׊ם קוב×Ĩ: {} [{}]", - "backup_controller_page_id": "מזהה: {}", + "backup_controller_page_failed": "({count}) נכשלו", + "backup_controller_page_filename": "׊ם הקוב×Ĩ: {size} [{filename}]", + "backup_controller_page_id": "מזהה: {id}", "backup_controller_page_info": "פרטי גיבוי", "backup_controller_page_none_selected": "אין בחירה", "backup_controller_page_remainder": "בהמ×Ēנה לגיבוי", @@ -545,14 +546,14 @@ "backup_controller_page_start_backup": "ה×Ēחל גיבוי", "backup_controller_page_status_off": "גיבוי חזי×Ē ××•×˜×•×ž×˜×™ כבוי", "backup_controller_page_status_on": "גיבוי חזי×Ē ××•×˜×•×ž×˜×™ מופ×ĸל", - "backup_controller_page_storage_format": "{} מ×Ēוך {} נו×Ļלו", + "backup_controller_page_storage_format": "{total} מ×Ēוך {used} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבו×Ē", "backup_controller_page_total_sub": "כל ה×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזי×Ē", "backup_controller_page_turn_on": "הפ×ĸל גיבוי חזי×Ē", "backup_controller_page_uploading_file_info": "מ×ĸלה מיד×ĸ ×ĸל הקוב×Ĩ", "backup_err_only_album": "לא ני×Ēן להסיר א×Ē ×”××œ×‘×•× היחיד", - "backup_info_card_assets": "נכסים", + "backup_info_card_assets": "×Ēמונו×Ē", "backup_manual_cancelled": "בוטל", "backup_manual_in_progress": "ה×ĸלאה כבר ב×Ēהליך. נסה אחרי זמן מה", "backup_manual_success": "ה×Ļלחה", @@ -566,25 +567,25 @@ "bugs_and_feature_requests": "באגים & בקשו×Ē ×œ×Ēכונו×Ē", "build": "גרס×Ē ×‘× ×™×™×”", "build_image": "גרס×Ē ×Ēוכנה", - "bulk_delete_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך למחוק בכמו×Ē ×’×“×•×œ×” {count, plural, one {נכס # כפול} other {# נכסים כפולים}}? זה ישמור ×ĸל הנכס הכי גדול של כל קבו×Ļה וימחק ל×Ļמי×Ēו×Ē ××Ē ×›×œ ׊אר הכפילויו×Ē. אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו!", - "bulk_keep_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להשאיר {count, plural, one {נכס # כפול} other {# נכסים כפולים}}? זה יפ×Ēור א×Ē ×›×œ הקבו×Ļו×Ē ×”×›×¤×•×œ×•×Ē ×ž×‘×œ×™ למחוק דבר.", - "bulk_trash_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לה×ĸביר לאשפה בכמו×Ē ×’×“×•×œ×” {count, plural, one {נכס # כפול} other {# נכסים כפולים}}? זה ישמור ×ĸל הנכס הגדול ביו×Ēר של כל קבו×Ļה וי×ĸביר לאשפה א×Ē ×›×œ ׊אר הכפילויו×Ē.", + "bulk_delete_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך למחוק בכמו×Ē ×’×“×•×œ×” {count, plural, one {×Ēמונה # כפולה} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}? זה ישמור ×ĸל ה×Ēמונה הכי גדולה של כל קבו×Ļה וימחק ל×Ļמי×Ēו×Ē ××Ē ×›×œ ׊אר הכפילויו×Ē. אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו!", + "bulk_keep_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להשאיר {count, plural, one {×Ēמונה # כפולה} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}? זה יסגור א×Ē ×›×œ הקבו×Ļו×Ē ×”×›×¤×•×œ×•×Ē ×ž×‘×œ×™ למחוק דבר.", + "bulk_trash_duplicates_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לה×ĸביר לאשפה בכמו×Ē ×’×“×•×œ×” {count, plural, one {×Ēמונה # כפולה} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}? זה ישמור ×ĸל ה×Ēמונה הגדולה ביו×Ēר של כל קבו×Ļה וי×ĸביר לאשפה א×Ē ×›×œ ׊אר הכפילויו×Ē.", "buy": "רכוש א×Ē Immich", - "cache_settings_album_thumbnails": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ דת ספרייה ({} נכסים)", + "cache_settings_album_thumbnails": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ דת ספרייה ({count} ×Ēמונו×Ē)", "cache_settings_clear_cache_button": "ניקוי מטמון", - "cache_settings_clear_cache_button_title": "מנקה א×Ē ×”×ž×˜×ž×•×Ÿ של היישום. זה ישפי×ĸ באופן מ׊מ×ĸו×Ēי ×ĸל הבי×Ļו×ĸים של היישום ×ĸד שהמטמון נבנה מחדש", + "cache_settings_clear_cache_button_title": "מנקה א×Ē ×”×ž×˜×ž×•×Ÿ של היישום. זה ישפי×ĸ באופן מ׊מ×ĸו×Ēי ×ĸל הבי×Ļו×ĸים של היישום ×ĸד שהמטמון מ×Ēמלא מחדש.", "cache_settings_duplicated_assets_clear_button": "נקה", "cache_settings_duplicated_assets_subtitle": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ׊נמ×Ļאים ברשימה השחורה של היישום", - "cache_settings_duplicated_assets_title": "נכסים משוכפלים ({})", - "cache_settings_image_cache_size": "גודל מטמון ×Ēמונה ({} נכסים)", + "cache_settings_duplicated_assets_title": "({count}) ×Ēמונו×Ē ×ž×Š×•×›×¤×œ×•×Ē", + "cache_settings_image_cache_size": "גודל מטמון ה×Ēמונה ({count} ×Ēמונו×Ē)", "cache_settings_statistics_album": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ ספרייה", - "cache_settings_statistics_assets": "{} נכסים ({})", + "cache_settings_statistics_assets": "{size} ×Ēמונו×Ē ({count})", "cache_settings_statistics_full": "×Ēמונו×Ē ×ž×œ××•×Ē", "cache_settings_statistics_shared": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ אלבום משו×Ē×Ŗ", "cache_settings_statistics_thumbnail": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", "cache_settings_statistics_title": "שימוש במטמון", - "cache_settings_subtitle": "שלוט בה×Ēנהגו×Ē ×Š×ž×™×¨×Ē ×”×ž×˜×ž×•×Ÿ של היישום הנייד", - "cache_settings_thumbnail_size": "גודל מטמון ×Ēמונה ממוז×ĸר×Ē ({} נכסים)", + "cache_settings_subtitle": "הגדר כי×Ļד אפליק×Ļיי×Ē Immich שומר×Ē × ×Ēונים באופן זמני", + "cache_settings_thumbnail_size": "גודל מטמון ×Ēמונה ממוז×ĸר×Ē ({count} ×Ēמונו×Ē)", "cache_settings_tile_subtitle": "שלוט בה×Ēנהגו×Ē ×”××—×Ą×•×Ÿ המקומי", "cache_settings_tile_title": "אחסון מקומי", "cache_settings_title": "הגדרו×Ē ×Š×ž×™×¨×Ē ×ž×˜×ž×•×Ÿ", @@ -610,12 +611,13 @@ "change_password_form_new_password": "סיסמה חדשה", "change_password_form_password_mismatch": "סיסמאו×Ē ×œ× ×Ēואמו×Ē", "change_password_form_reenter_new_password": "הכנס שוב סיסמה חדשה", + "change_pin_code": "שנה קוד PIN", "change_your_password": "×”×—×œ×Ŗ א×Ē ×”×Ą×™×Ą×ž×” שלך", "changed_visibility_successfully": "הנראו×Ē ×Š×•× ×Ēה בה×Ļלחה", "check_all": "לסמן הכל", - "check_corrupt_asset_backup": "בדוק גיבויים פגומים של נכסים", + "check_corrupt_asset_backup": "בדוק גיבויים פגומים של ×Ēמונו×Ē", "check_corrupt_asset_backup_button": "ב×Ļ×ĸ בדיקה", - "check_corrupt_asset_backup_description": "הר×Ĩ בדיקה זו רק ×ĸל Wi-Fi ולאחר שכל הנכסים גובו. ההליך ×ĸשוי לקח×Ē ×›×ž×” דקו×Ē.", + "check_corrupt_asset_backup_description": "הר×Ĩ בדיקה זו רק ×ĸל Wi-Fi ולאחר שכל ה×Ēמונו×Ē ×’×•×‘×•. ההליך ×ĸשוי לקח×Ē ×›×ž×” דקו×Ē.", "check_logs": "בדוק יומני רישום", "choose_matching_people_to_merge": "בחר אנשים ×Ēואמים למיזוג", "city": "×ĸיר", @@ -643,18 +645,19 @@ "comments_and_likes": "×Ēגובו×Ē & לייקים", "comments_are_disabled": "×Ēגובו×Ē ×ž×•×Š×‘×Ēו×Ē", "common_create_new_album": "×Ļור אלבום חדש", - "common_server_error": "נא לבדוק א×Ē ×—×™×‘×•×¨ הרש×Ē ×Š×œ×š, ×Ēוודא/י שהשר×Ē × ×’×™×Š ושגרסאו×Ē ××¤×œ×™×§×Ļיה/׊ר×Ē ×Ēואמו×Ē", + "common_server_error": "נא לבדוק א×Ē ×—×™×‘×•×¨ הרש×Ē ×Š×œ×š, ×Ēוודא/י שהשר×Ē × ×’×™×Š ושגרסאו×Ē ××¤×œ×™×§×Ļיה/׊ר×Ē ×Ēואמו×Ē.", "completed": "הושלמו", "confirm": "אישור", "confirm_admin_password": "אישור סיסמ×Ē ×ž× ×”×œ", - "confirm_delete_face": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×¤× ×™× של {name} מהנכס?", + "confirm_delete_face": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×¤× ×™× של {name} מה×Ēמונה?", "confirm_delete_shared_link": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”×§×™×Š×•×¨ המשו×Ē×Ŗ הזה?", - "confirm_keep_this_delete_others": "כל ׊אר הנכסים ב×ĸרימה יימחקו למ×ĸט נכס זה. האם באמ×Ē ×‘×¨×Ļונך להמשיך?", + "confirm_keep_this_delete_others": "כל ׊אר ×Ēמונו×Ē ×Š×‘×ĸרימה יימחקו למ×ĸט ×Ēמונה זא×Ē. האם באמ×Ē ×‘×¨×Ļונך להמשיך?", + "confirm_new_pin_code": "א׊ר קוד PIN חדש", "confirm_password": "א׊ר סיסמה", "contain": "מכיל", "context": "הקשר", "continue": "המשך", - "control_bottom_app_bar_album_info_shared": "{} פריטים ¡ משו×Ēפים", + "control_bottom_app_bar_album_info_shared": "{count} פריטים ¡ משו×Ēפים", "control_bottom_app_bar_create_new_album": "×Ļור אלבום חדש", "control_bottom_app_bar_delete_from_immich": "מחק מהשר×Ē", "control_bottom_app_bar_delete_from_local": "מחק מהמכשיר", @@ -684,9 +687,9 @@ "create_link_to_share_description": "אפ׊ר לכל אחד ×ĸם הקישור לראו×Ē ××Ē ×”×Ēמונו×Ē ×Š× ×‘×—×¨×•", "create_new": "×Ļור חדש", "create_new_person": "×Ļור אדם חדש", - "create_new_person_hint": "הק×Ļה א×Ē ×”× ×›×Ą×™× שנבחרו לאדם חדש", + "create_new_person_hint": "הק×Ļה א×Ē ×”×Ēמונו×Ē ×Š× ×‘×—×¨×• לאדם חדש", "create_new_user": "×Ļור מ׊×Ēמ׊ חדש", - "create_shared_album_page_share_add_assets": "×”×•×Ą×Ŗ נכסים", + "create_shared_album_page_share_add_assets": "×”×•×Ą×Ŗ ×Ēמונו×Ē", "create_shared_album_page_share_select_photos": "בחיר×Ē ×Ēמונו×Ē", "create_tag": "×Ļור ×Ēג", "create_tag_description": "×Ļור ×Ēג חדש. ×ĸבור ×Ēגים מקוננים, נא להזין א×Ē ×”× ×Ēיב המלא של ה×Ēג כולל קווים נטויים.", @@ -695,16 +698,14 @@ "crop": "ח×Ēוך", "curated_object_page_title": "דברים", "current_device": "מכשיר נוכחי", + "current_pin_code": "קוד PIN הנוכחי", "current_server_address": "כ×Ēוב×Ē ×Š×¨×Ē × ×•×›×—×™×Ē", "custom_locale": "אזור שפה מו×Ēאם אישי×Ē", "custom_locale_description": "×ĸ×Ļב ×Ēאריכים ומספרים ×ĸל סמך השפה והאזור", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", "date_after": "×Ēאריך אחרי", "date_and_time": "×Ēאריך וש×ĸה", "date_before": "×Ēאריך לפני", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "×Ēאריך לידה נ׊מר בה×Ļלחה", "date_range": "טווח ×Ēאריכים", "day": "יום", @@ -712,12 +713,12 @@ "deduplication_criteria_1": "גודל ×Ēמונה בב×Ēים", "deduplication_criteria_2": "ספיר×Ē × ×Ēוני EXIF", "deduplication_info": "מיד×ĸ ×ĸל ביטול כפילויו×Ē", - "deduplication_info_description": "כדי לבחור מרא׊ נכסים באופן אוטומטי ולהסיר כפילויו×Ē ×‘×›×ž×•×Ē ×’×“×•×œ×”, אנו מס×Ēכלים ×ĸל:", + "deduplication_info_description": "כדי לבחור מרא׊ ×Ēמונו×Ē ×‘××•×¤×Ÿ אוטומטי ולהסיר כפילויו×Ē ×‘×›×ž×•×Ē ×’×“×•×œ×”, אנו מס×Ēכלים ×ĸל:", "default_locale": "׊פ×Ē ×‘×¨×™×¨×Ē ×ž×—×“×œ", "default_locale_description": "פורמט ×Ēאריכים ומספרים מבוסס ׊פ×Ē ×”×“×¤×“×¤×Ÿ שלך", "delete": "מחק", "delete_album": "מחק אלבום", - "delete_api_key_prompt": "האם באמ×Ē ×‘×¨×Ļונך למחוק מפ×Ēח ה-API הזה?", + "delete_api_key_prompt": "האם א×Ēה בטוח שבר×Ļונך למחוק מפ×Ēח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו ל×Ļמי×Ēו×Ē ×ž×”×Š×¨×Ē ×•×ž×”×ž×›×Š×™×¨ שלך", "delete_dialog_alert_local": "הפריטים האלה יוסרו ל×Ļמי×Ēו×Ē ×ž×”×ž×›×Š×™×¨ שלך אבל ×ĸדיין יהיו זמינים בשר×Ē", "delete_dialog_alert_local_non_backed_up": "חלק מהפריטים לא מגובים לשר×Ē ×•×™×•×Ą×¨×• ל×Ļמי×Ēו×Ē ×ž×”×ž×›×Š×™×¨ שלך", @@ -738,7 +739,7 @@ "delete_tag_confirmation_prompt": "האם באמ×Ē ×‘×¨×Ļונך למחוק ×Ēג {tagName}?", "delete_user": "מחק מ׊×Ēמ׊", "deleted_shared_link": "קישור משו×Ē×Ŗ נמחק", - "deletes_missing_assets": "מוחק נכסים שחסרים בדיסק", + "deletes_missing_assets": "מוחק ×Ēמונו×Ē ×Š×—×Ą×¨×•×Ē ×‘×“×™×Ą×§", "description": "×Ēיאור", "description_input_hint_text": "×”×•×Ą×Ŗ ×Ēיאור...", "description_input_submit_error": "שגיאה ב×ĸדכון ×Ēיאור, בדוק א×Ē ×”×™×•×ž×Ÿ לפרטים נוספים", @@ -753,7 +754,7 @@ "display_options": "ה×Ļג×Ē ××¤×Š×¨×•×™×•×Ē", "display_order": "סדר ×Ē×Ļוגה", "display_original_photos": "ה×Ļג×Ē ×Ēמונו×Ē ×ž×§×•×¨×™×•×Ē", - "display_original_photos_setting_description": "ה×ĸדת לה×Ļיג א×Ē ×”×Ēמונה המקורי×Ē ×‘×ĸ×Ē ×Ļפיי×Ē × ×›×Ą במקום ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×›××Š×¨ הנכס המקורי ×Ēומך ב×Ē×Ļוגה בדפדפן. זה ×ĸלול לגרום ל×Ēמונו×Ē ×œ×”×™×•×Ē ×ž×•×Ļגו×Ē ×‘××™×˜×™×•×Ē.", + "display_original_photos_setting_description": "ה×Ļג ×Ēמונה מקורי×Ē ×‘×ĸ×Ē ×Ļפייה ב×Ēמונה במקום ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×›××Š×¨ ה×Ēמונה המקורי×Ē ×Ēומכ×Ē ×‘×Ē×Ļוגה בדפדפן. זה ×ĸלול לגרום ל×Ēמונו×Ē ×œ×”×™×•×Ē ×ž×•×Ļגו×Ē ×‘××™×˜×™×•×Ē.", "do_not_show_again": "אל ×Ē×Ļיג א×Ē ×”×”×•×“×ĸה הזא×Ē ×Š×•×‘", "documentation": "×Ēי×ĸוד", "done": "סיום", @@ -763,20 +764,20 @@ "download_enqueue": "הורדה נוספה ל×Ēור", "download_error": "שגיא×Ē ×”×•×¨×“×”", "download_failed": "הורדה נכשלה", - "download_filename": "קוב×Ĩ: {}", + "download_filename": "קוב×Ĩ: {filename}", "download_finished": "הורדה הס×Ēיימה", "download_include_embedded_motion_videos": "סרטונים מוטמ×ĸים", "download_include_embedded_motion_videos_description": "כלול סרטונים מוט×ĸמים ב×Ēמונו×Ē ×ĸם ×Ēנו×ĸה כקוב×Ĩ נפרד", "download_notfound": "הורדה לא נמ×Ļא", "download_paused": "הורדה הופסקה", "download_settings": "הורדה", - "download_settings_description": "ניהול הגדרו×Ē ×”×§×Š×•×¨×•×Ē ×œ×”×•×¨×“×Ē × ×›×Ą×™×", + "download_settings_description": "ניהול הגדרו×Ē ×”×§×Š×•×¨×•×Ē ×œ×”×•×¨×“×Ē ×Ēמונו×Ē", "download_started": "הורדה החלה", "download_sucess": "ה×Ļלח×Ē ×”×•×¨×“×”", "download_sucess_android": "המדיה הורדה אל DCIM/Immich", "download_waiting_to_retry": "מחכה כדי לנסו×Ē ×Š×•×‘", "downloading": "מוריד", - "downloading_asset_filename": "מוריד נכס {filename}", + "downloading_asset_filename": "מוריד ×Ēמונה {filename}", "downloading_media": "מוריד מדיה", "drop_files_to_upload": "שחרר קב×Ļים בכל מקום כדי לה×ĸלו×Ē", "duplicates": "כפילויו×Ē", @@ -809,7 +810,7 @@ "email": "דוא\"ל", "empty_folder": "×Ēיקיה זו ריקה", "empty_trash": "רוקן אשפה", - "empty_trash_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לרוקן א×Ē ×”××Š×¤×”? זה יסיר ל×Ļמי×Ēו×Ē ××Ē ×›×œ הנכסים באשפה מImmich.\nאין באפשרו×Ēך לבטל פ×ĸולה זו!", + "empty_trash_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לרוקן א×Ē ×”××Š×¤×”? זה יסיר ל×Ļמי×Ēו×Ē ××Ē ×›×œ ה×Ēמונו×Ē ×ž×”××Š×¤×” של השר×Ē.\nאין באפשרו×Ēך לבטל פ×ĸולה זו!", "enable": "אפ׊ר", "enabled": "מופ×ĸל", "end_date": "×Ēאריך סיום", @@ -817,42 +818,44 @@ "enter_wifi_name": "הזן ׊ם אינטרנט אלחוטי", "error": "שגיאה", "error_change_sort_album": "שינוי סדר מיון אלבום נכשל", - "error_delete_face": "שגיאה במחיק×Ē ×¤× ×™× מנכס", + "error_delete_face": "שגיאה במחיק×Ē ×¤× ×™× מ×Ēמונה", "error_loading_image": "שגיאה בט×ĸינ×Ē ×”×Ēמונה", - "error_saving_image": "שגיאה: {}", + "error_saving_image": "שגיאה: {error}", "error_title": "שגיאה - משהו הש×Ēבש", "errors": { - "cannot_navigate_next_asset": "לא ני×Ēן לנווט לנכס הבא", - "cannot_navigate_previous_asset": "לא ני×Ēן לנווט לנכס הקודם", + "cannot_navigate_next_asset": "לא ני×Ēן לנווט ל×Ēמונה הבאה", + "cannot_navigate_previous_asset": "לא ני×Ēן לנווט ל×Ēמונה הקודמ×Ē", "cant_apply_changes": "לא ני×Ēן להחיל שינויים", "cant_change_activity": "לא ני×Ēן {enabled, select, true {להשבי×Ē} other {לאפשר}} פ×ĸילו×Ē", - "cant_change_asset_favorite": "לא ני×Ēן לשנו×Ē ×ž×Ļב מו×ĸדת ×ĸבור נכס", - "cant_change_metadata_assets_count": "לא ני×Ēן לשנו×Ē ××Ē ×”×ž×˜×-× ×Ēונים של {count, plural, one {נכס #} other {# נכסים}}", + "cant_change_asset_favorite": "לא ני×Ēן לשנו×Ē ×Ą×™×ž×•×Ÿ מו×ĸדת ×ĸבור ה×Ēמונה", + "cant_change_metadata_assets_count": "לא ני×Ēן לשנו×Ē ××Ē ×”×ž×˜×-× ×Ēונים של {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "cant_get_faces": "לא ני×Ēן לקבל פנים", "cant_get_number_of_comments": "לא ני×Ēן לקבל א×Ē ×ž×Ą×¤×¨ ה×Ēגובו×Ē", "cant_search_people": "לא ני×Ēן לחפש אנשים", "cant_search_places": "לא ני×Ēן לחפש מקומו×Ē", "cleared_jobs": "משימו×Ē × ×•×§×• ×ĸבור: {job}", - "error_adding_assets_to_album": "שגיאה בהוספ×Ē × ×›×Ą×™× לאלבום", + "error_adding_assets_to_album": "שגיאה בהוספ×Ē ×Ēמונו×Ē ×œ××œ×‘×•×", "error_adding_users_to_album": "שגיאה בהוספ×Ē ×ž×Š×Ēמשים לאלבום", "error_deleting_shared_user": "שגיאה במחיק×Ē ×ž×Š×Ēמ׊ משו×Ē×Ŗ", "error_downloading": "שגיאה בהורד×Ē {filename}", "error_hiding_buy_button": "שגיאה בהס×Ēר×Ē ×œ×—×Ļן 'קנה'", - "error_removing_assets_from_album": "שגיאה בהסר×Ē × ×›×Ą×™× מאלבום, בדוק א×Ē ×”×ž×Ą×•×Ŗ לפרטים נוספים", - "error_selecting_all_assets": "שגיאה בבחיר×Ē ×›×œ הנכסים", + "error_removing_assets_from_album": "שגיאה בהסר×Ē ×Ēמונו×Ē ×ž×”××œ×‘×•×, בדוק א×Ē ×”×™×•×ž× ×™× לפרטים נוספים", + "error_selecting_all_assets": "שגיאה בבחיר×Ē ×›×œ ה×Ēמונו×Ē", "exclusion_pattern_already_exists": "דפוס החרגה זה כבר קיים.", "failed_job_command": "הפקודה {command} נכשלה ×ĸבור המשימה: â€Ēâ€Ē{job}", "failed_to_create_album": "י×Ļיר×Ē ××œ×‘×•× נכשלה", "failed_to_create_shared_link": "י×Ļיר×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ נכשלה", "failed_to_edit_shared_link": "×ĸריכ×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ נכשלה", "failed_to_get_people": "קבל×Ē ×× ×Š×™× נכשלה", - "failed_to_keep_this_delete_others": "נכשל לשמור א×Ē ×”× ×›×Ą הזה ולמחוק א×Ē ×”× ×›×Ą×™× האחרים", - "failed_to_load_asset": "ט×ĸינ×Ē × ×›×Ą נכשלה", - "failed_to_load_assets": "ט×ĸינ×Ē × ×›×Ą×™× נכשלה", + "failed_to_keep_this_delete_others": "הפ×ĸולה נכשלה לא ני×Ēן היה לשמור א×Ē ×”×Ēמונה הזו ולמחוק א×Ē ×Š××¨ ה×Ēמונו×Ē", + "failed_to_load_asset": "ט×ĸינ×Ē ×”×Ēמונה נכשלה", + "failed_to_load_assets": "ט×ĸינ×Ē ×”×Ēמונו×Ē × ×›×Š×œ×”", + "failed_to_load_notifications": "איר×ĸה שגיאה ב×ĸ×Ē ×˜×ĸינ×Ē ×”×”×Ēראו×Ē", "failed_to_load_people": "נכשל באחזור אנשים", "failed_to_remove_product_key": "הסר×Ē ×ž×¤×Ēח מו×Ļר נכשלה", - "failed_to_stack_assets": "י×Ļיר×Ē ×ĸרימ×Ē × ×›×Ą×™× נכשלה", - "failed_to_unstack_assets": "ביטול ×ĸרימ×Ē × ×›×Ą×™× נכשל", + "failed_to_stack_assets": "י×Ļיר×Ē ×ĸרימ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", + "failed_to_unstack_assets": "ביטול ×ĸרימ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", + "failed_to_update_notification_status": "שגיאה ב×ĸדכון הה×Ēראה", "import_path_already_exists": "× ×Ēיב הייבוא הזה כבר קיים.", "incorrect_email_or_password": "דוא\"ל או סיסמה שגויים", "paths_validation_failed": "{paths, plural, one {× ×Ēיב # נכשל} other {# × ×Ēיבים נכשלו}} אימו×Ē", @@ -860,17 +863,17 @@ "quota_higher_than_disk_size": "הגדר×Ē ×ž×›×Ą×” גבוהה יו×Ēר מגודל הדיסק", "repair_unable_to_check_items": "לא ני×Ēן לסמן {count, select, one {פריט} other {פריטים}}", "unable_to_add_album_users": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ מ׊×Ēמשים לאלבום", - "unable_to_add_assets_to_shared_link": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ נכסים לקישור משו×Ē×Ŗ", + "unable_to_add_assets_to_shared_link": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēמונו×Ē ×œ×§×™×Š×•×¨ משו×Ē×Ŗ", "unable_to_add_comment": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēגובה", "unable_to_add_exclusion_pattern": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ דפוס החרגה", "unable_to_add_import_path": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ × ×Ēיב ייבוא", "unable_to_add_partners": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ שו×Ēפים", - "unable_to_add_remove_archive": "לא ני×Ēן {archived, select, true {להסיר נכס מ} other {×œ×”×•×Ą×™×Ŗ נכס ל}}ארכיון", - "unable_to_add_remove_favorites": "לא ני×Ēן {favorite, select, true {×œ×”×•×Ą×™×Ŗ נכס ל} other {להסיר נכס מ}}מו×ĸדפים", + "unable_to_add_remove_archive": "לא ני×Ēן {archived, select, true {להסיר ×Ēמונה מ} other {×œ×”×•×Ą×™×Ŗ ×Ēמונה ל}}ארכיון", + "unable_to_add_remove_favorites": "לא ני×Ēן {favorite, select, true {×œ×”×•×Ą×™×Ŗ ×Ēמונה ל} other {להסיר ×Ēמונה מ}}מו×ĸדפים", "unable_to_archive_unarchive": "לא ני×Ēן {archived, select, true {לה×ĸביר לארכיון} other {להו×Ļיא מארכיון}}", "unable_to_change_album_user_role": "לא ני×Ēן לשנו×Ē ××Ē ×”×Ēפקיד של מ׊×Ēמ׊ האלבום", "unable_to_change_date": "לא ני×Ēן לשנו×Ē ×Ēאריך", - "unable_to_change_favorite": "לא ני×Ēן לשנו×Ē ×ž×Ļב מו×ĸדת ×ĸבור נכס", + "unable_to_change_favorite": "לא ני×Ēן לשנו×Ē ×ž×Ļב מו×ĸדת ×ĸבור ה×Ēמונה", "unable_to_change_location": "לא ני×Ēן לשנו×Ē ×ž×™×§×•×", "unable_to_change_password": "לא ני×Ēן לשנו×Ē ×Ą×™×Ą×ž×”", "unable_to_change_visibility": "לא ני×Ēן לשנו×Ē ××Ē ×”× ×¨××•×Ē ×ĸבור {count, plural, one {אדם #} other {# אנשים}}", @@ -883,8 +886,8 @@ "unable_to_create_library": "לא ני×Ēן לי×Ļור ספרייה", "unable_to_create_user": "לא ני×Ēן לי×Ļור מ׊×Ēמ׊", "unable_to_delete_album": "לא ני×Ēן למחוק אלבום", - "unable_to_delete_asset": "לא ני×Ēן למחוק נכס", - "unable_to_delete_assets": "שגיאה במחיק×Ē × ×›×Ą×™×", + "unable_to_delete_asset": "לא ני×Ēן למחוק א×Ē ×”×Ēמונה", + "unable_to_delete_assets": "שגיאה במחיק×Ē ×”×Ēמונו×Ē", "unable_to_delete_exclusion_pattern": "לא ני×Ēן למחוק דפוס החרגה", "unable_to_delete_import_path": "לא ני×Ēן למחוק א×Ē × ×Ēיב הייבוא", "unable_to_delete_shared_link": "לא ני×Ēן למחוק קישור משו×Ē×Ŗ", @@ -901,27 +904,28 @@ "unable_to_link_motion_video": "לא ני×Ēן לקשר סרטון ×Ēנו×ĸה", "unable_to_link_oauth_account": "לא ני×Ēן לקשר חשבון OAuth", "unable_to_load_album": "לא ני×Ēן לט×ĸון אלבום", - "unable_to_load_asset_activity": "לא ני×Ēן לט×ĸון א×Ē ×¤×ĸילו×Ē ×”× ×›×Ą", + "unable_to_load_asset_activity": "לא ני×Ēן לט×ĸון א×Ē ×”×¤×ĸילו×Ē ×‘×Ēמונה", "unable_to_load_items": "לא ני×Ēן לט×ĸון פריטים", "unable_to_load_liked_status": "לא ני×Ēן לט×ĸון מ×Ļב 'אהב×Ēי'", "unable_to_log_out_all_devices": "לא ני×Ēן לנ×Ē×§ א×Ē ×›×œ המכשירים", "unable_to_log_out_device": "לא ני×Ēן לנ×Ē×§ מכשיר", "unable_to_login_with_oauth": "לא ני×Ēן לה×Ēחבר באמ×Ļ×ĸו×Ē OAuth", "unable_to_play_video": "לא ני×Ēן לנגן סרטון", - "unable_to_reassign_assets_existing_person": "לא ני×Ēן להק×Ļו×Ē ×ž×—×“×Š נכסים ל{name, select, null {אדם קיים} other {{name}}}", - "unable_to_reassign_assets_new_person": "לא ני×Ēן להק×Ļו×Ē ×ž×—×“×Š נכסים לאדם חדש", + "unable_to_reassign_assets_existing_person": "לא ני×Ēן להק×Ļו×Ē ×ž×—×“×Š ×Ēמונו×Ē ×œ{name, select, null {אדם קיים} other {{name}}}", + "unable_to_reassign_assets_new_person": "לא ני×Ēן להק×Ļו×Ē ×ž×—×“×Š ×Ēמונו×Ē ×œ××“× חדש", "unable_to_refresh_user": "לא ני×Ēן לר×ĸנן א×Ē ×”×ž×Š×Ēמ׊", "unable_to_remove_album_users": "לא ני×Ēן להסיר מ׊×Ēמשים מהאלבום", "unable_to_remove_api_key": "לא ני×Ēן להסיר מפ×Ēח API", - "unable_to_remove_assets_from_shared_link": "לא ני×Ēן להסיר נכסים מקישור משו×Ē×Ŗ", + "unable_to_remove_assets_from_shared_link": "לא ני×Ēן להסיר ×Ēמונו×Ē ×ž×§×™×Š×•×¨ משו×Ē×Ŗ", "unable_to_remove_deleted_assets": "לא ני×Ēן להסיר קב×Ļים לא מקוונים", "unable_to_remove_library": "לא ני×Ēן להסיר ספרייה", "unable_to_remove_partner": "לא ני×Ēן להסיר שו×Ē×Ŗ", "unable_to_remove_reaction": "לא ני×Ēן להסיר ×Ēגובה", "unable_to_repair_items": "לא ני×Ēן ל×Ēקן פריטים", "unable_to_reset_password": "לא ני×Ēן לאפס סיסמה", + "unable_to_reset_pin_code": "לא ני×Ēן לאפס קוד PIN", "unable_to_resolve_duplicate": "לא ני×Ēן לפ×Ēור כפילו×Ē", - "unable_to_restore_assets": "לא ני×Ēן לשחזר נכסים", + "unable_to_restore_assets": "לא ני×Ēן לשחזר ×Ēמונו×Ē", "unable_to_restore_trash": "לא ני×Ēן לשחזר אשפה", "unable_to_restore_user": "לא ני×Ēן לשחזר מ׊×Ēמ׊", "unable_to_save_album": "לא ני×Ēן לשמור אלבום", @@ -935,7 +939,7 @@ "unable_to_set_feature_photo": "לא ני×Ēן להגדיר ×Ēמונה מיי×Ļג×Ē", "unable_to_set_profile_picture": "לא ני×Ēן להגדיר ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", "unable_to_submit_job": "לא ני×Ēן לשלוח משימה", - "unable_to_trash_asset": "לא ני×Ēן לה×ĸביר נכס לאשפה", + "unable_to_trash_asset": "לא ני×Ēן לה×ĸביר ×Ēמונה לאשפה", "unable_to_unlink_account": "לא ני×Ēן לבטל קישור חשבון", "unable_to_unlink_motion_video": "לא ני×Ēן לבטל קישור סרטון ×Ēנו×ĸה", "unable_to_update_album_cover": "לא ני×Ēן ל×ĸדכן ×ĸטיפ×Ē ××œ×‘×•×", @@ -947,16 +951,15 @@ "unable_to_update_user": "לא ני×Ēן ל×ĸדכן מ׊×Ēמ׊", "unable_to_upload_file": "לא ני×Ēן לה×ĸלו×Ē ×§×•×‘×Ĩ" }, - "exif": "Exif", "exif_bottom_sheet_description": "×”×•×Ą×Ŗ ×Ēיאור...", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", "exif_bottom_sheet_people": "אנשים", "exif_bottom_sheet_person_add_person": "×”×•×Ą×Ŗ ׊ם", - "exif_bottom_sheet_person_age": "גיל {}", - "exif_bottom_sheet_person_age_months": "גיל {} חודשים", - "exif_bottom_sheet_person_age_year_months": "גיל שנה, {} חודשים", - "exif_bottom_sheet_person_age_years": "גיל {}", + "exif_bottom_sheet_person_age": "גיל {age}", + "exif_bottom_sheet_person_age_months": "גיל {months} חודשים", + "exif_bottom_sheet_person_age_year_months": "גיל שנה ו-{months} חודשים", + "exif_bottom_sheet_person_age_years": "גיל {years}", "exit_slideshow": "×Ļא ממ×Ļג×Ē ×Š×§×•×¤×™×•×Ē", "expand_all": "הרחב הכל", "experimental_settings_new_asset_list_subtitle": "×ĸבודה ב×Ēהליך", @@ -977,12 +980,12 @@ "external_network_sheet_info": "כאשר לא ×ĸל ר׊×Ē ×”××™× ×˜×¨× ×˜ האלחוטי המו×ĸדפ×Ē, היישום י×Ēחבר לשר×Ē ×“×¨×š הכ×Ēוב×Ē ×”×¨××Š×•× ×” שני×Ēן להשיג מהכ×Ēובו×Ē ×Š×œ×”×œ×Ÿ, החל מלמ×ĸלה למטה", "face_unassigned": "לא מוק×Ļה", "failed": "נכשלו", - "failed_to_load_assets": "ט×ĸינ×Ē × ×›×Ą×™× נכשלה", + "failed_to_load_assets": "ט×ĸינ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", "failed_to_load_folder": "ט×ĸינ×Ē ×Ēיקיה נכשלה", "favorite": "מו×ĸדת", "favorite_or_unfavorite_photo": "×”×•×Ą×Ŗ או הסר ×Ēמונה מהמו×ĸדפים", "favorites": "מו×ĸדפים", - "favorites_page_no_favorites": "לא נמ×Ļאו נכסים מו×ĸדפים", + "favorites_page_no_favorites": "לא נמ×Ļאו ×Ēמונו×Ē ×ž×•×ĸדפים", "feature_photo_updated": "×Ēמונה מיי×Ļג×Ē ×ĸודכנה", "features": "×Ēכונו×Ē", "features_setting_description": "ניהול ×Ēכונו×Ē ×”×™×™×Š×•×", @@ -992,6 +995,7 @@ "filetype": "סוג קוב×Ĩ", "filter": "סנן", "filter_people": "סנן אנשים", + "filter_places": "סינון מקומו×Ē", "find_them_fast": "מ×Ļא או×Ēם מהר לפי ׊ם ×ĸם חיפוש", "fix_incorrect_match": "×Ēקן ה×Ēאמה שגויה", "folder": "×Ēיקיה", @@ -1029,24 +1033,24 @@ "hide_password": "הס×Ēר סיסמה", "hide_person": "הס×Ēר אדם", "hide_unnamed_people": "הס×Ēר אנשים ללא ׊ם", - "home_page_add_to_album_conflicts": "{added} נכסים נוספו לאלבום {album}. {failed} נכסים כבר נמ×Ļאים באלבום", - "home_page_add_to_album_err_local": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ נכסים מקומיים לאלבום ×ĸדיין, מדלג", - "home_page_add_to_album_success": "{added} נכסים נוספו לאלבום {album}", - "home_page_album_err_partner": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ נכסי שו×Ē×Ŗ לאלבום ×ĸדיין, מדלג", - "home_page_archive_err_local": "לא ני×Ēן לה×ĸביר לארכיון נכסים מקומיים ×ĸדיין, מדלג", - "home_page_archive_err_partner": "לא ני×Ēן לה×ĸביר לארכיון נכסי שו×Ē×Ŗ, מדלג", + "home_page_add_to_album_conflicts": "{added} ×Ēמונו×Ē × ×•×Ą×¤×• לאלבום {album}. {failed} ×Ēמונו×Ē ×›×‘×¨ נמ×Ļאו×Ē ×‘××œ×‘×•×.", + "home_page_add_to_album_err_local": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē ×œ××œ×‘×•× ×ĸדיין, מדלג", + "home_page_add_to_album_success": "{added} ×Ēמונו×Ē × ×•×Ą×¤×• לאלבום {album}.", + "home_page_album_err_partner": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēמונ×Ē ×Š×•×Ē×Ŗ לאלבום ×ĸדיין, מדלג", + "home_page_archive_err_local": "לא ני×Ēן לה×ĸביר לארכיון ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē ×ĸדיין, מדלג", + "home_page_archive_err_partner": "לא ני×Ēן לה×ĸביר לארכיון ×Ēמונו×Ē ×Š×œ השו×Ē×Ŗ, מדלג", "home_page_building_timeline": "בונה א×Ē ×Ļיר הזמן", - "home_page_delete_err_partner": "לא ני×Ēן למחוק נכסי שו×Ē×Ŗ, מדלג", - "home_page_delete_remote_err_local": "נכסים מקומיים נבחרו מרחוק למחיקה, מדלג", - "home_page_favorite_err_local": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ למו×ĸדפים נכסים מקומיים ×ĸדיין, מדלג", - "home_page_favorite_err_partner": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ למו×ĸדפים נכסי שו×Ē×Ŗ ×ĸדיין, מדלג", + "home_page_delete_err_partner": "לא ני×Ēן למחוק ×Ēמונו×Ē ×Š×œ השו×Ē×Ŗ, מדלג", + "home_page_delete_remote_err_local": "×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē × ×‘×—×¨×• מרחוק למחיקה, מדלג", + "home_page_favorite_err_local": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ למו×ĸדפים ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē ×ĸדיין, מדלג", + "home_page_favorite_err_partner": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ למו×ĸדפים ×Ēמונו×Ē ×Š×œ השו×Ē×Ŗ ×ĸדיין, מדלג", "home_page_first_time_notice": "אם זא×Ē ×”×¤×ĸם הראשונה ׊א×Ē/ה מ׊×Ēמ׊/×Ē ×‘×™×™×Š×•×, נא להקפיד לבחור אלבומ(ים) לגיבוי כך ׊×Ļיר הזמן יוכל לאכלס ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× באלבומ(ים)", - "home_page_share_err_local": "לא ני×Ēן לש×Ē×Ŗ נכסים מקומיים ×ĸל ידי קישור, מדלג", - "home_page_upload_err_limit": "ני×Ēן לה×ĸלו×Ē ×¨×§ מקסימום של 30 נכסים בכל פ×ĸם, מדלג", + "home_page_share_err_local": "לא ני×Ēן לש×Ē×Ŗ ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē ×ĸל ידי קישור, מדלג", + "home_page_upload_err_limit": "ני×Ēן לה×ĸלו×Ē ×¨×§ מקסימום של 30 ×Ēמונו×Ē ×‘×›×œ פ×ĸם, מדלג", "host": "מארח", "hour": "׊×ĸה", "ignore_icloud_photos": "ה×Ē×ĸלם מ×Ēמונו×Ē iCloud", - "ignore_icloud_photos_description": "×Ēמונו×Ē ×Š×ž××•×—×Ą× ×•×Ē ×‘-iCloud לא יו×ĸלו לשר×Ē ×”-Immich", + "ignore_icloud_photos_description": "×Ēמונו×Ē ×Š×ž××•×—×Ą× ×•×Ē ×‘-iCloud לא יו×ĸלו לשר×Ē", "image": "×Ēמונה", "image_alt_text_date": "{isVideo, select, true {סרטון ׊×Ļולם} other {×Ēמונה ׊×Ļולמה}} ב-{date}", "image_alt_text_date_1_person": "{isVideo, select, true {סרטון ׊×Ļולם} other {×Ēמונה ׊×Ļולמה}} ×ĸם {person1} ב-{date}", @@ -1070,7 +1074,7 @@ "in_archive": "בארכיון", "include_archived": "כלול ארכיון", "include_shared_albums": "כלול אלבומים משו×Ēפים", - "include_shared_partner_assets": "כלול נכסי שו×Ē×Ŗ משו×Ēפים", + "include_shared_partner_assets": "כלול ×Ēמונו×Ē ×Š×Š×•×Ēפו ×ĸ\"י השו×Ē×Ŗ", "individual_share": "שי×Ēות יחיד", "individual_shares": "שי×Ēופים בודדים", "info": "מיד×ĸ", @@ -1089,7 +1093,7 @@ "keep": "שמור", "keep_all": "שמור הכל", "keep_this_delete_others": "שמור ×ĸל זה, מחק אחרים", - "kept_this_deleted_others": "נכס זה נ׊מר ונמחקו {count, plural, one {נכס #} other {# נכסים}}", + "kept_this_deleted_others": "×Ēמונה זו נשמרה ונמחקו {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "keyboard_shortcuts": "קי×Ļורי מקלד×Ē", "language": "שפה", "language_setting_description": "בחר א×Ē ×”×Š×¤×” המו×ĸדפ×Ē ×ĸליך", @@ -1104,7 +1108,7 @@ "library_options": "אפשרויו×Ē ×Ą×¤×¨×™×™×”", "library_page_device_albums": "אלבומים במכשיר", "library_page_new_album": "אלבום חדש", - "library_page_sort_asset_count": "מספר נכסים", + "library_page_sort_asset_count": "מספר ×Ēמונו×Ē", "library_page_sort_created": "×Ēאריך י×Ļירה", "library_page_sort_last_modified": "שונה לאחרונה", "library_page_sort_title": "כו×Ēר×Ē ××œ×‘×•×", @@ -1132,10 +1136,9 @@ "logged_out_device": "מכשיר מנו×Ē×§", "login": "כניסה", "login_disabled": "כניסה למ×ĸרכ×Ē ×”×•×Š×‘×Ēה", - "login_form_api_exception": "חריג×Ē API. נא לבדוק א×Ē ×›×Ēוב×Ē ×”×Š×¨×Ē ×•×œ× ×Ą×•×Ē ×Š×•×‘", + "login_form_api_exception": "חריג×Ē API. נא לבדוק א×Ē ×›×Ēוב×Ē ×”×Š×¨×Ē ×•×œ× ×Ą×•×Ē ×Š×•×‘.", "login_form_back_button_text": "חזרה", "login_form_email_hint": "yourmail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "כ×Ēוב×Ē × ×§×•×“×Ē ×§×Ļה השר×Ē", "login_form_err_http": "נא ל×Ļיין //:http או //:https", "login_form_err_invalid_email": "דוא\"ל שגוי", @@ -1145,11 +1148,11 @@ "login_form_failed_get_oauth_server_config": "שגיאה בה×Ēחברו×Ē ×‘××ž×Ļ×ĸו×Ē OAuth, בדוק א×Ē ×›×Ēוב×Ē ×”×Š×¨×Ē", "login_form_failed_get_oauth_server_disable": "×Ēכונ×Ē OAuth לא זמינה בשר×Ē ×–×”", "login_form_failed_login": "שגיאה בכניסה למ×ĸרכ×Ē, בדוק א×Ē ×›×Ēוב×Ē ×”×Š×¨×Ē, דוא\"ל וסיסמה", - "login_form_handshake_exception": "איר×ĸה חריג×Ē ×œ×—×™×Ļ×Ē ×™×“ ×ĸם השר×Ē. אפ׊ר ×Ēמיכה ב×Ē×ĸודה בח×Ēימה ×ĸ×Ļמי×Ē ×‘×”×’×“×¨×•×Ē ×× א×Ē/ה מ׊×Ēמ׊/×Ē ×‘×Ē×ĸודה בח×Ēימה ×ĸ×Ļמי×Ē", + "login_form_handshake_exception": "איר×ĸה חריגה ב×ĸ×Ē ×‘×™×Ļו×ĸ Handshake ×ĸם השר×Ē. אפ׊ר ×Ēמיכה ב×Ē×ĸודה בח×Ēימה ×ĸ×Ļמי×Ē ×‘×”×’×“×¨×•×Ē ×× א×Ē/ה מ׊×Ēמ׊/×Ē ×‘×Ē×ĸודה בח×Ēימה ×ĸ×Ļמי×Ē.", "login_form_password_hint": "סיסמה", "login_form_save_login": "הישאר/י מחובר/×Ē", - "login_form_server_empty": "הכנס כ×Ēוב×Ē ×Š×¨×Ē", - "login_form_server_error": "לא היה ני×Ēן לה×Ēחבר לשר×Ē", + "login_form_server_empty": "הכנס כ×Ēוב×Ē ×Š×¨×Ē.", + "login_form_server_error": "לא היה ני×Ēן לה×Ēחבר לשר×Ē.", "login_has_been_disabled": "הכניסה הושב×Ēה.", "login_password_changed_error": "היי×Ēה שגיאה ב×ĸדכון הסיסמה שלך", "login_password_changed_success": "סיסמה ×ĸודכנה בה×Ļלחה", @@ -1170,30 +1173,33 @@ "manage_your_devices": "ניהול המכשירים המחוברים שלך", "manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך", "map": "מפה", - "map_assets_in_bound": "{} ×Ēמונה", - "map_assets_in_bounds": "{} ×Ēמונו×Ē", - "map_cannot_get_user_location": "לא ני×Ēן לקבל א×Ē ×ž×™×§×•× המש×Ēמ׊", + "map_assets_in_bound": "×Ēמונה {count}", + "map_assets_in_bounds": "{count} ×Ēמונו×Ē", + "map_cannot_get_user_location": "לא ני×Ēן לקבו×ĸ א×Ē ×ž×™×§×•× המש×Ēמ׊", "map_location_dialog_yes": "כן", "map_location_picker_page_use_location": "הש×Ēמ׊ במיקום הזה", - "map_location_service_disabled_content": "שירו×Ē ×ž×™×§×•× ×Ļריך להיו×Ē ×ž×•×¤×ĸל כדי לה×Ļיג נכסים מהמיקום הנוכחי שלך. האם בר×Ļונך להפ×ĸיל או×Ēו ×ĸכשיו?", + "map_location_service_disabled_content": "שירו×Ē ×”×ž×™×§×•× ×Ļריך להיו×Ē ×ž×•×¤×ĸל כדי לה×Ļיג ×Ēמונו×Ē ×ž×”×ž×™×§×•× הנוכחי שלך. האם בר×Ļונך להפ×ĸיל או×Ēו ×ĸכשיו?", "map_location_service_disabled_title": "שירו×Ē ×ž×™×§×•× מבוטל", "map_marker_for_images": "סמן מפה ל×Ēמונו×Ē ×Š×Ļולמו ב{city}, {country}", "map_marker_with_image": "סמן מפה ×ĸם ×Ēמונה", "map_no_assets_in_bounds": "אין ×Ēמונו×Ē ×‘××–×•×¨ זה", - "map_no_location_permission_content": "יש ×Ļורך בהרשאה למיקום כדי לה×Ļיג נכסים מהמיקום הנוכחי שלך. האם בר×Ļונך לאפשר זא×Ē ×ĸכשיו?", + "map_no_location_permission_content": "יש ×Ļורך בהרשאה למיקום כדי לה×Ļיג ×Ēמונו×Ē ×ž×”×ž×™×§×•× הנוכחי שלך. האם בר×Ļונך לאפשר זא×Ē ×ĸכשיו?", "map_no_location_permission_title": "הרשאה למיקום נדח×Ēה", "map_settings": "הגדרו×Ē ×ž×¤×”", "map_settings_dark_mode": "מ×Ļב כהה", "map_settings_date_range_option_day": "24 ׊×ĸו×Ē ××—×¨×•× ×•×Ē", - "map_settings_date_range_option_days": "{} ימים אחרונים", + "map_settings_date_range_option_days": "ב-{days} ימים אחרונים", "map_settings_date_range_option_year": "שנה אחרונה", - "map_settings_date_range_option_years": "{} שנים אחרונו×Ē", + "map_settings_date_range_option_years": "ב-{years} שנים אחרונו×Ē", "map_settings_dialog_title": "הגדרו×Ē ×ž×¤×”", "map_settings_include_show_archived": "כלול ארכיון", "map_settings_include_show_partners": "כלול שו×Ēפים", "map_settings_only_show_favorites": "ה×Ļג מו×ĸדפים בלבד", "map_settings_theme_settings": "×ĸרכ×Ē × ×•×Š× למפה", "map_zoom_to_see_photos": "הקטן א×Ē ×”×Ē×Ļוגה כדי לראו×Ē ×Ēמונו×Ē", + "mark_all_as_read": "סמן הכל כנקרא", + "mark_as_read": "סמן כנקרא", + "marked_all_as_read": "כל הה×Ēראו×Ē ×Ą×•×ž× ×• כנקראו", "matches": "ה×Ēאמו×Ē", "media_type": "סוג מדיה", "memories": "זכרונו×Ē", @@ -1202,8 +1208,6 @@ "memories_setting_description": "נהל א×Ē ×ž×” שרואים בזכרונו×Ē ×Š×œ×š", "memories_start_over": "ה×Ēחל מחדש", "memories_swipe_to_close": "החלק למ×ĸלה כדי לסגור", - "memories_year_ago": "לפני שנה", - "memories_years_ago": "לפני {} שנים", "memory": "זיכרון", "memory_lane_title": "מ׊×ĸול הזיכרונו×Ē {title}", "menu": "×Ēפריט", @@ -1218,11 +1222,10 @@ "missing": "חסרים", "model": "דגם", "month": "חודש", - "monthly_title_text_date_format": "MMMM y", "more": "×ĸוד", "moved_to_trash": "הו×ĸבר לאשפה", - "multiselect_grid_edit_date_time_err_read_only": "לא ני×Ēן ל×ĸרוך ×Ēאריך של נכס(ים) לקריאה בלבד, מדלג", - "multiselect_grid_edit_gps_err_read_only": "לא ני×Ēן ל×ĸרוך מיקום של נכס(ים) לקריאה בלבד, מדלג", + "multiselect_grid_edit_date_time_err_read_only": "לא ני×Ēן ל×ĸרוך ×Ēאריך של ×Ēמונו×Ē ×œ×§×¨×™××” בלבד, מדלג", + "multiselect_grid_edit_gps_err_read_only": "לא ני×Ēן ל×ĸרוך מיקום של ×Ēמונו×Ē ×œ×§×¨×™××” בלבד, מדלג", "mute_memories": "הש×Ē×§×Ē ×–×™×›×¨×•× ×•×Ē", "my_albums": "האלבומים שלי", "name": "׊ם", @@ -1234,6 +1237,7 @@ "new_api_key": "מפ×Ēח API חדש", "new_password": "סיסמה חדשה", "new_person": "אדם חדש", + "new_pin_code": "קוד PIN חדש", "new_user_created": "מ׊×Ēמ׊ חדש נו×Ļר", "new_version_available": "גרסה חדשה זמינה", "newest_first": "החדש ביו×Ēר ראשון", @@ -1245,30 +1249,31 @@ "no_albums_yet": "זה נראה שאין לך ×ĸדיין אלבומים.", "no_archived_assets_message": "ה×ĸבר ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× לארכיון כדי להס×Ēיר או×Ēם מ×Ē×Ļוג×Ē ×”×Ēמונו×Ē ×Š×œ×š", "no_assets_message": "לח×Ĩ כדי לה×ĸלו×Ē ××Ē ×”×Ēמונה הראשונה שלך", - "no_assets_to_show": "אין נכסים לה×Ļיג", + "no_assets_to_show": "אין ×Ēמונו×Ē ×œ×”×Ļגה", "no_duplicates_found": "לא נמ×Ļאו כפילויו×Ē.", "no_exif_info_available": "אין מיד×ĸ זמין ×ĸל מטא-× ×Ēונים (exif)", "no_explore_results_message": "ה×ĸלה ×Ēמונו×Ē × ×•×Ą×¤×•×Ē ×›×“×™ לחקור א×Ē ×”××•×Ą×Ŗ שלך.", "no_favorites_message": "×”×•×Ą×Ŗ מו×ĸדפים כדי למ×Ļוא במהירו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× הכי טובים שלך", "no_libraries_message": "×Ļור ספרייה חי×Ļוני×Ē ×›×“×™ לראו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שלך", "no_name": "אין ׊ם", + "no_notifications": "אין ה×Ēראו×Ē", + "no_people_found": "לא נמ×Ļאו אנשים ×Ēואמים", "no_places": "אין מקומו×Ē", "no_results": "אין ×Ēו×Ļאו×Ē", "no_results_description": "נסה להש×Ēמ׊ במילה נרדפ×Ē ××• במיל×Ē ×ž×¤×Ēח יו×Ēר כללי×Ē", "no_shared_albums_message": "×Ļור אלבום כדי לש×Ē×Ŗ ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ×ĸם אנשים ברש×Ē ×Š×œ×š", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", - "note_apply_storage_label_to_previously_uploaded assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל נכסים שהו×ĸלו ב×ĸבר, הפ×ĸל א×Ē", + "note_apply_storage_label_to_previously_uploaded assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē", "notes": "ה×ĸרו×Ē", - "notification_permission_dialog_content": "כדי לאפשר ה×Ēראו×Ē, לך להגדרו×Ē ×•×‘×—×¨ ה×Ēר", - "notification_permission_list_tile_content": "ה×ĸ× ×§ הרשאה כדי לאפשר ה×Ēראו×Ē", + "notification_permission_dialog_content": "כדי לאפשר ה×Ēראו×Ē, לך להגדרו×Ē ×”×ž×›×Š×™×¨ ובחר אפ׊ר.", + "notification_permission_list_tile_content": "ה×ĸ× ×§ הרשאה כדי לאפשר ה×Ēראו×Ē.", "notification_permission_list_tile_enable_button": "אפ׊ר ה×Ēראו×Ē", "notification_permission_list_tile_title": "הרשא×Ē ×”×Ēראה", "notification_toggle_setting_description": "אפ׊ר ה×Ēראו×Ē ×“×•×\"ל", "notifications": "ה×Ēראו×Ē", "notifications_setting_description": "ניהול ה×Ēראו×Ē", - "oauth": "OAuth", - "official_immich_resources": "משאבי Immich רשמיים", + "official_immich_resources": "מקורו×Ē ×¨×Š×ž×™×™× של Immich", "offline": "לא מקוון", "offline_paths": "× ×Ēיבים לא מקוונים", "offline_paths_description": "×Ēו×Ļאו×Ē ××œ×• ×ĸשויו×Ē ×œ×”×™×•×Ē ×ĸקב מחיקה ידני×Ē ×Š×œ קב×Ļים שאינם חלק מספרייה חי×Ļוני×Ē.", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "ברוך בואך, {user}", "online": "מקוון", "only_favorites": "רק מו×ĸדפים", + "open": "פ×Ēח", "open_in_map_view": "פ×Ēח ב×Ē×Ļוג×Ē ×ž×¤×”", "open_in_openstreetmap": "פ×Ēח ב-OpenStreetMap", "open_the_search_filters": "פ×Ēח א×Ē ×ž×Ą× × ×™ החיפוש", @@ -1300,12 +1306,12 @@ "partner_can_access_location": "המיקום שבו ×Ļולמו ה×Ēמונו×Ē ×Š×œ×š", "partner_list_user_photos": "×Ēמונו×Ē ×Š×œ {user}", "partner_list_view_all": "ה×Ļג הכל", - "partner_page_empty_message": "ה×Ēמונו×Ē ×Š×œ×š ×ĸדיין לא משו×Ēפו×Ē ×ĸם את שו×Ē×Ŗ", + "partner_page_empty_message": "ה×Ēמונו×Ē ×Š×œ×š ×ĸדיין לא משו×Ēפו×Ē ×ĸם את שו×Ē×Ŗ.", "partner_page_no_more_users": "אין ×ĸוד מ׊×Ēמשים ×œ×”×•×Ą×™×Ŗ", "partner_page_partner_add_failed": "הוספ×Ē ×Š×•×Ē×Ŗ נכשלה", "partner_page_select_partner": "בחיר×Ē ×Š×•×Ē×Ŗ", "partner_page_shared_to_title": "משו×Ē×Ŗ ×ĸם", - "partner_page_stop_sharing_content": "{} לא יוכל יו×Ēר לגש×Ē ×œ×Ēמונו×Ē ×Š×œ×š", + "partner_page_stop_sharing_content": "{partner} לא יוכל יו×Ēר לגש×Ē ×œ×Ēמונו×Ē ×Š×œ×š.", "partner_sharing": "שי×Ēות שו×Ēפים", "partners": "שו×Ēפים", "password": "סיסמה", @@ -1328,20 +1334,20 @@ "people_feature_description": "×ĸיון ב×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× שקוב×Ļו ×ĸל ידי אנשים", "people_sidebar_description": "ה×Ļג קישור אל אנשים בסרגל ה×Ļד", "permanent_deletion_warning": "אזהר×Ē ×ž×—×™×§×” ל×Ļמי×Ēו×Ē", - "permanent_deletion_warning_setting_description": "ה×Ļג אזהרה ב×ĸ×Ē ×ž×—×™×§×Ē × ×›×Ą×™× ל×Ļמי×Ēו×Ē", + "permanent_deletion_warning_setting_description": "ה×Ļג אזהרה ב×ĸ×Ē ×ž×—×™×§×Ē ×Ēמונו×Ē ×œ×Ļמי×Ēו×Ē", "permanently_delete": "מחק ל×Ļמי×Ēו×Ē", - "permanently_delete_assets_count": "מחק ל×Ļמי×Ēו×Ē {count, plural, one {נכס} other {נכסים}}", - "permanently_delete_assets_prompt": "האם באמ×Ē ×‘×¨×Ļונך למחוק ל×Ļמי×Ēו×Ē {count, plural, one {נכס זה?} other {# נכסים אלה?}}זה גם יסיר {count, plural, one {או×Ēו מאלבומו} other {או×Ēם מאלבומם}}.", - "permanently_deleted_asset": "נכס נמחק ל×Ļמי×Ēו×Ē", - "permanently_deleted_assets_count": "{count, plural, one {נכס # נמחק} other {# נכסים נמחקו}} ל×Ļמי×Ēו×Ē", + "permanently_delete_assets_count": "מחק ל×Ļמי×Ēו×Ē {count, plural, one {×Ēמונה} other {×Ēמונו×Ē}}", + "permanently_delete_assets_prompt": "האם באמ×Ē ×‘×¨×Ļונך למחוק ל×Ļמי×Ēו×Ē {count, plural, one {×Ēמונה זא×Ē?} other {# ×Ēמונו×Ē ××œ×•?}}זה גם יסיר {count, plural, one {או×Ēו מאלבומו} other {או×Ēם מאלבומים}}.", + "permanently_deleted_asset": "ה×Ēמונה נמחקה ל×Ļמי×Ēו×Ē", + "permanently_deleted_assets_count": "{count, plural, one {×Ēמונה # נמחקה} other {# ×Ēמונו×Ē × ×ž×—×§×•}} ל×Ļמי×Ēו×Ē", "permission_onboarding_back": "חזרה", "permission_onboarding_continue_anyway": "המשך בכל זא×Ē", "permission_onboarding_get_started": "לה×Ēחיל", "permission_onboarding_go_to_settings": "לך להגדרו×Ē", - "permission_onboarding_permission_denied": "הרשאה נדח×Ēה. כדי להש×Ēמ׊ ביישום, ה×ĸ× ×§ הרשאה ל×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× בהגדרו×Ē", - "permission_onboarding_permission_granted": "ההרשאה ני×Ēנה! א×Ē/ה מוכנ/ה", - "permission_onboarding_permission_limited": "הרשאה מוגבל×Ē. כדי ל×Ē×Ē ×œ×™×™×Š×•× לגבו×Ē ×•×œ× ×”×œ א×Ē ×›×œ ××•×Ą×Ŗ הגלריה שלך, ה×ĸ× ×§ הרשאה ל×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× בהגדרו×Ē", - "permission_onboarding_request": "היישום דורש הרשאה כדי לראו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שלך", + "permission_onboarding_permission_denied": "הרשאה נדח×Ēה. כדי להש×Ēמ׊ ביישום, ה×ĸ× ×§ הרשאה ל×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× בהגדרו×Ē.", + "permission_onboarding_permission_granted": "ההרשאה ני×Ēנה! הכל מוכן.", + "permission_onboarding_permission_limited": "הרשאה מוגבל×Ē. כדי ל×Ē×Ē ×œ×™×™×Š×•× לגבו×Ē ×•×œ× ×”×œ א×Ē ×›×œ ××•×Ą×Ŗ הגלריה שלך, ה×ĸ× ×§ הרשאה ל×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× בהגדרו×Ē.", + "permission_onboarding_request": "היישום דורש הרשאה כדי לראו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שלך.", "person": "אדם", "person_birthdate": "נולד ב×Ēאריך {date}", "person_hidden": "{name}{hidden, select, true { (מוס×Ēר)} other {}}", @@ -1351,6 +1357,9 @@ "photos_count": "{count, plural, one {×Ēמונה {count, number}} other {{count, number} ×Ēמונו×Ē}}", "photos_from_previous_years": "×Ēמונו×Ē ×ž×Š× ×™× קודמו×Ē", "pick_a_location": "בחר מיקום", + "pin_code_changed_successfully": "קוד ה PIN שונה בה×Ļלחה", + "pin_code_reset_successfully": "קוד PIN אופס בה×Ļלחה", + "pin_code_setup_successfully": "קוד PIN הוגדר בה×Ļלחה", "place": "מקום", "places": "מקומו×Ē", "places_count": "{count, plural, one {מקום {count, number}} other {{count, number} מקומו×Ē}}", @@ -1369,12 +1378,11 @@ "primary": "ראשי", "privacy": "פרטיו×Ē", "profile_drawer_app_logs": "יומן", - "profile_drawer_client_out_of_date_major": "האפליק×Ļיה לנייד היא מיושנ×Ē. נא ל×ĸדכן לגרסה הראשי×Ē ×”××—×¨×•× ×”", - "profile_drawer_client_out_of_date_minor": "האפליק×Ļיה לנייד היא מיושנ×Ē. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”", - "profile_drawer_client_server_up_to_date": "הלקוח והשר×Ē ×”× מ×ĸודכנים", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה הראשי×Ē ×”××—×¨×•× ×”", - "profile_drawer_server_out_of_date_minor": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”", + "profile_drawer_client_out_of_date_major": "גרס×Ē ×”×™×™×Š×•× לנייד מיושנ×Ē. נא ל×ĸדכן לגרסה הראשי×Ē ×”××—×¨×•× ×”.", + "profile_drawer_client_out_of_date_minor": "גרס×Ē ×”×™×™×Š×•× לנייד מיושנ×Ē. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”.", + "profile_drawer_client_server_up_to_date": "היישום והשר×Ē ×ž×ĸודכנים", + "profile_drawer_server_out_of_date_major": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה הראשי×Ē ×”××—×¨×•× ×”.", + "profile_drawer_server_out_of_date_minor": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”.", "profile_image_of_user": "×Ēמונ×Ē ×¤×¨×•×¤×™×œ של {user}", "profile_picture_set": "×Ēמונ×Ē ×¤×¨×•×¤×™×œ נבחרה.", "public_album": "אלבום ×Ļיבורי", @@ -1418,14 +1426,16 @@ "reaction_options": "אפשרויו×Ē ×”×’×‘×”", "read_changelog": "קרא א×Ē ×™×•×ž×Ÿ השינויים", "reassign": "הק×Ļה מחדש", - "reassigned_assets_to_existing_person": "{count, plural, one {נכס # הוק×Ļה} other {# נכסים הוק×Ļו}} מחדש אל {name, select, null {אדם קיים} other {{name}}}", - "reassigned_assets_to_new_person": "{count, plural, one {נכס # הוק×Ļה} other {# נכסים הוק×Ļו}} מחדש לאדם חדש", - "reassing_hint": "הק×Ļה נכסים שנבחרו לאדם קיים", + "reassigned_assets_to_existing_person": "{count, plural, one {×Ēמונה # הוק×Ļ×Ēה} other {# ×Ēמונו×Ē ×”×•×§×Ļו}} מחדש אל {name, select, null {אדם קיים} other {{name}}}", + "reassigned_assets_to_new_person": "{count, plural, one {×Ēמונה # הוק×Ļ×Ēה} other {# ×Ēמונו×Ē ×”×•×§×Ļו}} מחדש לאדם חדש", + "reassing_hint": "הק×Ļה ×Ēמונו×Ē ×Š× ×‘×—×¨×• לאדם קיים", "recent": "חדש", "recent-albums": "אלבומים אחרונים", "recent_searches": "חיפושים אחרונים", "recently_added": "× ×•×Ą×Ŗ לאחרונה", "recently_added_page_title": "× ×•×Ą×Ŗ לאחרונה", + "recently_taken": "×Ļולם לאחרונה", + "recently_taken_page_title": "×Ļולם לאחרונה", "refresh": "ר×ĸנן", "refresh_encoded_videos": "ר×ĸנן סרטונים מקודדים", "refresh_faces": "ר×ĸנן פנים", @@ -1438,9 +1448,9 @@ "refreshing_metadata": "מר×ĸנן מטא-× ×Ēונים", "regenerating_thumbnails": "מחדש ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", "remove": "הסר", - "remove_assets_album_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר {count, plural, one {נכס #} other {# נכסים}} מהאלבום?", - "remove_assets_shared_link_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר {count, plural, one {נכס #} other {# נכסים}} מהקישור המשו×Ē×Ŗ הזה?", - "remove_assets_title": "הסר נכסים?", + "remove_assets_album_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}} מהאלבום?", + "remove_assets_shared_link_confirmation": "האם א×Ēה בטוח שבר×Ļונך להסיר {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}} מהקישור המשו×Ē×Ŗ הזה?", + "remove_assets_title": "להסיר ×Ēמונו×Ē?", "remove_custom_date_range": "הסר טווח ×Ēאריכים מו×Ēאם", "remove_deleted_assets": "הסר קב×Ļים לא מקוונים", "remove_from_album": "הסר מאלבום", @@ -1456,7 +1466,7 @@ "removed_from_favorites_count": "{count, plural, other {הוסרו #}} מהמו×ĸדפים", "removed_memory": "זיכרון הוסר", "removed_photo_from_memory": "ה×Ēמונה הוסרה מהזיכרון", - "removed_tagged_assets": "×Ēג הוסר מ{count, plural, one {נכס #} other {# נכסים}}", + "removed_tagged_assets": "×Ēג הוסר מ{count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "rename": "שנה ׊ם", "repair": "×Ēיקון", "repair_no_results_message": "קב×Ļים חסרי מ×ĸקב וחסרים יופי×ĸו כאן", @@ -1468,13 +1478,14 @@ "reset": "איפוס", "reset_password": "איפוס סיסמה", "reset_people_visibility": "אפץ א×Ē × ×¨××•×Ē ×”×× ×Š×™×", + "reset_pin_code": "אפץ קוד PIN", "reset_to_default": "אפץ לבריר×Ē ×ž×—×“×œ", "resolve_duplicates": "פ×Ēור כפילויו×Ē", "resolved_all_duplicates": "כל הכפילויו×Ē × ×¤×Ēרו", "restore": "שחזר", "restore_all": "שחזר הכל", "restore_user": "שחזר מ׊×Ēמ׊", - "restored_asset": "נכס משוחזר", + "restored_asset": "ה×Ēמונה שוחזרה", "resume": "המשך", "retry_upload": "נסה שוב לה×ĸלו×Ē", "review_duplicates": "בדוק כפילויו×Ē", @@ -1540,7 +1551,7 @@ "search_result_page_new_search_hint": "חיפוש חדש", "search_settings": "הגדרו×Ē ×—×™×¤×•×Š", "search_state": "חיפוש מדינה...", - "search_suggestion_list_smart_search_hint_1": "חיפוש חכם מופ×ĸל כבריר×Ē ×ž×—×“×œ, כדי לחפש מטא-× ×Ēונים הש×Ēמ׊ ב×Ēחביר", + "search_suggestion_list_smart_search_hint_1": "חיפוש חכם מופ×ĸל כבריר×Ē ×ž×—×“×œ, כדי לחפש מטא-× ×Ēונים הש×Ēמ׊ ב×Ēחביר ", "search_suggestion_list_smart_search_hint_2": "×Ēנאי-החיפוש-שלך:m", "search_tags": "חיפוש ×Ēגים...", "search_timezone": "חיפוש אזור זמן...", @@ -1560,6 +1571,7 @@ "select_keep_all": "בחר שמור הכל", "select_library_owner": "בחר א×Ē ×”×‘×ĸלים של הספרייה", "select_new_face": "בחר פנים חדשו×Ē", + "select_person_to_tag": "בחר אדם ל×Ēיוג", "select_photos": "בחר ×Ēמונו×Ē", "select_trash_all": "בחר ה×ĸבר הכל לאשפה", "select_user_for_sharing_page_err_album": "י×Ļיר×Ē ××œ×‘×•× נכשלה", @@ -1570,8 +1582,8 @@ "server_endpoint": "נקוד×Ē ×§×Ļה ׊ר×Ē", "server_info_box_app_version": "גרס×Ē ×™×™×Š×•×", "server_info_box_server_url": "כ×Ēוב×Ē ×Š×¨×Ē", - "server_offline": "׊ר×Ē ×œ× מקוון", - "server_online": "׊ר×Ē ×ž×§×•×•×Ÿ", + "server_offline": "השר×Ē ×ž× ×•×Ē×§", + "server_online": "החיבור לשר×Ē ×¤×ĸיל", "server_stats": "סטטיסטיקו×Ē ×Š×¨×Ē", "server_version": "גרס×Ē ×Š×¨×Ē", "set": "הגדר", @@ -1581,25 +1593,25 @@ "set_date_of_birth": "הגדר ×Ēאריך לידה", "set_profile_picture": "הגדר ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", "set_slideshow_to_fullscreen": "הגדר מ×Ļג×Ē ×Š×§×•×¤×™×•×Ē ×œ×ž×Ą×š מלא", - "setting_image_viewer_help": "מ×Ļיג הפרטים טו×ĸן א×Ē ×”×Ēמונה הממוז×ĸר×Ē ×”×§×˜× ×” קודם, לאחר מכן טו×ĸן א×Ē ×”×Ē×Ļוגה המקדימה בגודל בינוני (אם מופ×ĸל×Ē), ×œ×‘×Ą×•×Ŗ טו×ĸן א×Ē ×”×ž×§×•×¨×™×Ē (אם מופ×ĸל×Ē)", - "setting_image_viewer_original_subtitle": "אפ׊ר לט×ĸון א×Ē ×”×Ēמונה המקורי×Ē ×‘×¨×–×œ×•×Ļיה מלאה (גדולה!). השב×Ē ×›×“×™ להקטין שימוש בנ×Ēונים (גם בשר×Ē ×•×’× בזיכרון המטמון שבמכשיר)", + "setting_image_viewer_help": "מ×Ļיג הפרטים טו×ĸן א×Ē ×”×Ēמונה הממוז×ĸר×Ē ×”×§×˜× ×” קודם, לאחר מכן טו×ĸן א×Ē ×”×Ē×Ļוגה המקדימה בגודל בינוני (אם מופ×ĸל), ×œ×‘×Ą×•×Ŗ טו×ĸן א×Ē ×”×ž×§×•×¨×™×Ē (אם מופ×ĸל).", + "setting_image_viewer_original_subtitle": "אפ׊ר לט×ĸון א×Ē ×”×Ēמונה המקורי×Ē ×‘×¨×–×œ×•×Ļיה מלאה (גדולה!). השב×Ē ×›×“×™ להקטין שימוש בנ×Ēונים (גם בשר×Ē ×•×’× בזיכרון המטמון שבמכשיר).", "setting_image_viewer_original_title": "ט×ĸן ×Ēמונה מקורי×Ē", - "setting_image_viewer_preview_subtitle": "אפ׊ר לט×ĸון ×Ēמונה ברזלו×Ļיה בינוני×Ē. השב×Ē ×›×“×™ או לט×ĸון א×Ē ×”×ž×§×•×¨×™×Ē ××• רק להש×Ēמ׊ ב×Ēמונה הממוז×ĸר×Ē", + "setting_image_viewer_preview_subtitle": "אפ׊ר לט×ĸון ×Ēמונה ברזלו×Ļיה בינוני×Ē. השב×Ē ×›×“×™ או לט×ĸון א×Ē ×”×ž×§×•×¨×™×Ē ××• רק להש×Ēמ׊ ב×Ēמונה הממוז×ĸר×Ē.", "setting_image_viewer_preview_title": "ט×ĸן ×Ēמונ×Ē ×Ē×Ļוגה מקדימה", "setting_image_viewer_title": "×Ēמונו×Ē", "setting_languages_apply": "החל", "setting_languages_subtitle": "שינוי ׊פ×Ē ×”×™×™×Š×•×", "setting_languages_title": "שפו×Ē", - "setting_notifications_notify_failures_grace_period": "הוד×ĸ ×ĸל כשלים בגיבוי ברק×ĸ: {}", - "setting_notifications_notify_hours": "{} ׊×ĸו×Ē", + "setting_notifications_notify_failures_grace_period": "הוד×ĸ ×ĸל כשלים בגיבוי ברק×ĸ: {duration}", + "setting_notifications_notify_hours": "{count} ׊×ĸו×Ē", "setting_notifications_notify_immediately": "באופן מיידי", - "setting_notifications_notify_minutes": "{} דקו×Ē", + "setting_notifications_notify_minutes": "{count} דקו×Ē", "setting_notifications_notify_never": "את פ×ĸם", - "setting_notifications_notify_seconds": "{} שניו×Ē", - "setting_notifications_single_progress_subtitle": "מיד×ĸ מפורט ×ĸל ה×Ēקדמו×Ē ×”×ĸלאה לכל נכס", + "setting_notifications_notify_seconds": "{count} שניו×Ē", + "setting_notifications_single_progress_subtitle": "מיד×ĸ מפורט ×ĸל ה×Ēקדמו×Ē ×”×ĸלאה לכל ×Ēמונה", "setting_notifications_single_progress_title": "הראה פרטי ה×Ēקדמו×Ē ×’×™×‘×•×™ ברק×ĸ", "setting_notifications_subtitle": "ה×Ēאם א×Ē ×”×ĸדפו×Ē ×”×”×Ēראה שלך", - "setting_notifications_total_progress_subtitle": "ה×Ēקדמו×Ē ×”×ĸלאה כללי×Ē (בו×Ļ×ĸ/סה״כ נכסים)", + "setting_notifications_total_progress_subtitle": "ה×Ēקדמו×Ē ×”×ĸלאה כללי×Ē (בו×Ļ×ĸ/סה״כ ×Ēמונו×Ē)", "setting_notifications_total_progress_title": "הראה סה״כ ה×Ēקדמו×Ē ×’×™×‘×•×™ ברק×ĸ", "setting_video_viewer_looping_title": "הפ×ĸלה חוזר×Ē", "setting_video_viewer_original_video_subtitle": "כאשר מזרימים סרטון מהשר×Ē, נגן א×Ē ×”×ž×§×•×¨×™ אפילו כשהמר×Ē ×§×™×“×•×“ זמינה. ×ĸלול להוביל ל×Ēקי×ĸו×Ē. סרטונים זמינים מקומי×Ē ×ž× ×•×’× ×™× באיכו×Ē ×ž×§×•×¨×™×Ē ×œ×œ× ק׊ר להגדרה זו.", @@ -1607,9 +1619,10 @@ "settings": "הגדרו×Ē", "settings_require_restart": "אנא הפ×ĸל מחדש א×Ē ×”×™×™×Š×•× כדי להחיל הגדרה זו", "settings_saved": "ההגדרו×Ē × ×Š×ž×¨×•", + "setup_pin_code": "הגדר קוד PIN", "share": "׊×Ē×Ŗ", "share_add_photos": "×”×•×Ą×Ŗ ×Ēמונו×Ē", - "share_assets_selected": "{} נבחרו", + "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", "shared": "משו×Ē×Ŗ", "shared_album_activities_input_disable": "ה×Ēגובה מושב×Ē×Ē", @@ -1623,34 +1636,33 @@ "shared_by_user": "משו×Ē×Ŗ ×ĸל ידי {user}", "shared_by_you": "משו×Ē×Ŗ ×ĸל ידך", "shared_from_partner": "×Ēמונו×Ē ×ž××Ē {partner}", - "shared_intent_upload_button_progress_text": "הו×ĸלו {} / {}", + "shared_intent_upload_button_progress_text": "{total} / {current} הו×ĸלו", "shared_link_app_bar_title": "קישורים משו×Ēפים", "shared_link_clipboard_copied_massage": "הו×ĸ×Ē×§ ללוח", - "shared_link_clipboard_text": "קישור: {}\nסיסמה: {}", + "shared_link_clipboard_text": "קישור: {password}\nסיסמה: {link}", "shared_link_create_error": "שגיאה בי×Ļיר×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ", "shared_link_edit_description_hint": "הכנס א×Ē ×Ēיאור השי×Ēות", "shared_link_edit_expire_after_option_day": "1 יום", - "shared_link_edit_expire_after_option_days": "{} ימים", + "shared_link_edit_expire_after_option_days": "{count} ימים", "shared_link_edit_expire_after_option_hour": "1 ׊×ĸה", - "shared_link_edit_expire_after_option_hours": "{} ׊×ĸו×Ē", + "shared_link_edit_expire_after_option_hours": "{count} ׊×ĸו×Ē", "shared_link_edit_expire_after_option_minute": "1 דקה", - "shared_link_edit_expire_after_option_minutes": "{} דקו×Ē", - "shared_link_edit_expire_after_option_months": "{} חודשים", - "shared_link_edit_expire_after_option_year": "{} שנה", + "shared_link_edit_expire_after_option_minutes": "{count} דקו×Ē", + "shared_link_edit_expire_after_option_months": "{count} חודשים", + "shared_link_edit_expire_after_option_year": "{count} שנה", "shared_link_edit_password_hint": "הכנס א×Ē ×Ą×™×Ą×ž×Ē ×”×Š×™×Ēות", "shared_link_edit_submit_button": "×ĸדכן קישור", "shared_link_error_server_url_fetch": "לא ני×Ēן להשיג א×Ē ×›×Ēוב×Ē ×”××™× ×˜×¨× ×˜ של השר×Ē", - "shared_link_expires_day": "יפוג ב×ĸוד {} יום", - "shared_link_expires_days": "יפוג ב×ĸוד {} ימים", - "shared_link_expires_hour": "יפוג ב×ĸוד {} ׊×ĸה", - "shared_link_expires_hours": "יפוג ב×ĸוד {} ׊×ĸו×Ē", - "shared_link_expires_minute": "יפוג ב×ĸוד {} דקה", - "shared_link_expires_minutes": "יפוג ב×ĸוד {} דקו×Ē", + "shared_link_expires_day": "יפוג ב×ĸוד יום {count}", + "shared_link_expires_days": "יפוג ב×ĸוד {count} ימים", + "shared_link_expires_hour": "יפוג ב×ĸוד ׊×ĸה {count}", + "shared_link_expires_hours": "יפוג ב×ĸוד {count} ׊×ĸו×Ē", + "shared_link_expires_minute": "יפוג ב×ĸוד דקה {count}", + "shared_link_expires_minutes": "יפוג ב×ĸוד {count} דקו×Ē", "shared_link_expires_never": "יפוג ∞", - "shared_link_expires_second": "יפוג ב×ĸוד {} שניה", - "shared_link_expires_seconds": "יפוג ב×ĸוד {} שניו×Ē", + "shared_link_expires_second": "יפוג ב×ĸוד שנייה {count}", + "shared_link_expires_seconds": "יפוג ב×ĸוד {count} שניו×Ē", "shared_link_individual_shared": "משו×Ē×Ŗ ליחיד", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ניהול קישורים משו×Ēפים", "shared_link_options": "אפשרויו×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ", "shared_links": "קישורים משו×Ēפים", @@ -1661,12 +1673,12 @@ "sharing": "שי×Ēות", "sharing_enter_password": "נא להזין א×Ē ×”×Ą×™×Ą×ž×” כדי ל×Ļפו×Ē ×‘×“×Ŗ זה.", "sharing_page_album": "אלבומים משו×Ēפים", - "sharing_page_description": "×Ļור אלבומים משו×Ēפים כדי לש×Ē×Ŗ ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ×ĸם אנשים ברש×Ē ×Š×œ×š", + "sharing_page_description": "×Ļור אלבומים משו×Ēפים כדי לש×Ē×Ŗ ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ×ĸם אנשים ברש×Ē ×Š×œ×š.", "sharing_page_empty_list": "רשימה ריקה", "sharing_sidebar_description": "ה×Ļג קישור אל שי×Ēות בסרגל ה×Ļד", "sharing_silver_appbar_create_shared_album": "אלבום משו×Ē×Ŗ חדש", "sharing_silver_appbar_share_partner": "שי×Ēות ×ĸם שו×Ē×Ŗ", - "shift_to_permanent_delete": "לח×Ĩ ⇧ כדי למחוק ל×Ļמי×Ēו×Ē × ×›×Ą", + "shift_to_permanent_delete": "לח×Ĩ ⇧ כדי למחוק ×Ēמונה ל×Ļמי×Ēו×Ē", "show_album_options": "ה×Ļג אפשרויו×Ē ××œ×‘×•×", "show_albums": "ה×Ļג אלבומים", "show_all_people": "ה×Ļג א×Ē ×›×œ האנשים", @@ -1706,12 +1718,12 @@ "sort_people_by_similarity": "מיין אנשים לפי דמיון", "sort_recent": "×Ēמונה אחרונה ביו×Ēר", "sort_title": "כו×Ēר×Ē", - "source": "מקור", + "source": "קוד מקור", "stack": "×ĸרימה", "stack_duplicates": "×Ļור ×ĸרימ×Ē ×›×¤×™×œ×•×™×•×Ē", "stack_select_one_photo": "בחר ×Ēמונה ראשי×Ē ××—×Ē ×ĸבור ה×ĸרימה", "stack_selected_photos": "×Ļור ×ĸרימ×Ē ×Ēמונו×Ē × ×‘×—×¨×•×Ē", - "stacked_assets_count": "{count, plural, one {נכס # × ×ĸרם} other {# נכסים × ×ĸרמו}}", + "stacked_assets_count": "{count, plural, one {×Ēמונה # × ×ĸרמה} other {# ×Ēמונו×Ē × ×ĸרמו}}", "stacktrace": "Stack trace", "start": "ה×Ēחל", "start_date": "×Ēאריך ה×Ēחלה", @@ -1736,25 +1748,25 @@ "sync_albums_manual_subtitle": "סנכרן א×Ē ×›×œ הסרטונים וה×Ēמונו×Ē ×Š×”×•×ĸלו לאלבומי הגיבוי שנבחרו", "sync_upload_album_setting_subtitle": "×Ļור וה×ĸלה ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× שלך לאלבומים שנבחרו ביישום", "tag": "×Ēג", - "tag_assets": "×Ēיוג נכסים", + "tag_assets": "×Ēיוג ×Ēמונו×Ē", "tag_created": "נו×Ļר ×Ēג: {tag}", "tag_feature_description": "×ĸיון ב×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× שקוב×Ļו ×ĸל ידי נושאי ×Ēג לוגיים", "tag_not_found_question": "לא מ×Ļליח למ×Ļוא ×Ēג? ×Ļור ×Ēג חדש", "tag_people": "×Ēייג אנשים", "tag_updated": "×Ēג מ×ĸודכן: {tag}", - "tagged_assets": "×Ēויגו {count, plural, one {נכס #} other {# נכסים}}", + "tagged_assets": "×Ēויגו {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "tags": "×Ēגים", "template": "×Ēבני×Ē", "theme": "×ĸרכ×Ē × ×•×Š×", "theme_selection": "בחיר×Ē ×ĸרכ×Ē × ×•×Š×", "theme_selection_description": "הגדר אוטומטי×Ē ××Ē ×ĸרכ×Ē ×”× ×•×Š× לבהיר או כהה בה×Ēבסס ×ĸל ה×ĸדפ×Ē ×”×ž×ĸרכ×Ē ×Š×œ הדפדפן שלך", - "theme_setting_asset_list_storage_indicator_title": "הראה מחוון אחסון ×ĸל אריחי נכסים", - "theme_setting_asset_list_tiles_per_row_title": "מספר נכסים בכל שורה ({})", - "theme_setting_colorful_interface_subtitle": "החל א×Ē ×”×Ļב×ĸ ה×ĸיקרי למשטחי רק×ĸ", + "theme_setting_asset_list_storage_indicator_title": "ה×Ļג סטטוס גיבוי ×ĸל גבי ה×Ēמונו×Ē", + "theme_setting_asset_list_tiles_per_row_title": "מספר ×Ēמונו×Ē ×‘×›×œ שורה ({count})", + "theme_setting_colorful_interface_subtitle": "החל א×Ē ×”×Ļב×ĸ ה×ĸיקרי למשטחי רק×ĸ.", "theme_setting_colorful_interface_title": "ממ׊ק ×Ļב×ĸוני", "theme_setting_image_viewer_quality_subtitle": "ה×Ēאם א×Ē ×”××™×›×•×Ē ×Š×œ מ×Ļיג פרטי ה×Ēמונו×Ē", "theme_setting_image_viewer_quality_title": "איכו×Ē ×ž×Ļיג ×Ēמונו×Ē", - "theme_setting_primary_color_subtitle": "בחר ×Ļב×ĸ לפ×ĸולו×Ē ×ĸיקריו×Ē ×•×”×“×’×Š×•×Ē", + "theme_setting_primary_color_subtitle": "בחר ×Ļב×ĸ לפ×ĸולו×Ē ×ĸיקריו×Ē ×•×”×“×’×Š×•×Ē.", "theme_setting_primary_color_title": "×Ļב×ĸ ×ĸיקרי", "theme_setting_system_primary_color_title": "הש×Ēמ׊ ב×Ļב×ĸ המ×ĸרכ×Ē", "theme_setting_system_theme_switch": "אוטומטי (×ĸקוב אחרי הגדר×Ē ×ž×ĸרכ×Ē)", @@ -1779,18 +1791,20 @@ "trash": "אשפה", "trash_all": "ה×ĸבר הכל לאשפה", "trash_count": "ה×ĸבר לאשפה {count, number}", - "trash_delete_asset": "ה×ĸבר לאשפה/מחק נכס", + "trash_delete_asset": "ה×ĸבר לאשפה/מחק ×Ēמונה", "trash_emptied": "האשפה רוקנה", "trash_no_results_message": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× שהו×ĸברו לאשפה יופי×ĸו כאן.", "trash_page_delete_all": "מחק הכל", - "trash_page_empty_trash_dialog_content": "האם בר×Ļונך לרוקן א×Ē ×”× ×›×Ą×™× שבאשפה? הפריטים האלה ימחקו ל×Ļמי×Ēו×Ē ×ž×”×Š×¨×Ē", - "trash_page_info": "פריטים באשפה ימחקו ל×Ļמי×Ēו×Ē ×œ××—×¨ {} ימים", - "trash_page_no_assets": "אין נכסים באשפה", + "trash_page_empty_trash_dialog_content": "האם בר×Ļונך לרוקן א×Ē ×”×Ēמונו×Ē ×Š×‘××Š×¤×”? הפריטים האלה ימחקו ל×Ļמי×Ēו×Ē ×ž×”×Š×¨×Ē", + "trash_page_info": "פריטים באשפה ימחקו ל×Ļמי×Ēו×Ē ×œ××—×¨ {days} ימים", + "trash_page_no_assets": "אין ×Ēמונו×Ē ×‘××Š×¤×”", "trash_page_restore_all": "שחזר הכל", - "trash_page_select_assets_btn": "בחר נכסים", - "trash_page_title": "אשפה ({})", + "trash_page_select_assets_btn": "בחר ×Ēמונו×Ē", + "trash_page_title": "אשפה ({count})", "trashed_items_will_be_permanently_deleted_after": "פריטים באשפה ימחקו ל×Ļמי×Ēו×Ē ×œ××—×¨ {days, plural, one {יום #} other {# ימים}}.", "type": "סוג", + "unable_to_change_pin_code": "לא ני×Ēן לשנו×Ē ××Ē ×§×•×“ ה PIN", + "unable_to_setup_pin_code": "לא ני×Ēן להגדיר קוד PIN", "unarchive": "הו×Ļא מארכיון", "unarchived_count": "{count, plural, other {# הו×Ļאו מהארכיון}}", "unfavorite": "לא מו×ĸדת", @@ -1810,31 +1824,32 @@ "unselect_all": "בטל בחירה בהכל", "unselect_all_duplicates": "בטל בחיר×Ē ×›×œ הכפילויו×Ē", "unstack": "בטל ×ĸרימה", - "unstacked_assets_count": "{count, plural, one {נכס # הוסר} other {# נכסים הוסרו}} מ×ĸרימה", + "unstacked_assets_count": "{count, plural, one {×Ēמונה # הוסרה} other {# ×Ēמונו×Ē ×”×•×Ą×¨×•}} מה×ĸרימה", "untracked_files": "קב×Ļים ללא מ×ĸקב", "untracked_files_decription": "קב×Ļים אלה אינם נמ×Ļאים במ×ĸקב של היישום. הם יכולים להיו×Ē ×Ēו×Ļאו×Ē ×Š×œ ה×ĸברו×Ē ×›×•×Š×œ×•×Ē, ה×ĸלאו×Ē ×Š× ×§×˜×ĸו, או שנו×Ēרו מאחור בגלל שיבוש ב×Ēוכנה", "up_next": "הבא ב×Ēור", "updated_password": "סיסמה ×ĸודכנה", "upload": "ה×ĸלאה", "upload_concurrency": "בו-זמניו×Ē ×Š×œ ה×ĸלאה", - "upload_dialog_info": "האם בר×Ļונך לגבו×Ē ××Ē ×”× ×›×Ą(ים) שנבחרו לשר×Ē?", - "upload_dialog_title": "ה×ĸלא×Ē × ×›×Ą", - "upload_errors": "ה×ĸלאה הושלמה ×ĸם {count, plural, one {שגיאה #} other {# שגיאו×Ē}}, ר×ĸנן א×Ē ×”×“×Ŗ כדי לראו×Ē × ×›×Ą×™ ה×ĸלאה חדשים.", + "upload_dialog_info": "האם בר×Ļונך לגבו×Ē ××Ē ×”×Ēמונו×Ē ×Š× ×‘×—×¨×• לשר×Ē?", + "upload_dialog_title": "ה×ĸלא×Ē ×Ēמונה", + "upload_errors": "ה×ĸלאה הושלמה ×ĸם {count, plural, one {שגיאה #} other {# שגיאו×Ē}}, ר×ĸנן א×Ē ×”×“×Ŗ כדי לראו×Ē ×Ēמונו×Ē ×Š×”×•×ĸלו.", "upload_progress": "נו×Ēרו {remaining, number} - טופלו {processed, number}/{total, number}", - "upload_skipped_duplicates": "דילג ×ĸל {count, plural, one {נכס כפול #} other {# נכסים כפולים}}", + "upload_skipped_duplicates": "דילג ×ĸל {count, plural, one {×Ēמונה כפולה #} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}", "upload_status_duplicates": "כפילויו×Ē", "upload_status_errors": "שגיאו×Ē", "upload_status_uploaded": "הו×ĸלה", - "upload_success": "הה×ĸלאה ה×Ļליחה, ר×ĸנן א×Ē ×”×“×Ŗ כדי לראו×Ē × ×›×Ą×™ ה×ĸלאה חדשים.", - "upload_to_immich": "ה×ĸלה לשר×Ē ({})", + "upload_success": "הה×ĸלאה בו×Ļ×ĸה בה×Ļלחה. ר×ĸנן א×Ē ×”×“×Ŗ כדי ל×Ļפו×Ē ×‘×Ēמונו×Ē ×Š×”×•×ĸלו.", + "upload_to_immich": "ה×ĸלה לשר×Ē ({count})", "uploading": "מ×ĸלה", - "url": "URL", "usage": "שימוש", "use_current_connection": "הש×Ēמ׊ בחיבור נוכחי", "use_custom_date_range": "הש×Ēמ׊ בטווח ×Ēאריכים מו×Ēאם במקום", "user": "מ׊×Ēמ׊", "user_id": "מזהה מ׊×Ēמ׊", - "user_liked": "{user} אהב א×Ē {type, select, photo {ה×Ēמונה הזא×Ē} video {הסרטון הזה} asset {הנכס הזה} other {זה}}", + "user_liked": "{user} אהב א×Ē {type, select, photo {ה×Ēמונה הזא×Ē} video {הסרטון הזה} asset {ה×Ēמונה הזא×Ē} other {זה}}", + "user_pin_code_settings": "קוד PIN", + "user_pin_code_settings_description": "נהל א×Ē ×§×•×“ ה PIN שלך", "user_purchase_settings": "רכישה", "user_purchase_settings_description": "ניהול הרכישה שלך", "user_role_set": "הגדר א×Ē {user} ב×Ēור {role}", @@ -1853,7 +1868,7 @@ "version_announcement_overlay_release_notes": "ה×ĸרו×Ē ×¤×¨×Ą×•×", "version_announcement_overlay_text_1": "הי חבר/ה, יש מהדורה חדשה של", "version_announcement_overlay_text_2": "אנא קח/י א×Ē ×”×–×ž×Ÿ שלך לבקר ב ", - "version_announcement_overlay_text_3": " ולוודא שמבנה ה docker-compose וה env. שלך ×ĸדכני כדי למנו×ĸ ×Ē×Ļורו×Ē ×Š×’×•×™×•×Ē, במיוחד אם א×Ē/ה מ׊×Ēמ׊/×Ē ×‘ WatchTower או בכל מנגנון שמטפל ב×ĸדכון יישום השר×Ē ×Š×œ×š באופן אוטומטי", + "version_announcement_overlay_text_3": " ולוודא שמבנה ה docker-compose וה env. שלך ×ĸדכני כדי למנו×ĸ ×Ē×Ļורו×Ē ×Š×’×•×™×•×Ē, במיוחד אם א×Ēה מ׊×Ēמ׊ ב WatchTower או במנגנון שמטפל ב×ĸדכון השר×Ē ×‘××•×¤×Ÿ אוטומטי.", "version_announcement_overlay_title": "גרס×Ē ×Š×¨×Ē ×—×“×Š×” זמינה 🎉", "version_history": "היסטוריי×Ē ×’×¨×Ą××•×Ē", "version_history_item": "{version} הו×Ēקנה ב-{date}", @@ -1870,11 +1885,12 @@ "view_link": "ה×Ļג קישור", "view_links": "ה×Ļג קישורים", "view_name": "ה×Ļג", - "view_next_asset": "ה×Ļג א×Ē ×”× ×›×Ą הבא", - "view_previous_asset": "ה×Ļג א×Ē ×”× ×›×Ą הקודם", + "view_next_asset": "ה×Ļג א×Ē ×”×Ēמונה הבאה", + "view_previous_asset": "ה×Ļג א×Ē ×”×Ēמונה הקודמ×Ē", + "view_qr_code": "ה×Ļג ברקוד", "view_stack": "ה×Ļג ×ĸרימה", "viewer_remove_from_stack": "הסר מ×ĸרימה", - "viewer_stack_use_as_main_asset": "הש×Ēמ׊ כנכס ראשי", + "viewer_stack_use_as_main_asset": "הש×Ēמ׊ כ×Ēמונה ראשי×Ē", "viewer_unstack": "ביטול ×ĸרימה", "visibility_changed": "הנראו×Ē ×”×Š×Ē× ×Ēה ×ĸבור {count, plural, one {אדם #} other {# אנשים}}", "waiting": "ממ×Ēין", @@ -1882,7 +1898,7 @@ "week": "שבו×ĸ", "welcome": "ברוכים הבאים", "welcome_to_immich": "ברוכים הבאים אל immich", - "wifi_name": "׊ם אינטרנט אלחוטי", + "wifi_name": "׊ם הרש×Ē ×”××œ×—×•×˜×™×Ē", "year": "שנה", "years_ago": "לפני {years, plural, one {שנה #} other {# שנים}}", "yes": "כן", diff --git a/i18n/hi.json b/i18n/hi.json index 7f50906791..823380c24f 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -4,33 +4,33 @@ "account_settings": "⤅⤭ā¤ŋ⤞āĨ‡ā¤– ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", "acknowledge": "⤏āĨā¤ĩāĨ€ā¤•ā¤žā¤° ⤕⤰āĨ‡ā¤‚", "action": "ā¤•ā¤žā¤°āĨā¤°ā¤ĩā¤žā¤ˆ", - "action_common_update": "Update", "actions": "ā¤•ā¤žā¤°āĨā¤¯ā¤ĩā¤žā¤šā¤ŋā¤¯ā¤žā¤‚", "active": "⤏⤕āĨā¤°ā¤ŋ⤝", "activity": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ", "activity_changed": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ {enabled, select, true {enabled} other {disabled}}", - "add": "⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_a_description": "ā¤ā¤• ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_a_location": "ā¤ā¤• ⤏āĨā¤Ĩā¤žā¤¨ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_a_name": "ā¤¨ā¤žā¤Ž ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_a_title": "ā¤ā¤• ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_endpoint": "Add endpoint", - "add_exclusion_pattern": "⤅ā¤Ēā¤ĩā¤žā¤Ļ ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_more_users": "⤅⤧ā¤ŋ⤕ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_partner": "⤜āĨ‹ā¤Ąā¤ŧāĨ€ā¤Ļā¤žā¤° ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_path": "ā¤Ēā¤Ĩ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_to": "ā¤‡ā¤¸ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚â€Ļ", - "add_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", - "add_to_shared_album": "ā¤¸ā¤žā¤ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", - "add_url": "URL ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", + "add": "ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_a_description": "ā¤ā¤• ā¤ĩā¤ŋā¤ĩ⤰⤪ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_a_location": "ā¤ā¤• ⤏āĨā¤Ĩā¤žā¤¨ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_a_name": "ā¤¨ā¤žā¤Ž ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_a_title": "ā¤ā¤• ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_endpoint": "endpoint ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_exclusion_pattern": "⤅ā¤Ēā¤ĩā¤žā¤Ļ ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_more_users": "⤅⤧ā¤ŋ⤕ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_partner": "⤜āĨ‹ā¤Ąā¤ŧāĨ€ā¤Ļā¤žā¤° ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_path": "ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_to": "ā¤‡ā¤¸ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚â€Ļ", + "add_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_to_album_bottom_sheet_added": "{album} ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_to_album_bottom_sheet_already_exists": "{album} ā¤ŽāĨ‡ā¤‚ ā¤Ēā¤šā¤˛āĨ‡ ⤏āĨ‡ ā¤šāĨˆ", + "add_to_locked_folder": "⤗āĨā¤ĒāĨā¤¤ ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤ŽāĨ‡ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_to_shared_album": "ā¤ļāĨ‡ā¤¯ā¤° ⤕ā¤ŋā¤ ā¤—ā¤ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_url": "URL ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "added_to_archive": "⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", - "added_to_favorites": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", - "added_to_favorites_count": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ {count, number} ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "added_to_favorites": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", + "added_to_favorites_count": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ {count, number} ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "admin": { "add_exclusion_pattern_description": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚. *, **, ⤔⤰ ? ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤕āĨ‡ ⤗āĨā¤˛āĨ‹ā¤Ŧā¤ŋ⤂⤗ ā¤•ā¤°ā¤¨ā¤ž ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ā¤šāĨˆāĨ¤ \"Raw\" ā¤¨ā¤žā¤Žā¤• ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋā¤•ā¤ž ⤕āĨ€ ⤏⤭āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"**/Raw/**\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ \".tif\" ⤏āĨ‡ ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ ā¤šāĨ‹ā¤¨āĨ‡ ā¤ĩā¤žā¤˛āĨ€ ⤏⤭āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"**/*.tif\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ ⤕ā¤ŋ⤏āĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤Ēā¤Ĩ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"/path/to/ignore/**\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤", "asset_offline_description": "ā¤¯ā¤š ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤ā¤¸āĨ‡ā¤Ÿ ⤅ā¤Ŧ ā¤Ąā¤ŋ⤏āĨā¤• ā¤Ē⤰ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ ⤔⤰ ⤇⤏āĨ‡ ⤟āĨā¤°āĨˆā¤ļ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤ ⤝ā¤Ļā¤ŋ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤕āĨ‹ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‡ ⤭āĨ€ā¤¤ā¤° ā¤•ā¤šāĨ€ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤Ĩā¤ž, ⤤āĨ‹ ⤍⤈ ⤏⤂ā¤Ŧ⤂⤧ā¤ŋ⤤ ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤅ā¤Ē⤍āĨ€ ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚āĨ¤ ⤇⤏ ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤ĩā¤žā¤Ē⤏ ā¤Ēā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨƒā¤Ēā¤¯ā¤ž ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤍āĨ€ā¤šāĨ‡ ā¤Ļā¤ŋā¤ ā¤—ā¤ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤Ēā¤Ĩ ⤕āĨ‹ ā¤‡ā¤ŽāĨā¤Žā¤ŋ⤚ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤ā¤•āĨā¤¸āĨ‡ā¤¸ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆ ⤔⤰ ā¤Ģā¤ŋ⤰ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ⤏āĨā¤•āĨˆā¤¨ ⤕⤰āĨ‡ā¤‚āĨ¤", @@ -45,6 +45,7 @@ "backup_settings": "ā¤ŦāĨˆā¤•⤅ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "backup_settings_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍", "check_all": "⤏⤭āĨ€ ⤚āĨ‡ā¤• ⤕⤰āĨ‡ā¤‚", + "cleanup": "ā¤¸ā¤žā¤Ģā¤ŧ-⤏ā¤Ģā¤ŧā¤žā¤ˆ", "cleared_jobs": "{job}: ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤•ā¤žā¤°āĨā¤¯ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰ ā¤Ļā¤ŋā¤ ā¤—ā¤", "config_set_by_file": "Config ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ā¤ŽāĨ‡ā¤‚ ā¤ā¤• config ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤏āĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", "confirm_delete_library": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {library} ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", @@ -146,8 +147,8 @@ "metadata_extraction_job_description": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤏āĨ‡ ⤜āĨ€ā¤ĒāĨ€ā¤ā¤¸ ⤔⤰ ⤰ā¤ŋ⤜ā¤ŧāĨ‰ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤜āĨˆā¤¸āĨ€ ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤍ā¤ŋā¤•ā¤žā¤˛āĨ‡ā¤‚", "migration_job": "ā¤ĒāĨā¤°ā¤ĩā¤žā¤¸", "migration_job_description": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤔⤰ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ‹ ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ā¤ŽāĨ‡ā¤‚ ā¤Žā¤žā¤‡ā¤—āĨā¤°āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", - "no_paths_added": "⤕āĨ‹ā¤ˆ ā¤Ēā¤Ĩ ā¤¨ā¤šāĨ€ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", - "no_pattern_added": "⤕āĨ‹ā¤ˆ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "no_paths_added": "⤕āĨ‹ā¤ˆ ā¤Ēā¤Ĩ ā¤¨ā¤šāĨ€ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", + "no_pattern_added": "⤕āĨ‹ā¤ˆ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "note_apply_storage_label_previous_assets": "⤍āĨ‹ā¤Ÿ: ā¤Ēā¤šā¤˛āĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤šā¤˛ā¤žā¤ā¤", "note_cannot_be_changed_later": "⤍āĨ‹ā¤Ÿ: ⤇⤏āĨ‡ ā¤Ŧā¤žā¤Ļ ā¤ŽāĨ‡ā¤‚ ā¤Ŧā¤Ļā¤˛ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž!", "notification_email_from_address": "⤇⤏ ā¤Ē⤤āĨ‡ ⤏āĨ‡", @@ -171,20 +172,13 @@ "oauth_auto_register": "ā¤‘ā¤ŸāĨ‹ ⤰⤜ā¤ŋ⤏āĨā¤Ÿā¤°", "oauth_auto_register_description": "OAuth ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤¨ā¤ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤Ēā¤‚ā¤œāĨ€ā¤•āĨƒā¤¤ ⤕⤰āĨ‡ā¤‚", "oauth_button_text": "⤟āĨ‡ā¤•āĨā¤¸āĨā¤Ÿ ā¤Ŧ⤟⤍", - "oauth_client_id": "⤗āĨā¤°ā¤žā¤šā¤• ID", - "oauth_client_secret": "⤗āĨā¤°ā¤žā¤šā¤• ⤗āĨā¤ĒāĨā¤¤", "oauth_enable_description": "OAuth ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚", - "oauth_issuer_url": "ā¤œā¤žā¤°āĨ€ā¤•⤰āĨā¤¤ā¤ž URL", "oauth_mobile_redirect_uri": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ", "oauth_mobile_redirect_uri_override": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ ⤓ā¤ĩā¤°ā¤°ā¤žā¤‡ā¤Ą", "oauth_mobile_redirect_uri_override_description": "⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚ ⤜ā¤Ŧ 'app.immitch:/' ā¤ā¤• ā¤…ā¤Žā¤žā¤¨āĨā¤¯ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ ā¤šāĨ‹āĨ¤", - "oauth_profile_signing_algorithm": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤šā¤¸āĨā¤¤ā¤žā¤•āĨā¤ˇā¤° ā¤ā¤˛āĨā¤—āĨ‹ā¤°ā¤ŋā¤ĨāĨā¤Ž", - "oauth_profile_signing_algorithm_description": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤Ē⤰ ā¤šā¤¸āĨā¤¤ā¤žā¤•āĨā¤ˇā¤° ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ā¤˛āĨā¤—āĨ‹ā¤°ā¤ŋā¤Ļā¤Ž ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆāĨ¤", - "oauth_scope": "⤏āĨā¤•āĨ‹ā¤Ē", "oauth_settings": "⤓⤑ā¤Ĩ", "oauth_settings_description": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "oauth_settings_more_details": "⤇⤏ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤕āĨ‡ ā¤Ŧā¤žā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚ ā¤ĄāĨ‰ā¤•āĨā¤¸āĨ¤", - "oauth_signing_algorithm": "ā¤šā¤¸āĨā¤¤ā¤žā¤•āĨā¤ˇā¤° ā¤ā¤˛āĨā¤—āĨ‹ā¤°ā¤ŋā¤ĨāĨā¤Ž", "oauth_storage_label_claim": "ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤞āĨ‡ā¤Ŧ⤞ ā¤•ā¤ž ā¤Ļā¤žā¤ĩā¤ž", "oauth_storage_label_claim_description": "⤇⤏ ā¤Ļā¤žā¤ĩāĨ‡ ⤕āĨ‡ ā¤ŽāĨ‚⤞āĨā¤¯ ā¤Ē⤰ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‡ ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤞āĨ‡ā¤Ŧ⤞ ⤕āĨ‹ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚āĨ¤", "oauth_storage_quota_claim": "ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ā¤•ā¤ž ā¤Ļā¤žā¤ĩā¤ž", @@ -316,41 +310,18 @@ "admin_password": "ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "administration": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤¨", "advanced": "ā¤ĩā¤ŋ⤕⤏ā¤ŋ⤤", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", - "album_added": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "album_added": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "album_added_notification_setting_description": "⤜ā¤Ŧ ⤆ā¤Ē⤕āĨ‹ ⤕ā¤ŋ⤏āĨ€ ā¤¸ā¤žā¤ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤œā¤žā¤ ⤤āĨ‹ ā¤ā¤• ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰āĨ‡ā¤‚", "album_cover_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕ā¤ĩ⤰ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", "album_info_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕āĨ€ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕āĨ€ ā¤—ā¤ˆ", "album_leave": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤛āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚?", "album_name": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤•ā¤ž ā¤¨ā¤žā¤Ž", "album_options": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "album_remove_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤žā¤ā¤‚?", "album_share_no_users": "ā¤ā¤¸ā¤ž ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē⤍āĨ‡ ā¤¯ā¤š ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏⤭āĨ€ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤šāĨˆ ā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ā¤¸ā¤žā¤ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨ‹ā¤ˆ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", "album_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "album_updated_setting_description": "⤜ā¤Ŧ ⤕ā¤ŋ⤏āĨ€ ā¤¸ā¤žā¤ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤ ā¤šāĨ‹ā¤‚ ⤤āĨ‹ ā¤ā¤• ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰āĨ‡ā¤‚", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", "album_viewer_appbar_share_to": "ā¤¸ā¤žā¤ā¤ž ⤕⤰āĨ‡ā¤‚", - "album_viewer_page_share_add_users": "Add users", "album_with_link_access": "⤞ā¤ŋ⤂⤕ ā¤ĩā¤žā¤˛āĨ‡ ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤇⤏ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ā¤ĻāĨ‡ā¤‚āĨ¤", "albums": "ā¤ā¤˛ā¤Ŧā¤Ž", "all": "⤏⤭āĨ€", @@ -372,113 +343,34 @@ "appears_in": "ā¤ĒāĨā¤°ā¤•ā¤Ÿ ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆ", "archive": "⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯", "archive_or_unarchive_photo": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ā¤¯ā¤ž ⤅⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕⤰āĨ‡ā¤‚", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", "archive_size": "ā¤ĒāĨā¤°ā¤žā¤˛āĨ‡ā¤– ā¤†ā¤•ā¤žā¤°", "archive_size_description": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏⤂⤗āĨā¤°ā¤š ā¤†ā¤•ā¤žā¤° ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰ ⤕⤰āĨ‡ā¤‚ (GiB ā¤ŽāĨ‡ā¤‚)", "archived": "⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤", "are_these_the_same_person": "⤕āĨā¤¯ā¤ž ⤝āĨ‡ ā¤ĩā¤šāĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ā¤šāĨˆā¤‚?", "are_you_sure_to_do_this": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤¸āĨā¤¤ā¤ĩ ā¤ŽāĨ‡ā¤‚ ⤇⤏āĨ‡ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", - "asset_added_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", - "asset_adding_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆ..āĨ¤", + "asset_added_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", + "asset_adding_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆ..āĨ¤", "asset_description_updated": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", "asset_has_unassigned_faces": "ā¤ā¤¸āĨ‡ā¤Ÿ ā¤ŽāĨ‡ā¤‚ ⤅⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤šāĨˆā¤‚", "asset_hashing": "ā¤šāĨˆā¤ļā¤ŋ⤂⤗..āĨ¤", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", "asset_offline": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨", "asset_offline_description": "ā¤¯ā¤š ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤šāĨˆāĨ¤", "asset_restored_successfully": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", "asset_skipped": "⤛āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", "asset_uploaded": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕ā¤ŋā¤ ā¤—ā¤", "asset_uploading": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆ..āĨ¤", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", "assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤‚", - "assets_deleted_permanently": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", - "assets_deleted_permanently_from_server": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", - "assets_removed_permanently_from_device": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", + "assets_deleted_permanently": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", + "assets_deleted_permanently_from_server": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", + "assets_removed_permanently_from_device": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", "assets_restore_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤅ā¤Ē⤍āĨ€ ⤏⤭āĨ€ ⤍⤎āĨā¤Ÿ ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡!", - "assets_restored_successfully": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", - "assets_trashed": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", - "assets_trashed_from_server": "{} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", + "assets_restored_successfully": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", + "assets_trashed": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", + "assets_trashed_from_server": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", "authorized_devices": "⤅⤧ā¤ŋ⤕āĨƒā¤¤ ⤉ā¤Ē⤕⤰⤪", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "ā¤ĩā¤žā¤Ē⤏", "back_close_deselect": "ā¤ĩā¤žā¤Ē⤏ ā¤œā¤žā¤ā¤, ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚, ā¤¯ā¤ž ā¤…ā¤šā¤¯ā¤¨ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", - "backup_controller_page_background_app_refresh_enable_button_text": "Go to settings", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "backup_controller_page_turn_on": "Turn on foreground backup", - "backup_controller_page_uploading_file_info": "Uploading file info", - "backup_err_only_album": "Cannot remove the only album", - "backup_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "ā¤Ēā¤ŋā¤›ā¤˛ā¤ž", "birthdate_saved": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ", "birthdate_set_description": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤕āĨ‡ ā¤¸ā¤Žā¤¯ ⤇⤏ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ€ ⤆⤝āĨ ⤕āĨ€ ā¤—ā¤Ŗā¤¨ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆāĨ¤", @@ -486,52 +378,26 @@ "build": "⤍ā¤ŋ⤰āĨā¤Žā¤žā¤Ŗ", "build_image": "⤛ā¤ĩā¤ŋ ā¤Ŧā¤¨ā¤žā¤ā¤", "buy": "ā¤‡ā¤ŽāĨā¤ŽāĨ€ā¤š ⤖⤰āĨ€ā¤ĻāĨ‹", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", "cache_settings_tile_subtitle": "⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‡ ā¤ĩāĨā¤¯ā¤ĩā¤šā¤žā¤° ⤕āĨ‹ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "cache_settings_tile_title": "⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ", - "cache_settings_title": "Caching Settings", "camera": "⤕āĨˆā¤Žā¤°ā¤ž", "camera_brand": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ŦāĨā¤°ā¤žā¤‚ā¤Ą", "camera_model": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ŽāĨ‰ā¤Ąā¤˛", "cancel": "⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤¨ā¤ž", "cancel_search": "⤖āĨ‹ā¤œ ⤰ā¤ĻāĨā¤Ļ ⤕⤰āĨ‡ā¤‚", - "canceled": "Canceled", "cannot_merge_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ā¤•ā¤ž ā¤ĩā¤ŋ⤞⤝ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨ‹ ā¤¸ā¤•ā¤¤ā¤ž", "cannot_undo_this_action": "⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡!", "cannot_update_the_description": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", "change_date": "ā¤Ŧā¤Ļā¤˛ā¤žā¤ĩ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤•", - "change_display_order": "Change display order", "change_expiration_time": "ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ā¤ŋ ā¤¸ā¤Žā¤¯ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_name": "ā¤¨ā¤žā¤Ž ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤¨ ⤕⤰āĨ‡ā¤‚", "change_name_successfully": "ā¤¨ā¤žā¤Ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_password_description": "ā¤¯ā¤š ā¤¯ā¤ž ⤤āĨ‹ ā¤Ēā¤šā¤˛āĨ€ ā¤Ŧā¤žā¤° ā¤šāĨˆ ⤜ā¤Ŧ ⤆ā¤Ē ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚ ā¤¯ā¤ž ⤆ā¤Ēā¤•ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", "change_your_password": "⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "changed_visibility_successfully": "ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤ŋ⤤", "check_all": "⤏⤭āĨ€ ⤚āĨ‡ā¤• ⤕⤰āĨ‡ā¤‚", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "⤞āĨ‰ā¤— ā¤œā¤žā¤‚ā¤šāĨ‡ā¤‚", "choose_matching_people_to_merge": "ā¤Žā¤°āĨā¤œ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Žā¤ŋ⤞⤤āĨ‡-⤜āĨā¤˛ā¤¤āĨ‡ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤚āĨā¤¨āĨ‡ā¤‚", "city": "ā¤ļā¤šā¤°", @@ -540,14 +406,6 @@ "clear_all_recent_searches": "⤏⤭āĨ€ ā¤šā¤žā¤˛ā¤ŋā¤¯ā¤ž ⤖āĨ‹ā¤œāĨ‡ā¤‚ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰āĨ‡ā¤‚", "clear_message": "⤏āĨā¤Ē⤎āĨā¤Ÿ ⤏⤂ā¤ĻāĨ‡ā¤ļ", "clear_value": "⤏āĨā¤Ē⤎āĨā¤Ÿ ā¤ŽāĨ‚⤞āĨā¤¯", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "close": "ā¤Ŧ⤂ā¤Ļ", "collapse": "⤗ā¤ŋ⤰ ā¤œā¤žā¤¨ā¤ž", "collapse_all": "⤏⤭āĨ€ ⤕āĨ‹ ⤏⤂⤕āĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", @@ -556,9 +414,6 @@ "comment_options": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "comments_and_likes": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋā¤¯ā¤žā¤ ⤔⤰ ā¤Ē⤏⤂ā¤Ļ", "comments_are_disabled": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋā¤¯ā¤žā¤ ⤅⤕āĨā¤ˇā¤Ž ā¤šāĨˆā¤‚", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", "confirm": "ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ", "confirm_admin_password": "ā¤ā¤Ąā¤Žā¤ŋ⤍ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤕āĨ€ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕⤰āĨ‡ā¤‚", "confirm_delete_shared_link": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", @@ -566,15 +421,6 @@ "contain": "ā¤¸ā¤Žā¤žā¤šā¤ŋ⤤", "context": "⤏⤂ā¤Ļ⤰āĨā¤­", "continue": "ā¤œā¤žā¤°āĨ€", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", "copied_image_to_clipboard": "⤛ā¤ĩā¤ŋ ⤕āĨ‹ ⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤕āĨ‰ā¤ĒāĨ€ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤žāĨ¤", "copied_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤍⤕⤞!", "copy_error": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤Ēā¤ŋ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", @@ -589,7 +435,6 @@ "covers": "⤆ā¤ĩ⤰⤪", "create": "⤤āĨˆā¤¯ā¤žā¤° ⤕⤰āĨ‡ā¤‚", "create_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤“", - "create_album_page_untitled": "Untitled", "create_library": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_link": "⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_link_to_share": "ā¤ļāĨ‡ā¤¯ā¤° ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤ā¤‚", @@ -598,23 +443,16 @@ "create_new_person": "ā¤¨ā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_new_person_hint": "⤚⤝⤍ā¤ŋ⤤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ā¤• ā¤¨ā¤ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤏āĨŒā¤‚ā¤ĒāĨ‡ā¤‚", "create_new_user": "ā¤¨ā¤¯ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ŧā¤¨ā¤žā¤ā¤‚", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", "create_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ŧā¤¨ā¤žā¤‡ā¤¯āĨ‡", "created": "ā¤Ŧā¤¨ā¤žā¤¯ā¤ž", "crop": "ā¤›ā¤žā¤ā¤ŸāĨ‡ā¤‚", - "curated_object_page_title": "Things", "current_device": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤉ā¤Ē⤕⤰⤪", - "current_server_address": "Current server address", "custom_locale": "⤕⤏āĨā¤Ÿā¤Ž ⤞āĨ‹ā¤•āĨ‡ā¤˛", "custom_locale_description": "ā¤­ā¤žā¤ˇā¤ž ⤔⤰ ⤕āĨā¤ˇāĨ‡ā¤¤āĨā¤° ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ⤔⤰ ⤏⤂⤖āĨā¤¯ā¤žā¤ā¤ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "ā¤Ąā¤žā¤°āĨā¤•", "date_after": "⤇⤏⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ⤕āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", "date_and_time": "⤤ā¤ŋā¤Ĩā¤ŋ ⤔⤰ ā¤¸ā¤Žā¤¯", "date_before": "ā¤Ēā¤šā¤˛āĨ‡ ⤕āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ", "date_range": "⤤ā¤ŋā¤Ĩā¤ŋ ⤏āĨ€ā¤Žā¤ž", "day": "ā¤Ļā¤ŋ⤍", @@ -624,25 +462,15 @@ "delete": "ā¤šā¤Ÿā¤žā¤ā¤", "delete_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤žā¤ā¤", "delete_api_key_prompt": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨā¤‚ā¤œāĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", "delete_duplicates_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤍ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕āĨ‹ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "delete_key": "⤕āĨā¤‚ā¤œāĨ€ ā¤šā¤Ÿā¤žā¤ā¤", "delete_library": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤šā¤Ÿā¤žā¤ā¤", "delete_link": "⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤žā¤ā¤", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", "delete_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤ā¤‚", "delete_shared_link_dialog_title": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤ā¤‚", "delete_user": "⤉ā¤Ē⤭āĨ‹ā¤•āĨā¤¤ā¤ž ā¤Žā¤ŋā¤Ÿā¤žā¤¯āĨ‡ā¤‚", "deleted_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "description": "ā¤ĩ⤰āĨā¤Ŗā¤¨", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", "details": "ā¤ĩā¤ŋā¤ĩ⤰⤪", "direction": "ā¤Ļā¤ŋā¤ļā¤ž", "disabled": "⤅⤕āĨā¤ˇā¤Ž", @@ -662,7 +490,7 @@ "download_enqueue": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤¤ā¤žā¤° ā¤ŽāĨ‡ā¤‚ ā¤šāĨˆ", "download_error": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "download_failed": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĩā¤ŋā¤Ģ⤞", - "download_filename": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛: {}", + "download_filename": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛: {filename}", "download_finished": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤", "download_notfound": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋā¤˛ā¤ž", "download_paused": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏āĨā¤Ĩ⤗ā¤ŋ⤤", @@ -690,26 +518,21 @@ "edit_key": "⤕āĨā¤‚ā¤œāĨ€ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_link": "⤞ā¤ŋ⤂⤕ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "edit_location_dialog_title": "Location", "edit_name": "ā¤¨ā¤žā¤Ž ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_title": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_user": "⤝āĨ‚ā¤œā¤° ⤕āĨ‹ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‹", "edited": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤", - "editor": "", "email": "ā¤ˆā¤ŽāĨ‡ā¤˛", - "empty_folder": "This folder is empty", "empty_trash": "⤕āĨ‚ā¤Ąā¤ŧāĨ‡ā¤Ļā¤žā¤¨ ā¤–ā¤žā¤˛āĨ€ ⤕⤰āĨ‡ā¤‚", "empty_trash_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‹ ⤝⤕āĨ€ā¤¨ ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē ā¤•ā¤šā¤°ā¤ž ā¤–ā¤žā¤˛āĨ€ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ā¤¯ā¤š ā¤‡ā¤Žā¤ŋ⤚ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤•ā¤šā¤°ā¤ž ā¤ŽāĨ‡ā¤‚ ⤏⤭āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤šā¤Ÿā¤ž ā¤ĻāĨ‡ā¤—ā¤žāĨ¤\n⤆ā¤Ē ⤇⤏ ā¤•ā¤žā¤°āĨā¤°ā¤ĩā¤žā¤ˆ ⤕āĨ‹ ā¤¨ā¤šāĨ€ā¤‚ ⤰āĨ‹ā¤• ⤏⤕⤤āĨ‡!", "enable": "⤏⤕āĨā¤ˇā¤Ž", "enabled": "⤏⤕āĨā¤°ā¤ŋ⤝", "end_date": "⤅⤂⤤ā¤ŋā¤Ž ⤤ā¤ŋā¤Ĩā¤ŋ", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "⤗⤞⤤āĨ€", - "error_change_sort_album": "Failed to change album sort order", "error_loading_image": "⤛ā¤ĩā¤ŋ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", - "error_saving_image": "⤤āĨā¤°āĨā¤Ÿā¤ŋ: {}", + "error_saving_image": "⤤āĨā¤°āĨā¤Ÿā¤ŋ: {error}", "error_title": "⤤āĨā¤°āĨā¤Ÿā¤ŋ - ⤕āĨā¤› ⤗⤞⤤ ā¤šāĨ‹ ā¤—ā¤¯ā¤ž", "errors": { "cannot_navigate_next_asset": "⤅⤗⤞āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ē⤰ ⤍āĨ‡ā¤ĩā¤ŋ⤗āĨ‡ā¤Ÿ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", @@ -720,8 +543,8 @@ "cant_get_number_of_comments": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋ⤞ ⤏⤕āĨ€", "cant_search_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤖āĨ‹ā¤œā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", "cant_search_places": "⤏āĨā¤Ĩā¤žā¤¨ ⤖āĨ‹ā¤œ ā¤¨ā¤šāĨ€ā¤‚ ⤏⤕⤤āĨ‡", - "error_adding_assets_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", - "error_adding_users_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", + "error_adding_assets_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", + "error_adding_users_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "error_deleting_shared_user": "ā¤¸ā¤žā¤ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "error_hiding_buy_button": "⤖⤰āĨ€ā¤ĻāĨ‡ā¤‚ ā¤Ŧ⤟⤍ ⤛ā¤ŋā¤Ēā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "error_removing_assets_from_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏āĨ‡ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ, ⤅⤧ā¤ŋ⤕ ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕⤂⤏āĨ‹ā¤˛ ⤕āĨ€ ā¤œā¤žā¤ā¤š ⤕⤰āĨ‡ā¤‚", @@ -741,12 +564,12 @@ "incorrect_email_or_password": "⤗⤞⤤ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "profile_picture_transparent_pixels": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤°āĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ā¤Ēā¤žā¤°ā¤Ļ⤰āĨā¤ļāĨ€ ā¤Ēā¤ŋ⤕āĨā¤¸āĨ‡ā¤˛ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨ‹ ⤏⤕⤤āĨ‡āĨ¤", "quota_higher_than_disk_size": "⤆ā¤Ē⤍āĨ‡ ā¤Ąā¤ŋ⤏āĨā¤• ā¤†ā¤•ā¤žā¤° ⤏āĨ‡ ⤅⤧ā¤ŋ⤕ ⤕āĨ‹ā¤Ÿā¤ž ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ⤕ā¤ŋā¤¯ā¤ž ā¤šāĨˆ", - "unable_to_add_album_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_add_assets_to_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤ŽāĨ‡ā¤‚ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_add_comment": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_add_exclusion_pattern": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", - "unable_to_add_partners": "ā¤¸ā¤žā¤āĨ‡ā¤Ļā¤žā¤° ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_album_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_assets_to_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤ŽāĨ‡ā¤‚ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_comment": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_exclusion_pattern": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_partners": "ā¤¸ā¤žā¤āĨ‡ā¤Ļā¤žā¤° ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_album_user_role": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ€ ⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_date": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_favorite": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -823,21 +646,9 @@ "unable_to_upload_file": "ā¤Ģā¤žā¤‡ā¤˛ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ" }, "exif": "ā¤ā¤•āĨā¤¸ā¤ŋā¤Ģ", - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_add_person": "ā¤¨ā¤žā¤Ž ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "exit_slideshow": "⤏āĨā¤˛ā¤žā¤‡ā¤Ą ā¤ļāĨ‹ ⤏āĨ‡ ā¤Ŧā¤žā¤šā¤° ⤍ā¤ŋ⤕⤞āĨ‡ā¤‚", "expand_all": "⤏⤭āĨ€ ā¤•ā¤ž ā¤ĩā¤ŋ⤏āĨā¤¤ā¤žā¤°", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", - "experimental_settings_title": "Experimental", "expire_after": "ā¤ā¤•āĨā¤¸ā¤Ēā¤žā¤¯ā¤° ⤆ā¤ĢāĨā¤Ÿā¤°", "expired": "⤖⤤āĨā¤Ž ā¤šāĨ‹ ⤚āĨā¤•ā¤ž", "explore": "⤅⤍āĨā¤ĩāĨ‡ā¤ˇā¤Ŗ ā¤•ā¤°ā¤¨ā¤ž", @@ -846,16 +657,11 @@ "extension": "ā¤ĩā¤ŋ⤏āĨā¤¤ā¤žā¤°", "external": "ā¤Ŧā¤žā¤šā¤°āĨ€", "external_libraries": "ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "⤏āĨŒā¤‚ā¤ĒāĨ‡ ā¤¨ā¤šāĨ€ā¤‚ ā¤—ā¤", - "failed": "Failed", - "failed_to_load_assets": "Failed to load assets", - "failed_to_load_folder": "Failed to load folder", "favorite": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž", "favorite_or_unfavorite_photo": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤¯ā¤ž ā¤¨ā¤žā¤Ē⤏⤂ā¤Ļ ā¤ĢāĨ‹ā¤ŸāĨ‹", "favorites": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž", - "favorites_page_no_favorites": "No favorite assets found", "feature_photo_updated": "ā¤Ģā¤ŧāĨ€ā¤šā¤° ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "file_name": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤•ā¤ž ā¤¨ā¤žā¤Ž", "file_name_or_extension": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤•ā¤ž ā¤¨ā¤žā¤Ž ā¤¯ā¤ž ā¤ā¤•āĨā¤¸ā¤ŸāĨ‡ā¤‚ā¤ļ⤍", @@ -865,58 +671,36 @@ "filter_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤Ģā¤ŧā¤ŋ⤞āĨā¤Ÿā¤° ⤕⤰āĨ‡ā¤‚", "find_them_fast": "⤖āĨ‹ā¤œ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¨ā¤žā¤Ž ⤏āĨ‡ ⤉⤍āĨā¤šāĨ‡ā¤‚ ⤤āĨ‡ā¤œāĨ€ ⤏āĨ‡ ā¤ĸāĨ‚⤂ā¤ĸāĨ‡ā¤‚", "fix_incorrect_match": "⤗ā¤ŧ⤞⤤ ā¤Žā¤ŋā¤˛ā¤žā¤¨ ⤠āĨ€ā¤• ⤕⤰āĨ‡ā¤‚", - "folder": "Folder", - "folder_not_found": "Folder not found", - "folders": "Folders", "forward": "⤆⤗āĨ‡", "general": "ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯", "get_help": "ā¤Žā¤Ļā¤Ļ ⤞āĨ‡ā¤‚", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "ā¤ļāĨā¤°āĨ‚ ā¤•ā¤°ā¤¨ā¤ž", "go_back": "ā¤ĩā¤žā¤Ē⤏ ā¤œā¤žā¤“", "go_to_search": "⤖āĨ‹ā¤œ ā¤Ē⤰ ā¤œā¤žā¤ā¤", - "grant_permission": "Grant permission", "group_albums_by": "⤇⤍⤕āĨ‡ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤ŽāĨ‚ā¤š ā¤ā¤˛āĨā¤Ŧā¤Ž..āĨ¤", "group_no": "⤕āĨ‹ā¤ˆ ā¤¸ā¤ŽāĨ‚ā¤šāĨ€ā¤•⤰⤪ ā¤¨ā¤šāĨ€ā¤‚", "group_owner": "⤏āĨā¤ĩā¤žā¤ŽāĨ€ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤ŽāĨ‚ā¤š", "group_year": "ā¤ĩ⤰āĨā¤ˇ ⤕āĨ‡ ⤅⤍āĨā¤¸ā¤žā¤° ā¤¸ā¤ŽāĨ‚ā¤š", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", "has_quota": "⤕āĨ‹ā¤Ÿā¤ž ā¤šāĨˆ", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Header ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "hide_all_people": "⤏⤭āĨ€ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤛āĨā¤Ēā¤žā¤ā¤‚", "hide_gallery": "⤗āĨˆā¤˛ā¤°āĨ€ ⤛ā¤ŋā¤Ēā¤žā¤ā¤", "hide_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤛ā¤ŋā¤Ēā¤žā¤ā¤‚", "hide_person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤛ā¤ŋā¤Ēā¤žā¤ā¤", "hide_unnamed_people": "ā¤…ā¤¨ā¤žā¤Ž ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤛āĨā¤Ēā¤žā¤ā¤‚", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "⤅ā¤Ŧ ⤤⤕ ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", + "home_page_add_to_album_success": "{added} ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž {album} ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ ā¤Ļā¤ŋā¤¯ā¤ž", + "home_page_album_err_partner": "⤅⤭āĨ€ ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ ā¤¨ā¤šāĨ€ā¤‚ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", "home_page_archive_err_partner": "ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", - "home_page_building_timeline": "Building the timeline", "home_page_delete_err_partner": "ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤Ąā¤ŋ⤞āĨ€ā¤Ÿ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", "home_page_favorite_err_partner": "⤅ā¤Ŧ ⤤⤕ ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤ĢāĨ‡ā¤ĩ⤰āĨ‡ā¤Ÿ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "home_page_share_err_local": "⤞āĨ‹ā¤•⤞ ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ⤞ā¤ŋ⤂⤕ ⤕āĨ‡ ⤜⤰ā¤ŋā¤ ā¤ļāĨ‡ā¤¯ā¤° ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡, ⤏āĨā¤•ā¤ŋā¤Ē ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "host": "ā¤ŽāĨ‡ā¤œā¤ŧā¤Ŧā¤žā¤¨", "hour": "ā¤˜ā¤‚ā¤Ÿā¤ž", "ignore_icloud_photos": "⤆⤇⤕āĨā¤˛ā¤žā¤‰ā¤Ą ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰āĨ‡ā¤‚", "ignore_icloud_photos_description": "⤆⤇⤕āĨā¤˛ā¤žā¤‰ā¤Ą ā¤Ē⤰ ⤏āĨā¤ŸāĨ‹ā¤° ⤕āĨ€ ā¤—ā¤ˆ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ā¤œā¤ŧ ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ā¤Ē⤰ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤¨ā¤šāĨ€ā¤‚ ⤕āĨ€ ā¤œā¤žā¤ā¤‚ā¤—āĨ€", "image": "⤛ā¤ĩā¤ŋ", "image_saved_successfully": "ā¤‡ā¤ŽāĨ‡ā¤œ ā¤¸ā¤šāĨ‡ā¤œ ā¤ĻāĨ€ ā¤—ā¤ˆ", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", "immich_logo": "Immich ⤞āĨ‹ā¤—āĨ‹", "immich_web_interface": "ā¤‡ā¤Žā¤ŋ⤚ ā¤ĩāĨ‡ā¤Ŧ ā¤‡ā¤‚ā¤Ÿā¤°ā¤Ģā¤ŧāĨ‡ā¤¸", "import_from_json": "JSON ⤏āĨ‡ ā¤†ā¤¯ā¤žā¤¤ ⤕⤰āĨ‡ā¤‚", @@ -929,7 +713,6 @@ "info": "ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€", "interval": { "day_at_onepm": "ā¤šā¤° ā¤Ļā¤ŋ⤍ ā¤ĻāĨ‹ā¤Ēā¤šā¤° 1 ā¤Ŧ⤜āĨ‡", - "hours": "", "night_at_midnight": "ā¤šā¤° ā¤°ā¤žā¤¤ ⤆⤧āĨ€ ā¤°ā¤žā¤¤ ⤕āĨ‹", "night_at_twoam": "ā¤šā¤° ā¤°ā¤žā¤¤ 2 ā¤Ŧ⤜āĨ‡" }, @@ -951,12 +734,6 @@ "level": "⤏āĨā¤¤ā¤°", "library": "ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯", "library_options": "ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", "light": "⤰āĨ‹ā¤ļ⤍āĨ€", "like_deleted": "⤜āĨˆā¤¸āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "link_options": "⤞ā¤ŋ⤂⤕ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", @@ -965,42 +742,13 @@ "list": "⤏āĨ‚ā¤šāĨ€", "loading": "⤞āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆ", "loading_search_results_failed": "⤖āĨ‹ā¤œ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤¨ā¤ž ā¤ĩā¤ŋā¤Ģ⤞ ā¤°ā¤šā¤ž", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", "log_out": "⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ", "log_out_all_devices": "⤏⤭āĨ€ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ⤕⤰āĨ‡ā¤‚", "logged_out_all_devices": "⤏⤭āĨ€ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ⤕⤰ ā¤Ļā¤ŋā¤ ā¤—ā¤", "logged_out_device": "⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸", "login": "⤞āĨ‰ā¤— ⤇⤍ ⤕⤰āĨ‡ā¤‚", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", "login_has_been_disabled": "⤞āĨ‰ā¤—ā¤ŋ⤍ ⤅⤕āĨā¤ˇā¤Ž ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", "logout_all_device_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤏⤭āĨ€ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "logout_this_device_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤕āĨ‹ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "longitude": "ā¤ĻāĨ‡ā¤ļā¤žā¤¨āĨā¤¤ā¤°", @@ -1016,39 +764,12 @@ "manage_your_devices": "⤅ā¤Ē⤍āĨ‡ ⤞āĨ‰ā¤—-⤇⤍ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "manage_your_oauth_connection": "⤅ā¤Ēā¤¨ā¤ž OAuth ⤕⤍āĨ‡ā¤•āĨā¤ļ⤍ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "map": "⤍⤕āĨā¤ļā¤ž", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", "map_marker_with_image": "⤛ā¤ĩā¤ŋ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ā¤Žā¤žā¤°āĨā¤•⤰", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", "map_settings": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", "matches": "ā¤Žā¤žā¤šā¤ŋ⤏", "media_type": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤ĒāĨā¤°ā¤•ā¤žā¤°", "memories": "ā¤¯ā¤žā¤ĻāĨ‡ā¤‚", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", "memories_setting_description": "⤆ā¤Ē ⤅ā¤Ē⤍āĨ€ ā¤¯ā¤žā¤ĻāĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ ā¤ĻāĨ‡ā¤–⤤āĨ‡ ā¤šāĨˆā¤‚ ⤉⤏āĨ‡ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "ā¤¯ā¤žā¤Ļ", "menu": "ā¤ŽāĨ‡ā¤¨āĨā¤¯āĨ‚", "merge": "ā¤Žā¤°āĨā¤œ", @@ -1061,16 +782,11 @@ "missing": "⤗āĨā¤Ž", "model": "ā¤ŽāĨ‰ā¤Ąā¤˛", "month": "ā¤Žā¤šāĨ€ā¤¨ā¤ž", - "monthly_title_text_date_format": "MMMM y", "more": "⤅⤧ā¤ŋ⤕", "moved_to_trash": "⤕āĨ‚ā¤Ąā¤ŧāĨ‡ā¤Ļā¤žā¤¨ ā¤ŽāĨ‡ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤¯ā¤ž ā¤—ā¤¯ā¤ž", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", "my_albums": "ā¤ŽāĨ‡ā¤°āĨ‡ ā¤ā¤˛āĨā¤Ŧā¤Ž", "name": "ā¤¨ā¤žā¤Ž", "name_or_nickname": "ā¤¨ā¤žā¤Ž ā¤¯ā¤ž ⤉ā¤Ēā¤¨ā¤žā¤Ž", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "⤕⤭āĨ€ ā¤¨ā¤šāĨ€ā¤‚", "new_album": "⤍⤝āĨ€ ā¤ā¤˛āĨā¤Ŧā¤Ž", "new_api_key": "⤍⤈ ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨā¤‚ā¤œāĨ€", @@ -1087,7 +803,6 @@ "no_albums_yet": "ā¤ā¤¸ā¤ž ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ⤅⤭āĨ€ ⤤⤕ ⤕āĨ‹ā¤ˆ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "no_archived_assets_message": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕āĨ‹ ⤅ā¤Ē⤍āĨ‡ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤ĻāĨƒā¤ļāĨā¤¯ ⤏āĨ‡ ⤛ā¤ŋā¤Ēā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤉⤍āĨā¤šāĨ‡ā¤‚ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕⤰āĨ‡ā¤‚", "no_assets_message": "⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤šā¤˛ā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨā¤˛ā¤ŋ⤕ ⤕⤰āĨ‡ā¤‚", - "no_assets_to_show": "No assets to show", "no_duplicates_found": "⤕āĨ‹ā¤ˆ ā¤¨ā¤•ā¤˛ā¤šāĨ€ ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋā¤˛ā¤žāĨ¤", "no_exif_info_available": "⤕āĨ‹ā¤ˆ ā¤ā¤•āĨā¤¸ā¤ŋā¤Ģā¤ŧ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ", "no_explore_results_message": "⤅ā¤Ē⤍āĨ‡ ⤏⤂⤗āĨā¤°ā¤š ā¤•ā¤ž ā¤Ēā¤¤ā¤ž ā¤˛ā¤—ā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤔⤰ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰āĨ‡ā¤‚āĨ¤", @@ -1099,17 +814,11 @@ "no_results_description": "⤕āĨ‹ā¤ˆ ā¤Ē⤰āĨā¤¯ā¤žā¤¯ā¤ĩā¤žā¤šāĨ€ ā¤¯ā¤ž ⤅⤧ā¤ŋ⤕ ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ ⤕āĨ€ā¤ĩ⤰āĨā¤Ą ā¤†ā¤œā¤ŧā¤Žā¤žā¤ā¤", "no_shared_albums_message": "⤅ā¤Ē⤍āĨ‡ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ā¤ŽāĨ‡ā¤‚ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤¸ā¤žā¤ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ā¤• ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤ā¤‚", "not_in_any_album": "⤕ā¤ŋ⤏āĨ€ ā¤ā¤˛ā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¨ā¤šāĨ€ā¤‚", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "⤍āĨ‹ā¤Ÿ: ā¤Ēā¤šā¤˛āĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤šā¤˛ā¤žā¤ā¤", "notes": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋā¤¯ā¤žā¤", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", - "notification_permission_list_tile_content": "Grant permission to enable notifications.", - "notification_permission_list_tile_enable_button": "Enable Notifications", - "notification_permission_list_tile_title": "Notification Permission", "notification_toggle_setting_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤‚ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", "notifications": "⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤‚", "notifications_setting_description": "⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤‚ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "oauth": "OAuth", "offline": "⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨", "offline_paths": "⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤Ēā¤Ĩ", "offline_paths_description": "⤝āĨ‡ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ⤉⤍ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ā¤ŽāĨˆā¤¨āĨā¤¯āĨā¤…⤞ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ā¤•ā¤žā¤°ā¤Ŗ ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚ ⤜āĨ‹ ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤•ā¤ž ā¤šā¤ŋ⤏āĨā¤¸ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆā¤‚āĨ¤", @@ -1135,25 +844,13 @@ "partner": "ā¤¸ā¤žā¤ĨāĨ€", "partner_can_access_assets": "⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤔⤰ ā¤šā¤Ÿā¤žā¤ ā¤—ā¤ ⤕āĨ‹ ⤛āĨ‹ā¤Ąā¤ŧ⤕⤰ ⤆ā¤Ē⤕āĨ‡ ⤏⤭āĨ€ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", "partner_can_access_location": "ā¤ĩā¤š ⤏āĨā¤Ĩā¤žā¤¨ ā¤œā¤šā¤žā¤‚ ⤆ā¤Ē⤕āĨ€ ⤤⤏āĨā¤ĩāĨ€ā¤°āĨ‡ā¤‚ ⤞āĨ€ ā¤—ā¤ˆā¤‚ ā¤ĨāĨ€ā¤‚", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", + "partner_page_stop_sharing_content": "{partner} will no longer be able to access your photosāĨ¤", "partner_sharing": "ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ā¤ļāĨ‡ā¤¯ā¤°ā¤ŋ⤂⤗", "partners": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤°āĨ‹ā¤‚", "password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "password_does_not_match": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤ŽāĨˆā¤š ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ā¤°ā¤šā¤ž ā¤šāĨˆ", "password_required": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤆ā¤ĩā¤ļāĨā¤¯ā¤•", "password_reset_success": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤏ā¤Ģ⤞", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, "path": "ā¤Ēā¤Ĩ", "pattern": "ā¤¨ā¤ŽāĨ‚ā¤¨ā¤ž", "pause": "ā¤ĩā¤ŋā¤°ā¤žā¤Ž", @@ -1167,13 +864,6 @@ "permanently_delete": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨ā¤ž", "permanently_deleted_asset": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ˆ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ", "permission_onboarding_back": "ā¤ĩā¤žā¤Ē⤏", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", "person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ", "photo_shared_all_users": "ā¤ā¤¸ā¤ž ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē⤍āĨ‡ ⤅ā¤Ē⤍āĨ€ ⤤⤏āĨā¤ĩāĨ€ā¤°āĨ‡ā¤‚ ⤏⤭āĨ€ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕āĨ€ā¤‚ ā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ā¤¸ā¤žā¤ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨ‹ā¤ˆ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "photos": "⤤⤏āĨā¤ĩāĨ€ā¤°āĨ‡ā¤‚", @@ -1187,21 +877,13 @@ "play_motion_photo": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤šā¤˛ā¤žā¤ā¤‚", "play_or_pause_video": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤šā¤˛ā¤žā¤ā¤‚ ā¤¯ā¤ž ⤰āĨ‹ā¤•āĨ‡ā¤‚", "port": "ā¤Ē⤤āĨā¤¤ā¤¨", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", "preset": "ā¤ĒāĨā¤°āĨ€ā¤¸āĨ‡ā¤Ÿ", "preview": "ā¤ĒāĨ‚⤰āĨā¤ĩ ā¤Ļ⤰āĨā¤ļ⤍", "previous": "ā¤Ēā¤šā¤˛āĨ‡ ā¤•ā¤ž", "previous_memory": "ā¤Ēā¤ŋ⤛⤞āĨ€ ⤏āĨā¤ŽāĨƒā¤¤ā¤ŋ", "previous_or_next_photo": "ā¤Ēā¤ŋā¤›ā¤˛ā¤ž ā¤¯ā¤ž ā¤…ā¤—ā¤˛ā¤ž ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹", "primary": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", "profile_drawer_github": "⤗ā¤ŋā¤Ÿā¤šā¤Ŧ", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", "profile_picture_set": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤ŸāĨ¤", "public_album": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ā¤˛āĨā¤Ŧā¤Ž", "public_share": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ļāĨ‡ā¤¯ā¤°", @@ -1242,8 +924,8 @@ "reassing_hint": "⤚⤝⤍ā¤ŋ⤤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ⤕ā¤ŋ⤏āĨ€ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤏āĨŒā¤‚ā¤ĒāĨ‡ā¤‚", "recent": "ā¤šā¤žā¤˛ ā¤šāĨ€ ā¤•ā¤ž", "recent_searches": "ā¤šā¤žā¤˛ ⤕āĨ€ ⤖āĨ‹ā¤œāĨ‡ā¤‚", - "recently_added": "ā¤šā¤žā¤˛ ā¤šāĨ€ ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", - "recently_added_page_title": "Recently Added", + "recently_added": "ā¤šā¤žā¤˛ ā¤šāĨ€ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", + "recently_added_page_title": "ā¤šā¤žā¤˛ ā¤šāĨ€ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "refresh": "ā¤¤ā¤žā¤œā¤ŧā¤ž ā¤•ā¤°ā¤¨ā¤ž", "refresh_encoded_videos": "ā¤ā¤¨āĨā¤•āĨ‹ā¤ĄāĨ‡ā¤Ą ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤¤ā¤žā¤œā¤ŧā¤ž ⤕⤰āĨ‡ā¤‚", "refresh_metadata": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤¤ā¤žā¤œā¤ŧā¤ž ⤕⤰āĨ‡ā¤‚", @@ -1292,7 +974,6 @@ "saved_profile": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ", "saved_settings": "ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "say_something": "⤕āĨā¤› ā¤•ā¤šāĨ‡ā¤‚", - "scaffold_body_error_occurred": "Error occurred", "scan_all_libraries": "⤏⤭āĨ€ ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯āĨ‹ā¤‚ ⤕āĨ‹ ⤏āĨā¤•āĨˆā¤¨ ⤕⤰āĨ‡ā¤‚", "scan_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ⤏āĨā¤•āĨˆā¤¨ ⤕⤰āĨ‡ā¤‚", "scanning_for_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏āĨā¤•āĨˆā¤¨ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆ..āĨ¤", @@ -1305,40 +986,21 @@ "search_camera_model": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ŽāĨ‰ā¤Ąā¤˛ ⤖āĨ‹ā¤œāĨ‡ā¤‚..āĨ¤", "search_city": "ā¤ļā¤šā¤° ⤖āĨ‹ā¤œāĨ‡ā¤‚..āĨ¤", "search_country": "ā¤ĻāĨ‡ā¤ļ ⤖āĨ‹ā¤œāĨ‡ā¤‚..āĨ¤", - "search_filter_apply": "Apply filter", "search_filter_camera_title": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ĒāĨā¤°ā¤•ā¤žā¤° ⤚āĨā¤¨āĨ‡ā¤‚", "search_filter_date": "ā¤¤ā¤žā¤°āĨ€ā¤–ā¤ŧ", "search_filter_date_interval": "{start} ⤏āĨ‡ {end} ⤤⤕", "search_filter_date_title": "ā¤¤ā¤žā¤°āĨ€ā¤–ā¤ŧ ⤕āĨ€ ⤏āĨ€ā¤Žā¤ž ⤚āĨā¤¨āĨ‡ā¤‚", - "search_filter_display_option_not_in_album": "Not in album", "search_filter_display_options": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", - "search_filter_filename": "Search by file name", "search_filter_location": "⤏āĨā¤Ĩā¤žā¤¨", "search_filter_location_title": "⤏āĨā¤Ĩā¤žā¤¨ ⤚āĨā¤¨āĨ‡ā¤‚", "search_filter_media_type": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤ĒāĨā¤°ā¤•ā¤žā¤°", "search_filter_media_type_title": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤ĒāĨā¤°ā¤•ā¤žā¤° ⤚āĨā¤¨āĨ‡ā¤‚", "search_filter_people_title": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ā¤•ā¤ž ⤚⤝⤍ ⤕⤰āĨ‡ā¤‚", "search_for_existing_person": "ā¤ŽāĨŒā¤œāĨ‚ā¤Ļā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤖āĨ‹ā¤œāĨ‡ā¤‚", - "search_no_more_result": "No more results", "search_no_people": "⤕āĨ‹ā¤ˆ ⤞āĨ‹ā¤— ā¤¨ā¤šāĨ€ā¤‚", - "search_no_result": "No results found, try a different search term or combination", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", "search_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤖āĨ‹ā¤œāĨ‡ā¤‚", "search_places": "⤏āĨā¤Ĩā¤žā¤¨ ⤖āĨ‹ā¤œāĨ‡ā¤‚", - "search_result_page_new_search_hint": "New Search", "search_state": "⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ ⤖āĨ‹ā¤œāĨ‡ā¤‚..āĨ¤", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", "search_timezone": "ā¤¸ā¤Žā¤¯ā¤•āĨā¤ˇāĨ‡ā¤¤āĨā¤° ⤖āĨ‹ā¤œāĨ‡ā¤‚..āĨ¤", "search_type": "ā¤¤ā¤˛ā¤žā¤ļ ⤕āĨ€ ā¤ĩā¤ŋ⤧ā¤ŋ", "search_your_photos": "⤅ā¤Ē⤍āĨ€ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤖āĨ‹ā¤œāĨ‡ā¤‚", @@ -1357,12 +1019,9 @@ "select_new_face": "ā¤¨ā¤¯ā¤ž ⤚āĨ‡ā¤šā¤°ā¤ž ⤚āĨā¤¨āĨ‡ā¤‚", "select_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤚āĨā¤¨āĨ‡ā¤‚", "select_trash_all": "⤟āĨā¤°āĨˆā¤ļ ⤑⤞ ā¤•ā¤ž ⤚⤝⤍ ⤕⤰āĨ‡ā¤‚", - "select_user_for_sharing_page_err_album": "Failed to create album", "selected": "⤚⤝⤍ā¤ŋ⤤", "send_message": "ā¤ŽāĨ‡ā¤¸āĨ‡ā¤œ ⤭āĨ‡ā¤œāĨ‡ā¤‚", "send_welcome_email": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤭āĨ‡ā¤œāĨ‡ā¤‚", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", "server_info_box_server_url": "⤏⤰āĨā¤ĩ⤰ URL", "server_offline": "⤏⤰āĨā¤ĩ⤰ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨", "server_online": "⤏⤰āĨā¤ĩ⤰ ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨", @@ -1374,85 +1033,26 @@ "set_date_of_birth": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "set_profile_picture": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", "set_slideshow_to_fullscreen": "⤏āĨā¤˛ā¤žā¤‡ā¤Ą ā¤ļāĨ‹ ⤕āĨ‹ ā¤Ģā¤ŧāĨā¤˛ā¤¸āĨā¤•āĨā¤°āĨ€ā¤¨ ā¤Ē⤰ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "setting_notifications_subtitle": "Adjust your notification preferences", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "ā¤¸ā¤Žā¤žā¤¯āĨ‹ā¤œā¤¨", - "settings_require_restart": "Please restart Immich to apply this setting", "settings_saved": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ⤕āĨ‹ ā¤¸ā¤šāĨ‡ā¤œā¤ž ā¤—ā¤¯ā¤ž", "share": "ā¤ļāĨ‡ā¤¯ā¤° ā¤•ā¤°ā¤¨ā¤ž", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", + "share_add_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "shared": "ā¤¸ā¤žā¤ā¤ž", "shared_album_activities_input_disable": "⤕āĨ‰ā¤ŽāĨ‡ā¤‚ā¤Ÿ ā¤Ąā¤ŋ⤜āĨ‡ā¤Ŧ⤞āĨā¤Ą ā¤šāĨˆ", "shared_album_activity_remove_content": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ⤇⤏ ⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "shared_album_activity_remove_title": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ ā¤šā¤Ÿā¤žā¤ā¤‚", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", "shared_by": "ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤žā¤ā¤ž", "shared_by_you": "⤆ā¤Ē⤕āĨ‡ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", "shared_link_edit_description_hint": "ā¤ļāĨ‡ā¤¯ā¤° ā¤ĩā¤ŋā¤ĩ⤰⤪ ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", "shared_link_edit_password_hint": "ā¤ļāĨ‡ā¤¯ā¤° ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚", "shared_link_edit_submit_button": "⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤞ā¤ŋ⤂⤕", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕ ā¤•ā¤ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍ ⤕⤰āĨ‡ā¤‚", "shared_links": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕", "shared_with_me": "ā¤ŽāĨ‡ā¤°āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "sharing": "ā¤ļāĨ‡ā¤¯ā¤°ā¤ŋ⤂⤗", "sharing_enter_password": "⤕āĨƒā¤Ēā¤¯ā¤ž ⤇⤏ ā¤ĒāĨƒā¤ˇāĨā¤  ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚āĨ¤", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", "sharing_sidebar_description": "ā¤¸ā¤žā¤‡ā¤Ąā¤Ŧā¤žā¤° ā¤ŽāĨ‡ā¤‚ ā¤ļāĨ‡ā¤¯ā¤°ā¤ŋ⤂⤗ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ā¤• ⤞ā¤ŋ⤂⤕ ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", "shift_to_permanent_delete": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⇧ ā¤Ļā¤Ŧā¤žā¤ā¤", "show_album_options": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ĩā¤ŋ⤕⤞āĨā¤Ē ā¤Ļā¤ŋā¤–ā¤žā¤ā¤", "show_all_people": "⤏⤭āĨ€ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤Ļā¤ŋā¤–ā¤žā¤“", @@ -1510,19 +1110,11 @@ "theme": "ā¤ĩā¤ŋ⤎⤝", "theme_selection": "ā¤ĨāĨ€ā¤Ž ⤚⤝⤍", "theme_selection_description": "⤆ā¤Ē⤕āĨ‡ ā¤ŦāĨā¤°ā¤žā¤‰ā¤œā¤ŧ⤰ ⤕āĨ€ ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋā¤•ā¤¤ā¤ž ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤ĨāĨ€ā¤Ž ⤕āĨ‹ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤ĒāĨā¤°ā¤•ā¤žā¤ļ ā¤¯ā¤ž ⤅⤂⤧āĨ‡ā¤°āĨ‡ ā¤Ē⤰ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", "theme_setting_colorful_interface_subtitle": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤰⤂⤗ ⤕āĨ‹ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤¸ā¤¤ā¤šāĨ‹ā¤‚ ā¤Ē⤰ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰āĨ‡ā¤‚", "theme_setting_colorful_interface_title": "⤰⤂⤗āĨ€ā¤¨ ā¤‡ā¤‚ā¤Ÿā¤°ā¤Ģā¤ŧāĨ‡ā¤¸", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", "theme_setting_primary_color_subtitle": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤“ā¤‚ ⤔⤰ ā¤‰ā¤šāĨā¤šā¤žā¤°ā¤ŖāĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ā¤• ⤰⤂⤗ ⤚āĨā¤¨āĨ‡ā¤‚", "theme_setting_primary_color_title": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤰⤂⤗", "theme_setting_system_primary_color_title": "⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ⤰⤂⤗ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", "they_will_be_merged_together": "⤇⤍āĨā¤šāĨ‡ā¤‚ ā¤ā¤• ā¤¸ā¤žā¤Ĩ ā¤Žā¤ŋā¤˛ā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž", "time_based_memories": "ā¤¸ā¤Žā¤¯ ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ā¤¯ā¤žā¤ĻāĨ‡ā¤‚", "timezone": "ā¤¸ā¤Žā¤¯ ⤕āĨā¤ˇāĨ‡ā¤¤āĨā¤°", @@ -1539,13 +1131,9 @@ "trash_delete_asset": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤟āĨā¤°āĨˆā¤ļ/ā¤Ąā¤ŋ⤞āĨ€ā¤Ÿ ⤕⤰āĨ‡ā¤‚", "trash_emptied": "ā¤•ā¤šā¤°ā¤ž ā¤–ā¤žā¤˛āĨ€ ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž", "trash_no_results_message": "⤟āĨā¤°āĨˆā¤ļ ⤕āĨ€ ā¤—ā¤ˆ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤¯ā¤šā¤žā¤‚ ā¤Ļā¤ŋā¤–ā¤žā¤ˆ ā¤ĻāĨ‡ā¤‚⤗āĨ‡āĨ¤", - "trash_page_delete_all": "Delete All", "trash_page_empty_trash_dialog_content": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ⤅ā¤Ē⤍āĨ€ ⤕āĨ‚ā¤Ąā¤ŧāĨ‡ā¤Ļā¤žā¤¨ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤–ā¤žā¤˛āĨ€ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤇⤍ ā¤†ā¤‡ā¤Ÿā¤ŽāĨ‹ā¤‚ ⤕āĨ‹ Immich ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", "trash_page_restore_all": "⤏⤭āĨ€ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤ƒ ⤏āĨā¤Ĩā¤žā¤¨ā¤žā¤‚ā¤¤ā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "trash_page_select_assets_btn": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ⤚⤝⤍ ⤕⤰āĨ‡ā¤‚", - "trash_page_title": "Trash ({})", "type": "ā¤ĒāĨā¤°ā¤•ā¤žā¤°", "unarchive": "⤏⤂⤗āĨā¤°ā¤š ⤏āĨ‡ ⤍ā¤ŋā¤•ā¤žā¤˛āĨ‡ā¤‚", "unfavorite": "ā¤¨ā¤žā¤Ē⤏⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚", @@ -1567,17 +1155,12 @@ "updated_password": "⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", "upload": "ā¤Ąā¤žā¤˛ā¤¨ā¤ž", "upload_concurrency": "ā¤¸ā¤Žā¤ĩ⤰āĨā¤¤āĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰āĨ‡ā¤‚", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", "upload_status_duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ", "upload_status_errors": "⤤āĨā¤°āĨā¤Ÿā¤ŋā¤¯ā¤žā¤", "upload_status_uploaded": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕ā¤ŋā¤ ā¤—ā¤", "upload_success": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏ā¤Ģ⤞ ā¤°ā¤šā¤ž, ⤍⤈ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤‚ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ĒāĨ‡ā¤œ ⤕āĨ‹ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ⤕⤰āĨ‡ā¤‚āĨ¤", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", "url": "⤝āĨ‚ā¤†ā¤°ā¤ā¤˛", "usage": "ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤—", - "use_current_connection": "use current connection", "use_custom_date_range": "⤇⤏⤕āĨ‡ ā¤Ŧā¤œā¤žā¤¯ ⤕⤏āĨā¤Ÿā¤Ž ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ⤏āĨ€ā¤Žā¤ž ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚", "user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž", "user_id": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ēā¤šā¤šā¤žā¤¨", @@ -1589,16 +1172,10 @@ "users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂", "utilities": "⤉ā¤Ē⤝āĨ‹ā¤—ā¤ŋā¤¤ā¤žā¤“ā¤‚", "validate": "ā¤Žā¤žā¤¨āĨā¤¯", - "validate_endpoint_error": "Please enter a valid URL", "variables": "⤚⤰", "version": "⤏⤂⤏āĨā¤•⤰⤪", "version_announcement_closing": "⤆ā¤Ēā¤•ā¤ž ā¤Žā¤ŋ⤤āĨā¤°, ā¤ā¤˛āĨ‡ā¤•āĨā¤¸", "version_announcement_message": "ā¤¨ā¤Žā¤¸āĨā¤•ā¤žā¤° ā¤Žā¤ŋ⤤āĨā¤°, ā¤ā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ļ⤍ ā¤•ā¤ž ā¤ā¤• ā¤¨ā¤¯ā¤ž ⤏⤂⤏āĨā¤•⤰⤪ ā¤šāĨˆ, ⤕āĨƒā¤Ēā¤¯ā¤ž ⤅ā¤Ēā¤¨ā¤ž ā¤¸ā¤Žā¤¯ ⤍ā¤ŋā¤•ā¤žā¤˛ā¤•ā¤° ⤇⤏āĨ‡ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚ ⤰ā¤ŋ⤞āĨ€ā¤œ ⤍āĨ‹ā¤ŸāĨā¤¸ ⤔⤰ ⤅ā¤Ēā¤¨ā¤ž ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ docker-compose.yml, ⤔⤰ .env ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ⤗⤞⤤ ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰āĨ‡ā¤ļ⤍ ⤕āĨ‹ ⤰āĨ‹ā¤•⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏āĨ‡ā¤Ÿā¤…ā¤Ē ⤅ā¤ĻāĨā¤¯ā¤¤ā¤ŋ⤤ ā¤šāĨˆ, ā¤–ā¤žā¤¸ā¤•ā¤° ⤝ā¤Ļā¤ŋ ⤆ā¤Ē ā¤ĩāĨ‰ā¤šā¤Ÿā¤žā¤ĩ⤰ ā¤¯ā¤ž ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ⤤⤂⤤āĨā¤° ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤤āĨ‡ ā¤šāĨˆā¤‚ ⤜āĨ‹ ⤆ā¤Ē⤕āĨ‡ ā¤ā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ļ⤍ ⤕āĨ‹ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤•ā¤ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available 🎉", "video": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", "video_hover_setting": "ā¤šāĨ‹ā¤ĩ⤰ ā¤Ē⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤šā¤˛ā¤žā¤ā¤‚", "video_hover_setting_description": "⤜ā¤Ŧ ā¤Žā¤žā¤‰ā¤¸ ā¤†ā¤‡ā¤Ÿā¤Ž ā¤Ē⤰ ⤘āĨ‚ā¤Ž ā¤°ā¤šā¤ž ā¤šāĨ‹ ⤤āĨ‹ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤šā¤˛ā¤žā¤ā¤‚āĨ¤", diff --git a/i18n/hr.json b/i18n/hr.json index 53a7cc2640..5a2d259eb2 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -4,6 +4,7 @@ "account_settings": "Postavke računa", "acknowledge": "Potvrdi", "action": "Akcija", + "action_common_update": "AÅžuriranje", "actions": "Akcije", "active": "Aktivno", "activity": "Aktivnost", @@ -13,6 +14,7 @@ "add_a_location": "Dodaj lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_endpoint": "Dodaj krajnju točnu", "add_exclusion_pattern": "Dodaj uzorak izuzimanja", "add_import_path": "Dodaj import folder", "add_location": "Dodaj lokaciju", @@ -20,8 +22,11 @@ "add_partner": "Dodaj partnera", "add_path": "Dodaj putanju", "add_photos": "Dodaj slike", - "add_to": "Dodaj u...", + "add_to": "Dodaj uâ€Ļ", "add_to_album": "Dodaj u album", + "add_to_album_bottom_sheet_added": "Dodano u {album}", + "add_to_album_bottom_sheet_already_exists": "Već u {album}", + "add_to_locked_folder": "Dodaj zaključanu mapu", "add_to_shared_album": "Dodaj u dijeljeni album", "add_url": "Dodaj URL", "added_to_archive": "Dodano u arhivu", @@ -35,19 +40,21 @@ "authentication_settings_disable_all": "Jeste li sigurni da Åželite onemogućenit sve načine prijave? Prijava će biti potpuno onemogućena.", "authentication_settings_reenable": "Za ponovno uključivanje upotrijebite naredbu posluÅžitelja.", "background_task_job": "Pozadinski zadaci", - "backup_database": "Sigurnosna kopija baze podataka", + "backup_database": "Kreiraj sigurnosnu kopiju baze podataka", "backup_database_enable_description": "Omogućite sigurnosne kopije baze podataka", "backup_keep_last_amount": "Količina prethodnih sigurnosnih kopija za čuvanje", "backup_settings": "Postavke sigurnosne kopije", - "backup_settings_description": "Upravljanje postavkama sigurnosne kopije baze podataka", + "backup_settings_description": "Upravljajte postavkama izvoza baze podataka. Napomena: Ovi zadaci nisu nadzirani i nećete biti obavijeÅĄteni u slučaju neuspjeha.", "check_all": "Provjeri sve", + "cleanup": "ČiÅĄÄ‡enje", "cleared_jobs": "Izbrisani poslovi za: {job}", "config_set_by_file": "Konfiguracija je trenutno postavljena konfiguracijskom datotekom", "confirm_delete_library": "Jeste li sigurni da Åželite izbrisati biblioteku {library}?", - "confirm_delete_library_assets": "Jeste li sigurni da Åželite izbrisati ovu biblioteku? Time će se izbrisati sva {count} sadrÅžana sredstva iz Immicha i ne moÅže se poniÅĄtiti. Datoteke će ostati na disku.", + "confirm_delete_library_assets": "Jeste li sigurni da Åželite izbrisati ovu biblioteku? Time će {count, plural, one {biti izbrisana # sadrÅžana stavka} few {biti izbrisane sve # sadrÅžane stavke} other {biti izbrisano svih # sadrÅžanih stavki}} iz Immicha i to se ne moÅže poniÅĄtiti. Datoteke će ostati na disku.", "confirm_email_below": "Za potvrdu upiÅĄite \"{email}\" ispod", "confirm_reprocess_all_faces": "Jeste li sigurni da Åželite ponovno obraditi sva lica? Ovo će također obrisati imenovane osobe.", "confirm_user_password_reset": "Jeste li sigurni da Åželite poniÅĄtiti lozinku korisnika {user}?", + "confirm_user_pin_code_reset": "Jeste li sigurni da Åželite resetirati PIN korisnika {user}?", "create_job": "Izradi zadatak", "cron_expression": "Cron izraz (expression)", "cron_expression_description": "Postavite interval skeniranja koristeći cron format. Za viÅĄe informacija pogledajte npr. Crontab Guru", @@ -65,8 +72,13 @@ "forcing_refresh_library_files": "Prisilno osvjeÅžavanje svih datoteka knjiÅžnice", "image_format": "Format", "image_format_description": "WebP proizvodi manje datoteke od JPEG-a, ali se sporije kodira.", + "image_fullsize_description": "Slika pune veličine bez meta podataka, koristi se prilikom zumiranja", + "image_fullsize_enabled": "Omogući generiranje slike pune veličine", + "image_fullsize_enabled_description": "Generiraj sliku pune veličine za formate koji nisu prilagođeni webu. Kada je opcija \"Preferiraj ugrađeni pregled\" omogućena, ugrađeni pregledi koriste se izravno bez konverzije. Ne utječe na formate prilagođene webu kao ÅĄto je JPEG.", + "image_fullsize_quality_description": "Kvaliteta slike pune veličine od 1 do 100. Veća vrijednost znači bolja kvaliteta, ali stvara veće datoteke.", + "image_fullsize_title": "Postavke slike pune veličine", "image_prefer_embedded_preview": "Preferiraj ugrađeni pregled", - "image_prefer_embedded_preview_setting_description": "Koristite ugrađene preglede u RAW fotografije kao ulaz za obradu slike kada su dostupni. To moÅže proizvesti preciznije boje za neke slike, ali kvaliteta pregleda ovisi o kameri i slika moÅže imati viÅĄe artefakata kompresije.", + "image_prefer_embedded_preview_setting_description": "Koristite ugrađene preglede u RAW fotografije kao ulaz za obradu slike kada su dostupni. To moÅže proizvesti preciznije boje za neke slike, ali kvaliteta pregleda ovisi o kameri i slika moÅže imati viÅĄe artifakta kompresije.", "image_prefer_wide_gamut": "Preferirajte ÅĄiroku gamu", "image_prefer_wide_gamut_setting_description": "Koristite Display P3 za sličice. Ovo bolje čuva Åživost slika sa ÅĄirokim prostorima boja, ali slike mogu izgledati drugačije na starim uređajima sa starom verzijom preglednika. sRGB slike čuvaju se kao sRGB kako bi se izbjegle promjene boja.", "image_preview_description": "Slika srednje veličine s ogoljenim metapodacima, koristi se prilikom pregledavanja jednog sredstva i za strojno učenje", @@ -86,7 +98,7 @@ "job_settings": "Postavke posla", "job_settings_description": "Upravljajte istovremenoÅĄÄ‡u poslova", "job_status": "Status posla", - "jobs_delayed": "{jobCount, plural, other {# delayed}}", + "jobs_delayed": "{jobCount, plural, other {# odgođenih}}", "jobs_failed": "{jobCount, plural, other {# failed}}", "library_created": "Stvorena biblioteka: {library}", "library_deleted": "Biblioteka izbrisana", @@ -96,7 +108,7 @@ "library_scanning_enable_description": "Omogući periodično skeniranje biblioteke", "library_settings": "Externa biblioteka", "library_settings_description": "Upravljajte postavkama vanjske biblioteke", - "library_tasks_description": "Obavljati bibliotekne zadatke", + "library_tasks_description": "Skeniraj eksterne biblioteke za nove i/ili promijenjene resurse", "library_watching_enable_description": "Pratite vanjske biblioteke za promjena datoteke", "library_watching_settings": "Gledanje biblioteke (EKSPERIMENTALNO)", "library_watching_settings_description": "Automatsko praćenje promijenjenih datoteke", @@ -131,7 +143,7 @@ "machine_learning_smart_search_description": "PretraÅžujte slike semantički koristeći CLIP ugradnje", "machine_learning_smart_search_enabled": "Omogući pametno pretraÅživanje", "machine_learning_smart_search_enabled_description": "Ako je onemogućeno, slike neće biti kodirane za pametno pretraÅživanje.", - "machine_learning_url_description": "URL posluÅžitelja strojnog učenja. Ako ste dodali viÅĄe od jednog URLa, svaki server će biti kontaktiraj jedanput dok jedan ne odgovori uspjeÅĄno, u redu od prvog do zadnjeg.", + "machine_learning_url_description": "URL posluÅžitelja strojnog učenja. Ako ste dodali viÅĄe od jednog URLa, svaki server će biti kontaktiraj jedanput dok jedan ne odgovori uspjeÅĄno, u redu od prvog do zadnjeg. Serveri koji ne odgovore će privremeno biti ignorirani dok ponovo ne postanu dostupni.", "manage_concurrency": "Upravljanje IstovremenoÅĄÄ‡u", "manage_log_settings": "Upravljanje postavkama zapisivanje", "map_dark_style": "Tamni stil", @@ -147,6 +159,8 @@ "map_settings": "Karta", "map_settings_description": "Upravljanje postavkama karte", "map_style_description": "URL na style.json temu karte", + "memory_cleanup_job": "ČiÅĄÄ‡enje memorije", + "memory_generate_job": "Generiranje memorije", "metadata_extraction_job": "Izdvoj metapodatke", "metadata_extraction_job_description": "Izdvojite podatke o metapodacima iz svakog sredstva, kao ÅĄto su GPS, lica i rezolucija", "metadata_faces_import_setting": "Omogući uvoz lica", @@ -180,26 +194,21 @@ "oauth_auto_register": "Automatska registracija", "oauth_auto_register_description": "Automatski registrirajte nove korisnike nakon prijave s OAuth", "oauth_button_text": "Tekst gumba", - "oauth_client_id": "ID Klijenta", - "oauth_client_secret": "Tajna Klijenta", "oauth_enable_description": "Prijavite se putem OAutha", - "oauth_issuer_url": "URL Izdavatelja", "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "Nadjačavanje URI-preusmjeravanja za mobilne uređaje", "oauth_mobile_redirect_uri_override_description": "Omogući kada pruÅžatelj OAuth ne dopuÅĄta mobilni URI, poput '{callback}'", - "oauth_profile_signing_algorithm": "Algoritam za potpisivanje profila", - "oauth_profile_signing_algorithm_description": "Algoritam koji se koristi za potpisivanje korisničkog profila.", - "oauth_scope": "Opseg", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje postavkama za prijavu kroz OAuth", "oauth_settings_more_details": "Za viÅĄe pojedinosti o ovoj značajci pogledajte uputstva.", - "oauth_signing_algorithm": "Algoritam potpisivanja", "oauth_storage_label_claim": "PotraÅživanje oznake za pohranu", "oauth_storage_label_claim_description": "Automatski postavite korisničku oznaku za pohranu na vrijednost ovog zahtjeva.", "oauth_storage_quota_claim": "Zahtjev za kvotom pohrane", "oauth_storage_quota_claim_description": "Automatski postavite korisničku kvotu pohrane na vrijednost ovog zahtjeva.", "oauth_storage_quota_default": "Zadana kvota pohrane (GiB)", "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva (unesite 0 za neograničenu kvotu).", + "oauth_timeout": "Istek vremena zahtjeva", + "oauth_timeout_description": "Istek vremena zahtjeva je u milisekundama", "offline_paths": "IzvanmreÅžne putanje", "offline_paths_description": "Ovi rezultati mogu biti posljedica ručnog brisanja datoteka koje nisu dio vanjske biblioteke.", "password_enable_description": "Prijava s email adresom i zaporkom", @@ -239,7 +248,7 @@ "storage_template_hash_verification_enabled_description": "Omogućuje hash provjeru, nemojte je onemogućiti osim ako niste sigurni u implikacije", "storage_template_migration": "Migracija predloÅĄka za pohranu", "storage_template_migration_description": "Primijenite trenutni {template} na prethodno prenesena sredstva", - "storage_template_migration_info": "Promjene predloÅĄka primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloÅĄka na prethodno prenesena sredstva, pokrenite {job}.", + "storage_template_migration_info": "PredloÅžak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloÅĄka primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloÅĄka na prethodno prenesena sredstva, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije PredloÅĄka Pohrane", "storage_template_more_details": "Za viÅĄe pojedinosti o ovoj značajci pogledajte PredloÅžak pohrane i njegove implikacije", "storage_template_onboarding_description": "Kada je omogućena, ova će značajka automatski organizirati datoteke na temelju korisnički definiranog predloÅĄka. Zbog problema sa stabilnoÅĄÄ‡u značajka je isključena prema zadanim postavkama. Za viÅĄe informacija pogledajte dokumentaciju.", @@ -250,6 +259,15 @@ "system_settings": "Postavke Sustava", "tag_cleanup_job": "ČiÅĄÄ‡enje oznaka", "template_email_available_tags": "MoÅžete koristiti sljedeće varijable u vaÅĄem predloÅĄku:{tags}", + "template_email_if_empty": "Ukoliko je predloÅžak prazan, koristit će se zadana e-mail adresa.", + "template_email_invite_album": "PredloÅžak za pozivnicu u album", + "template_email_preview": "Pregled", + "template_email_settings": "E-mail PredloÅĄci", + "template_email_settings_description": "Upravljanje prilagođenim predloÅĄcima za obavijesti putem e-maila", + "template_email_update_album": "AÅžuriraj Album PredloÅžak", + "template_email_welcome": "PredloÅžak e-maila dobrodoÅĄlice", + "template_settings": "PredloÅžak Obavijesti", + "template_settings_description": "Upravljaj prilagođenim predloÅĄcima za obavijesti.", "theme_custom_css_settings": "Prilagođeni CSS", "theme_custom_css_settings_description": "Kaskadni listovi stilova (CSS) omogućuju prilagođavanje dizajna Immicha.", "theme_settings": "Postavke tema", @@ -279,6 +297,8 @@ "transcoding_constant_rate_factor": "Faktor konstantne stope (-crf)", "transcoding_constant_rate_factor_description": "Razina kvalitete videa. Uobičajene vrijednosti su 23 za H.264, 28 za HEVC, 31 za VP9 i 35 za AV1. NiÅže je bolje, ali stvara veće datoteke.", "transcoding_disabled_description": "Nemojte transkodirati nijedan videozapis, moÅže prekinuti reprodukciju na nekim klijentima", + "transcoding_encoding_options": "Opcije Kodiranja", + "transcoding_encoding_options_description": "Postavi kodeke, rezoluciju, kvalitetu i druge opcije za kodirane videje", "transcoding_hardware_acceleration": "Hardversko Ubrzanje", "transcoding_hardware_acceleration_description": "Eksperimentalno; puno brÅže, ali će imati niÅžu kvalitetu pri istoj bitrate postavci", "transcoding_hardware_decoding": "Hardversko dekodiranje", @@ -291,6 +311,8 @@ "transcoding_max_keyframe_interval": "Maksimalni interval ključnih sličica", "transcoding_max_keyframe_interval_description": "Postavlja maksimalnu udaljenost slika između ključnih kadrova. NiÅže vrijednosti pogorÅĄavaju učinkovitost kompresije, ali poboljÅĄavaju vrijeme traÅženja i mogu poboljÅĄati kvalitetu u scenama s brzim kretanjem. 0 automatski postavlja ovu vrijednost.", "transcoding_optimal_description": "Videozapisi koji su veći od ciljne rezolucije ili nisu u prihvatljivom formatu", + "transcoding_policy": "Politika Transkodiranja", + "transcoding_policy_description": "Postavi kada će video biti transkodiran", "transcoding_preferred_hardware_device": "Preferirani hardverski uređaj", "transcoding_preferred_hardware_device_description": "Odnosi se samo na VAAPI i QSV. Postavlja dri node koji se koristi za hardversko transkodiranje.", "transcoding_preset_preset": "Preset (-preset)", @@ -299,7 +321,7 @@ "transcoding_reference_frames_description": "Broj slika za referencu prilikom komprimiranja određene slike. ViÅĄe vrijednosti poboljÅĄavaju učinkovitost kompresije, ali usporavaju kodiranje. 0 automatski postavlja ovu vrijednost.", "transcoding_required_description": "Samo videozapisi koji nisu u prihvaćenom formatu", "transcoding_settings": "Postavke Video Transkodiranja", - "transcoding_settings_description": "Upravljajte informacijama o razlučivosti i kodiranju video datoteka", + "transcoding_settings_description": "Upravljaj koji videozapisi će se transkodirati i kako ih obraditi", "transcoding_target_resolution": "Ciljana rezolucija", "transcoding_target_resolution_description": "Veće razlučivosti mogu sačuvati viÅĄe detalja, ali trebaju dulje za kodiranje, imaju veće veličine datoteka i mogu smanjiti odziv aplikacije.", "transcoding_temporal_aq": "Vremenski AQ", @@ -312,7 +334,7 @@ "transcoding_transcode_policy_description": "Pravila o tome kada se video treba transkodirati. HDR videozapisi uvijek će biti transkodirani (osim ako je transkodiranje onemogućeno).", "transcoding_two_pass_encoding": "Kodiranje u dva prolaza", "transcoding_two_pass_encoding_setting_description": "Transkodiranje u dva prolaza za proizvodnju bolje kodiranih videozapisa. Kada je omogućena maksimalna brzina prijenosa (potrebna za rad s H.264 i HEVC), ovaj način rada koristi raspon brzine prijenosa na temelju maksimalne brzine prijenosa i zanemaruje CRF. Za VP9, CRF se moÅže koristiti ako je maksimalna brzina prijenosa onemogućena.", - "transcoding_video_codec": "Video Kodek", + "transcoding_video_codec": "Video kodek", "transcoding_video_codec_description": "VP9 ima visoku učinkovitost i web-kompatibilnost, ali treba dulje za transkodiranje. HEVC ima sličnu izvedbu, ali ima slabiju web kompatibilnost. H.264 ÅĄiroko je kompatibilan i brzo se transkodira, ali proizvodi mnogo veće datoteke. AV1 je najučinkovitiji kodek, ali nema podrÅĄku na starijim uređajima.", "trash_enabled_description": "Omogućite značajke Smeća", "trash_number_of_days": "Broj dana", @@ -327,45 +349,74 @@ "user_delete_delay_settings_description": "Broj dana nakon uklanjanja za trajno brisanje korisničkog računa i imovine. Posao brisanja korisnika pokreće se u ponoć kako bi se provjerili korisnici koji su spremni za brisanje. Promjene ove postavke bit će procijenjene pri sljedećem izvrÅĄavanju.", "user_delete_immediately": "Račun i sredstva korisnika {user} bit će stavljeni u red čekanja za trajno brisanje odmah.", "user_delete_immediately_checkbox": "Stavite korisnika i imovinu u red za trenutačno brisanje", + "user_details": "Detalji korisnika", "user_management": "Upravljanje Korisnicima", "user_password_has_been_reset": "Korisnička lozinka je poniÅĄtena:", "user_password_reset_description": "Molimo dostavite privremenu lozinku korisniku i obavijestite ga da će morati promijeniti lozinku pri sljedećoj prijavi.", "user_restore_description": "Račun korisnika {user} bit će vraćen.", "user_restore_scheduled_removal": "Vrati korisnika - zakazano uklanjanje {date, date, long}", - "user_settings": "Korisničke Postavke", + "user_settings": "Korisničke postavke", "user_settings_description": "Upravljanje korisničkim postavkama", "user_successfully_removed": "Korisnik {email} je uspjeÅĄno uklonjen.", "version_check_enabled_description": "Omogući provjeru verzije", "version_check_implications": "Značajka provjere verzije oslanja se na periodičnu komunikaciju s github.com", - "version_check_settings": "Provjera Verzije", + "version_check_settings": "Provjera verzije", "version_check_settings_description": "Omogućite/onemogućite obavijest o novoj verziji", "video_conversion_job": "Transkodiranje videozapisa", "video_conversion_job_description": "Transkodiranje videozapisa za veću kompatibilnost s preglednicima i uređajima" }, "admin_email": "E-poÅĄta administratora", - "admin_password": "Admin Lozinka", + "admin_password": "Admin lozinka", "administration": "Administracija", "advanced": "Napredno", - "age_months": "Dob {months, plural, one {# month} other {# months}}", - "age_year_months": "Dob 1 godina, {months, plural, one {# month} other {# months}}", - "age_years": "{years, plural, other {Age #}}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. PokuÅĄajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", + "advanced_settings_log_level_title": "Razina zapisivanja: {level}", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s resursa na uređaju. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", + "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mreÅžnim zahtjevom.", + "advanced_settings_proxy_headers_title": "Proxy zaglavlja", + "advanced_settings_self_signed_ssl_subtitle": "Preskoči provjeru SSL certifikata za krajnju točku posluÅžitelja. Potrebno za samo-potpisane certifikate.", + "advanced_settings_self_signed_ssl_title": "Dopusti samo-potpisane SSL certifikate", + "advanced_settings_sync_remote_deletions_subtitle": "Automatski izbriÅĄi ili obnovi resurs na ovom uređaju kada se ta radnja izvrÅĄi na webu", + "advanced_settings_sync_remote_deletions_title": "Sinkroniziraj udaljena brisanja [EKSPERIMENTALNO]", + "advanced_settings_tile_subtitle": "Postavke za napredne korisnike", + "advanced_settings_troubleshooting_subtitle": "Omogući dodatne značajke za rjeÅĄavanje problema", + "advanced_settings_troubleshooting_title": "RjeÅĄavanje problema", + "age_months": "Dob {months, plural, one {# mjesec} other {# mjeseca}}", + "age_year_months": "Dob 1 godina, {months, plural, one {# mjesec} other {# mjeseca}}", + "age_years": "{years, plural, other {Dob #}}", "album_added": "Album dodan", "album_added_notification_setting_description": "Primite obavijest e-poÅĄtom kada ste dodani u dijeljeni album", "album_cover_updated": "Naslovnica albuma aÅžurirana", "album_delete_confirmation": "Jeste li sigurni da Åželite izbrisati album {album}?", "album_delete_confirmation_description": "Ako se ovaj album dijeli, drugi korisnici mu viÅĄe neće moći pristupiti.", + "album_info_card_backup_album_excluded": "IZUZETO", + "album_info_card_backup_album_included": "UKLJUČENO", "album_info_updated": "Podaci o albumu aÅžurirani", "album_leave": "Napustiti album?", "album_leave_confirmation": "Jeste li sigurni da Åželite napustiti {album}?", - "album_name": "Naziv Albuma", + "album_name": "Naziv albuma", "album_options": "Opcije albuma", "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da Åželite ukloniti {user}?", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", + "album_thumbnail_card_item": "1 stavka", + "album_thumbnail_card_items": "{count} stavki", + "album_thumbnail_card_shared": " ¡ Podijeljeno", + "album_thumbnail_shared_by": "Podijeljeno sa {user}", "album_updated": "Album aÅžuriran", "album_updated_setting_description": "Primite obavijest e-poÅĄtom kada dijeljeni album ima nova sredstva", "album_user_left": "NapuÅĄten {album}", "album_user_removed": "Uklonjen {user}", + "album_viewer_appbar_delete_confirm": "Jeste li sigurni da Åželite izbrisati ovaj album s vaÅĄeg računa?", + "album_viewer_appbar_share_err_delete": "NeuspjeÅĄno brisanje albuma", + "album_viewer_appbar_share_err_leave": "NeuspjeÅĄno napuÅĄtanje albuma", + "album_viewer_appbar_share_err_remove": "Postoje problemi s uklanjanjem resursa iz albuma", + "album_viewer_appbar_share_err_title": "NeuspjeÅĄno mijenjanje naslova albuma", + "album_viewer_appbar_share_leave": "Napusti album", + "album_viewer_appbar_share_to": "Podijeli s", + "album_viewer_page_share_add_users": "Dodaj korisnike", "album_with_link_access": "Dopusti svima s poveznicom pristup fotografijama i osobama u ovom albumu.", "albums": "Albumi", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumi}}", @@ -377,47 +428,136 @@ "allow_edits": "Dozvoli izmjene", "allow_public_user_to_download": "Dopusti javnom korisniku preuzimanje", "allow_public_user_to_upload": "Dopusti javnom korisniku učitavanje", + "alt_text_qr_code": "Slika QR koda", "anti_clockwise": "Suprotno smjeru kazaljke na satu", "api_key": "API Ključ", "api_key_description": "Ova će vrijednost biti prikazana samo jednom. Obavezno ju kopirajte prije zatvaranja prozora.", "api_key_empty": "Naziv vaÅĄeg API ključa ne smije biti prazan", "api_keys": "API Ključevi", + "app_bar_signout_dialog_content": "Jeste li sigurni da se Åželite odjaviti?", + "app_bar_signout_dialog_ok": "Da", + "app_bar_signout_dialog_title": "Odjavi se", "app_settings": "Postavke Aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", + "archive_page_no_archived_assets": "Nema arhiviranih resursa", + "archive_page_title": "Arhiviraj ({count})", "archive_size": "Veličina arhive", "archive_size_description": "Konfigurirajte veličinu arhive za preuzimanja (u GiB)", - "archived_count": "{count, plural, other {Archived #}}", + "archived": "Ahrivirano", "are_these_the_same_person": "Je li ovo ista osoba?", "are_you_sure_to_do_this": "Jeste li sigurni da to Åželite učiniti?", + "asset_action_delete_err_read_only": "Nije moguće izbrisati resurse samo za čitanje, preskačem", + "asset_action_share_err_offline": "Nije moguće dohvatiti izvanmreÅžne resurse, preskačem", "asset_added_to_album": "Dodano u album", - "asset_adding_to_album": "Dodavanje u album...", + "asset_adding_to_album": "Dodavanje u albumâ€Ļ", "asset_description_updated": "Opis imovine je aÅžuriran", "asset_filename_is_offline": "Sredstvo {filename} je izvan mreÅže", "asset_has_unassigned_faces": "Materijal ima nedodijeljena lica", - "asset_hashing": "Hashiranje...", + "asset_hashing": "SaÅžimanjeâ€Ļ", + "asset_list_group_by_sub_title": "Grupiraj po", + "asset_list_layout_settings_dynamic_layout_title": "Dinamički raspored", + "asset_list_layout_settings_group_automatically": "Automatski", + "asset_list_layout_settings_group_by": "Grupiraj resurse po", + "asset_list_layout_settings_group_by_month_day": "Mjesec + dan", + "asset_list_layout_sub_title": "Raspored", + "asset_list_settings_subtitle": "Postavke izgleda mreÅže fotografija", + "asset_list_settings_title": "MreÅža Fotografija", "asset_offline": "Sredstvo izvan mreÅže", "asset_offline_description": "Ovaj materijal je izvan mreÅže. Immich ne moÅže pristupiti lokaciji datoteke. Provjerite je li sredstvo dostupno, a zatim ponovno skenirajte biblioteku.", + "asset_restored_successfully": "Resurs uspjeÅĄno obnovljen", "asset_skipped": "Preskočeno", "asset_skipped_in_trash": "U smeću", "asset_uploaded": "Učitano", - "asset_uploading": "Učitavanje...", + "asset_uploading": "Å aljemâ€Ļ", + "asset_viewer_settings_subtitle": "Upravljajte postavkama preglednika vaÅĄe galerije", + "asset_viewer_settings_title": "Preglednik Resursa", "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", "assets_added_to_name_count": "Dodano {count, plural, one {# asset} other {# assets}} u {hasName, select, true {{name}} other {new album}}", - "assets_count": "{count, plural, one {# asset} other {# assets}}", + "assets_deleted_permanently": "{count} resurs(i) uspjeÅĄno uklonjeni", + "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich posluÅžitelja", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premjeÅĄteno u smeće", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", - "assets_restore_confirmation": "Jeste li sigurni da Åželite vratiti sve svoje resurse bačene u otpad? Ne moÅžete poniÅĄtiti ovu radnju!", + "assets_removed_permanently_from_device": "{count} resurs(i) trajno uklonjen(i) s vaÅĄeg uređaja", + "assets_restore_confirmation": "Jeste li sigurni da Åželite obnoviti sve svoje resurse bačene u otpad? Ne moÅžete poniÅĄtiti ovu radnju! Imajte na umu da se bilo koji izvanmreÅžni resursi ne mogu obnoviti na ovaj način.", "assets_restored_count": "Vraćeno {count, plural, one {# asset} other {# assets}}", + "assets_restored_successfully": "{count} resurs(i) uspjeÅĄno obnovljen(i)", + "assets_trashed": "{count} resurs(i) premjeÅĄten(i) u smeće", "assets_trashed_count": "Bačeno u smeće {count, plural, one {# asset} other {# assets}}", + "assets_trashed_from_server": "{count} resurs(i) premjeÅĄten(i) u smeće s Immich posluÅžitelja", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} već dio albuma", "authorized_devices": "OvlaÅĄteni Uređaji", + "automatic_endpoint_switching_subtitle": "PoveÅžite se lokalno preko naznačene Wi-Fi mreÅže kada je dostupna i koristite alternativne veze na drugim lokacijama", + "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", "back": "Nazad", "back_close_deselect": "Natrag, zatvorite ili poniÅĄtite odabir", + "background_location_permission": "Dozvola za lokaciju u pozadini", + "background_location_permission_content": "Kako bi prebacivao mreÅže dok radi u pozadini, Immich mora *uvijek* imati pristup preciznoj lokaciji kako bi aplikacija mogla pročitati naziv Wi-Fi mreÅže", + "backup_album_selection_page_albums_device": "Albumi na uređaju ({count})", + "backup_album_selection_page_albums_tap": "Dodirnite za uključivanje, dvostruki dodir za isključivanje", + "backup_album_selection_page_assets_scatter": "Resursi mogu biti raspoređeni u viÅĄe albuma. Stoga, albumi mogu biti uključeni ili isključeni tijekom procesa sigurnosnog kopiranja.", + "backup_album_selection_page_select_albums": "Odabrani albumi", + "backup_album_selection_page_selection_info": "Informacije o odabiru", + "backup_album_selection_page_total_assets": "Ukupan broj jedinstvenih resursa", + "backup_all": "Sve", + "backup_background_service_backup_failed_message": "NeuspjeÅĄno sigurnosno kopiranje resursa. PokuÅĄavam ponovoâ€Ļ", + "backup_background_service_connection_failed_message": "NeuspjeÅĄno povezivanje s posluÅžiteljem. PokuÅĄavam ponovoâ€Ļ", + "backup_background_service_current_upload_notification": "Å aljem {filename}", + "backup_background_service_default_notification": "Provjera novih resursaâ€Ļ", + "backup_background_service_error_title": "PogreÅĄka pri sigurnosnom kopiranju", + "backup_background_service_in_progress_notification": "Sigurnosno kopiranje vaÅĄih resursaâ€Ļ", + "backup_background_service_upload_failure_notification": "NeuspjeÅĄno slanje {filename}", + "backup_controller_page_albums": "Sigurnosno kopiranje albuma", + "backup_controller_page_background_app_refresh_disabled_content": "Omogućite osvjeÅžavanje aplikacije u pozadini u Postavke > Opće Postavke > OsvjeÅžavanje Aplikacija u Pozadini kako biste koristili sigurnosno kopiranje u pozadini.", + "backup_controller_page_background_app_refresh_disabled_title": "OsvjeÅžavanje aplikacija u pozadini je onemogućeno", + "backup_controller_page_background_app_refresh_enable_button_text": "Idite u postavke", + "backup_controller_page_background_battery_info_link": "PokaÅži mi kako", + "backup_controller_page_background_battery_info_message": "Za najbolje iskustvo sigurnosnog kopiranja u pozadini, molimo onemogućite sve optimizacije baterije koje ograničavaju pozadinsku aktivnost Immicha.\n\nBudući da je ovo specifično za uređaj, molimo potraÅžite potrebne informacije za proizvođača vaÅĄeg uređaja.", + "backup_controller_page_background_battery_info_ok": "U redu", + "backup_controller_page_background_battery_info_title": "Optimizacije baterije", + "backup_controller_page_background_charging": "Samo tijekom punjenja", + "backup_controller_page_background_configure_error": "NeuspjeÅĄno konfiguriranje pozadinske usluge", + "backup_controller_page_background_delay": "Odgođeno sigurnosno kopiranje novih resursa: {duration}", + "backup_controller_page_background_description": "Uključite pozadinsku uslugu kako biste automatski sigurnosno kopirali nove resurse bez potrebe za otvaranjem aplikacije", + "backup_controller_page_background_is_off": "Automatsko sigurnosno kopiranje u pozadini je isključeno", + "backup_controller_page_background_is_on": "Automatsko sigurnosno kopiranje u pozadini je uključeno", + "backup_controller_page_background_turn_off": "Isključite pozadinsku uslugu", + "backup_controller_page_background_turn_on": "Uključite pozadinsku uslugu", + "backup_controller_page_background_wifi": "Samo na Wi-Fi mreÅži", + "backup_controller_page_backup": "Sigurnosna kopija", + "backup_controller_page_backup_selected": "Odabrani: ", + "backup_controller_page_backup_sub": "Sigurnosno kopirane fotografije i videozapisi", + "backup_controller_page_created": "Kreirano: {date}", + "backup_controller_page_desc_backup": "Uključite sigurnosno kopiranje u prvom planu kako biste automatski prenijeli nove resurse na posluÅžitelj prilikom otvaranja aplikacije.", + "backup_controller_page_excluded": "Izuzeto: ", + "backup_controller_page_failed": "NeuspjeÅĄno ({count})", + "backup_controller_page_filename": "Naziv datoteke: {filename} [{size}]", + "backup_controller_page_info": "Informacije o sigurnosnom kopiranju", + "backup_controller_page_none_selected": "Nema odabranih", + "backup_controller_page_remainder": "Podsjetnik", + "backup_controller_page_remainder_sub": "Preostale fotografije i videozapisi za sigurnosno kopiranje iz odabira", + "backup_controller_page_server_storage": "Pohrana na posluÅžitelju", + "backup_controller_page_start_backup": "Pokreni Sigurnosno Kopiranje", + "backup_controller_page_status_off": "Automatsko sigurnosno kopiranje u prvom planu je isključeno", + "backup_controller_page_status_on": "Automatsko sigurnosno kopiranje u prvom planu je uključeno", + "backup_controller_page_storage_format": "{used} od {total} iskoriÅĄteno", + "backup_controller_page_to_backup": "Albumi za sigurnosno kopiranje", + "backup_controller_page_total_sub": "Sve jedinstvene fotografije i videozapisi iz odabranih albuma", + "backup_controller_page_turn_off": "Isključite sigurnosno kopiranje u prvom planu", + "backup_controller_page_turn_on": "Uključite sigurnosno kopiranje u prvom planu", + "backup_controller_page_uploading_file_info": "Slanje informacija o datoteci", + "backup_err_only_album": "Nije moguće ukloniti jedini album", + "backup_info_card_assets": "resursi", + "backup_manual_cancelled": "Otkazano", + "backup_manual_in_progress": "Slanje već u tijeku. PokÅĄuajte nakon nekog vremena", + "backup_manual_success": "Uspijeh", + "backup_manual_title": "Status slanja", + "backup_options_page_title": "Opcije sigurnosnog kopiranja", + "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", "backward": "Unazad", "birthdate_saved": "Datum rođenja uspjeÅĄno spremljen", "birthdate_set_description": "Datum rođenja se koristi za izračunavanje godina ove osobe u trenutku fotografije.", @@ -429,24 +569,52 @@ "bulk_keep_duplicates_confirmation": "Jeste li sigurni da Åželite zadrÅžati {count, plural, one {# duplicate asset} other {# duplicate asset}}? Ovo će rijeÅĄiti sve duplicirane grupe bez brisanja ičega.", "bulk_trash_duplicates_confirmation": "Jeste li sigurni da Åželite na veliko baciti u smeće {count, plural, one {# duplicate asset} other {# duplicate asset}}? Ovo će zadrÅžati najveće sredstvo svake grupe i baciti sve ostale duplikate u smeće.", "buy": "Kupi Immich", + "cache_settings_album_thumbnails": "Sličice na stranici biblioteke ({count} resursa)", + "cache_settings_clear_cache_button": "Očisti predmemoriju", + "cache_settings_clear_cache_button_title": "BriÅĄe predmemoriju aplikacije. Ovo će značajno utjecati na performanse aplikacije dok se predmemorija ponovno ne izgradi.", + "cache_settings_duplicated_assets_clear_button": "OČISTI", + "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_title": "Duplicirani resursi ({count})", + "cache_settings_image_cache_size": "Veličina predmemorije slika ({count} resursa)", + "cache_settings_statistics_album": "Sličice biblioteke", + "cache_settings_statistics_assets": "{count} resursa ({size})", + "cache_settings_statistics_full": "Pune slike", + "cache_settings_statistics_shared": "Sličice dijeljenih albuma", + "cache_settings_statistics_thumbnail": "Sličice", + "cache_settings_statistics_title": "KoriÅĄtenje predmemorije", + "cache_settings_subtitle": "Upravljajte ponaÅĄanjem predmemorije mobilne aplikacije Immich", + "cache_settings_thumbnail_size": "Veličina predmemorije sličica ({count} stavki)", + "cache_settings_tile_subtitle": "Upravljajte ponaÅĄanjem lokalne pohrane", + "cache_settings_tile_title": "Lokalna pohrana", + "cache_settings_title": "Postavke predmemorije", "camera": "Kamera", "camera_brand": "Marka kamere", "camera_model": "Model kamere", "cancel": "OtkaÅži", "cancel_search": "OtkaÅži pretragu", + "canceled": "Otkazano", "cannot_merge_people": "Nije moguće spojiti osobe", "cannot_undo_this_action": "Ne moÅžete poniÅĄtiti ovu radnju!", "cannot_update_the_description": "Nije moguće aÅžurirati opis", "change_date": "Promjena datuma", + "change_display_order": "Promijeni redoslijed prikaza", "change_expiration_time": "Promjena vremena isteka", "change_location": "Promjena lokacije", "change_name": "Promjena imena", "change_name_successfully": "Promijena imena uspjeÅĄna", "change_password": "Promjena Lozinke", "change_password_description": "Ovo je ili prvi put da se prijavljujete u sustav ili je poslan zahtjev za promjenom lozinke. Unesite novu lozinku ispod.", + "change_password_form_confirm_password": "Potvrdi lozinku", + "change_password_form_description": "Pozdrav {name},\n\nOvo je ili prvi put da se prijavljujete u sustav ili je zatraÅžena promjena vaÅĄe lozinke. Molimo unesite novu lozinku dolje.", + "change_password_form_new_password": "Nova lozinka", + "change_password_form_password_mismatch": "Lozinke se ne podudaraju", + "change_password_form_reenter_new_password": "Ponovno unesite novu lozinku", "change_your_password": "Promijenite lozinku", "changed_visibility_successfully": "Vidljivost je uspjeÅĄno promijenjena", "check_all": "Provjeri Sve", + "check_corrupt_asset_backup": "Provjeri oÅĄtećene sigurnosne kopije stavki", + "check_corrupt_asset_backup_button": "IzvrÅĄi provjeru", + "check_corrupt_asset_backup_description": "Pokrenite ovu provjeru samo putem Wi-Fi mreÅže i nakon ÅĄto su sve stavke sigurnosno kopirane. Postupak moÅže potrajati nekoliko minuta.", "check_logs": "Provjera Zapisa", "choose_matching_people_to_merge": "Odaberite odgovarajuće osobe za spajanje", "city": "Grad", @@ -455,6 +623,14 @@ "clear_all_recent_searches": "IzbriÅĄi sva nedavna pretraÅživanja", "clear_message": "Jasna poruka", "clear_value": "Očisti vrijednost", + "client_cert_dialog_msg_confirm": "U redu", + "client_cert_enter_password": "Unesite lozinku", + "client_cert_import": "Uvezi", + "client_cert_import_success_msg": "Klijentski certifikat je uvezen", + "client_cert_invalid_msg": "Neispravna datoteka certifikata ili pogreÅĄna lozinka", + "client_cert_remove_msg": "Klijentski certifikat je uklonjen", + "client_cert_subtitle": "PodrÅžava samo PKCS12 (.p12, .pfx) format. Uvoz/uklanjanje certifikata moguće je samo prije prijave", + "client_cert_title": "SSL klijentski certifikat", "clockwise": "U smjeru kazaljke na satu", "close": "Zatvori", "collapse": "SaÅžmi", @@ -465,15 +641,27 @@ "comment_options": "Opcije komentara", "comments_and_likes": "Komentari i lajkovi", "comments_are_disabled": "Komentari onemogućeni", + "common_create_new_album": "Kreiraj novi album", + "common_server_error": "Provjerite svoju mreÅžnu vezu, osigurajte da je posluÅžitelj dostupan i da su verzije aplikacije/posluÅžitelja kompatibilne.", + "completed": "DovrÅĄeno", "confirm": "Potvrdi", "confirm_admin_password": "Potvrdite lozinku administratora", - "confirm_delete_face": "Jeste li sigurni da Åželite izbrisati lice {name} iz knjiÅžnice materijala.", + "confirm_delete_face": "Jeste li sigurni da Åželite izbrisati lice {name} iz resursa?", "confirm_delete_shared_link": "Jeste li sigurni da Åželite izbrisati ovu zajedničku vezu?", "confirm_keep_this_delete_others": "Sva druga sredstva u nizu bit će izbrisana osim ovog sredstva. Jeste li sigurni da Åželite nastaviti?", "confirm_password": "Potvrdite lozinku", "contain": "SadrÅži", "context": "Kontekst", "continue": "Nastavi", + "control_bottom_app_bar_album_info_shared": "{count} stavki ¡ Dijeljeno", + "control_bottom_app_bar_create_new_album": "Kreiraj novi album", + "control_bottom_app_bar_delete_from_immich": "IzbriÅĄi iz Immicha", + "control_bottom_app_bar_delete_from_local": "IzbriÅĄi s uređaja", + "control_bottom_app_bar_edit_location": "Uredi lokaciju", + "control_bottom_app_bar_edit_time": "Uredi datum i vrijeme", + "control_bottom_app_bar_share_link": "Podijeli poveznicu", + "control_bottom_app_bar_share_to": "Podijeli s...", + "control_bottom_app_bar_trash_from_immich": "Premjesti u smeće", "copied_image_to_clipboard": "Slika je kopirana u međuspremnik.", "copied_to_clipboard": "Kopirano u međuspremnik!", "copy_error": "GreÅĄka kopiranja", @@ -488,18 +676,25 @@ "covers": "Naslovnice", "create": "Kreiraj", "create_album": "Kreiraj album", + "create_album_page_untitled": "Bez naslova", "create_library": "Kreiraj Biblioteku", "create_link": "Kreiraj poveznicu", "create_link_to_share": "Izradite vezu za dijeljenje", "create_link_to_share_description": "Dopusti svakome s vezom da vidi odabrane fotografije", + "create_new": "KREIRAJ NOVO", "create_new_person": "Stvorite novu osobu", "create_new_person_hint": "Dodijelite odabrana sredstva novoj osobi", "create_new_user": "Kreiraj novog korisnika", + "create_shared_album_page_share_add_assets": "DODAJ STAVKE", + "create_shared_album_page_share_select_photos": "Odaberi fotografije", "create_tag": "Stvori oznaku", "create_tag_description": "Napravite novu oznaku. Za ugnijeŞđene oznake unesite punu putanju oznake uključujući kose crte.", "create_user": "Stvori korisnika", "created": "Stvoreno", + "crop": "ObreÅži", + "curated_object_page_title": "Stvari", "current_device": "Trenutačni uređaj", + "current_server_address": "Trenutna adresa posluÅžitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", "dark": "Tamno", @@ -510,28 +705,43 @@ "date_range": "Razdoblje", "day": "Dan", "deduplicate_all": "Dedupliciraj Sve", + "deduplication_criteria_1": "Veličina slike u bajtovima", + "deduplication_criteria_2": "Broj EXIF podataka", + "deduplication_info": "Informacije o uklanjanju duplikata", + "deduplication_info_description": "Za automatski odabir stavki i masovno uklanjanje duplikata, uzimamo u obzir:", "default_locale": "Zadana lokalizacija", "default_locale_description": "Oblikujte datume i brojeve na temelju jezika preglednika", "delete": "IzbriÅĄi", "delete_album": "IzbriÅĄi album", "delete_api_key_prompt": "Jeste li sigurni da Åželite izbrisati ovaj API ključ?", + "delete_dialog_alert": "Ove stavke bit će trajno izbrisane iz Immicha i s vaÅĄeg uređaja", + "delete_dialog_alert_local": "Ove stavke bit će trajno uklonjene s vaÅĄeg uređaja, ali će i dalje biti dostupne na Immich posluÅžitelju", + "delete_dialog_alert_local_non_backed_up": "Neke od stavki nisu sigurnosno kopirane na Immich i bit će trajno uklonjene s vaÅĄeg uređaja", + "delete_dialog_alert_remote": "Ove stavke bit će trajno izbrisane s Immich posluÅžitelja", + "delete_dialog_ok_force": "IzbriÅĄi svejedno", + "delete_dialog_title": "Trajno izbriÅĄi", "delete_duplicates_confirmation": "Jeste li sigurni da Åželite trajno izbrisati ove duplikate?", + "delete_face": "IzbriÅĄi lice", "delete_key": "Ključ za brisanje", "delete_library": "IzbriÅĄi knjiÅžnicu", "delete_link": "IzbriÅĄi poveznicu", + "delete_local_dialog_ok_backed_up_only": "IzbriÅĄi samo sigurnosno kopirane", + "delete_local_dialog_ok_force": "IzbriÅĄi svejedno", "delete_others": "IzbriÅĄi druge", "delete_shared_link": "IzbriÅĄi dijeljenu poveznicu", + "delete_shared_link_dialog_title": "IzbriÅĄi dijeljenu poveznicu", "delete_tag": "IzbriÅĄi oznaku", "delete_tag_confirmation_prompt": "Jeste li sigurni da Åželite izbrisati oznaku {tagName}?", "delete_user": "IzbriÅĄi korisnika", "deleted_shared_link": "Izbrisana dijeljena poveznica", "deletes_missing_assets": "BriÅĄe sredstva koja nedostaju s diska", "description": "Opis", + "description_input_hint_text": "Dodaj opis...", + "description_input_submit_error": "PogreÅĄka pri aÅžuriranju opisa, provjerite zapisnik za viÅĄe detalja", "details": "Detalji", "direction": "Smjer", "disabled": "Onemogućeno", "disallow_edits": "Zabrani izmjene", - "discord": "Discord", "discover": "Otkrij", "dismiss_all_errors": "Odbaci sve pogreÅĄke", "dismiss_error": "Odbaci pogreÅĄku", @@ -543,12 +753,26 @@ "documentation": "Dokumentacija", "done": "Gotovo", "download": "Preuzmi", + "download_canceled": "Preuzimanje otkazano", + "download_complete": "Preuzimanje zavrÅĄeno", + "download_enqueue": "Preuzimanje dodano u red", + "download_error": "PogreÅĄka pri preuzimanju", + "download_failed": "Preuzimanje nije uspjelo", + "download_filename": "datoteka: {filename}", + "download_finished": "Preuzimanje zavrÅĄeno", "download_include_embedded_motion_videos": "Ugrađeni videozapisi", "download_include_embedded_motion_videos_description": "Uključite videozapise ugrađene u fotografije s pokretom kao zasebnu datoteku", + "download_notfound": "Preuzimanje nije pronađeno", + "download_paused": "Preuzimanje pauzirano", "download_settings": "Preuzmi", "download_settings_description": "Upravljajte postavkama koje se odnose na preuzimanje sredstava", + "download_started": "Preuzimanje započeto", + "download_sucess": "Preuzimanje uspjeÅĄno", + "download_sucess_android": "Medij je preuzet u DCIM/Immich", + "download_waiting_to_retry": "Čeka se ponovni pokuÅĄaj", "downloading": "Preuzimanje", "downloading_asset_filename": "Preuzimanje materijala {filename}", + "downloading_media": "Preuzimanje medija", "drop_files_to_upload": "Ispustite datoteke bilo gdje za prijenos", "duplicates": "Duplikati", "duplicates_description": "RazrijeÅĄite svaku grupu tako da naznačite koji su duplikati, ako ih ima", @@ -565,6 +789,7 @@ "edit_key": "Ključ za uređivanje", "edit_link": "Uredi poveznicu", "edit_location": "Uredi lokaciju", + "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi ljude", "edit_tag": "Uredi oznaku", @@ -577,19 +802,25 @@ "editor_crop_tool_h2_aspect_ratios": "Omjeri stranica", "editor_crop_tool_h2_rotation": "Rotacija", "email": "E-poÅĄta", + "empty_folder": "Ova mapa je prazna", "empty_trash": "Isprazni smeće", "empty_trash_confirmation": "Jeste li sigurni da Åželite isprazniti smeće? Time će se iz Immicha trajno ukloniti sva sredstva u otpadu.\nNe moÅžete poniÅĄtiti ovu radnju!", "enable": "Omogući", "enabled": "Omogućeno", "end_date": "Datum zavrÅĄetka", + "enqueued": "Dodano u red", + "enter_wifi_name": "Unesite naziv Wi-Fi mreÅže", "error": "GreÅĄka", + "error_change_sort_album": "Nije moguće promijeniti redoslijed albuma", + "error_delete_face": "PogreÅĄka pri brisanju lica sa stavke", "error_loading_image": "PogreÅĄka pri učitavanju slike", + "error_saving_image": "PogreÅĄka: {error}", "error_title": "GreÅĄka - NeÅĄto je poÅĄlo krivo", "errors": { "cannot_navigate_next_asset": "Nije moguće prijeći na sljedeći materijal", "cannot_navigate_previous_asset": "Nije moguće prijeći na prethodni materijal", "cant_apply_changes": "Nije moguće primijeniti promjene", - "cant_change_activity": "Ne mogu {enabled, select, true {disable} druge {enable}} aktivnosti", + "cant_change_activity": "Ne moÅže se {enabled, select, true {onemogućiti} ostalo {omogućiti}} aktivnost", "cant_change_asset_favorite": "Nije moguće promijeniti favorita za sredstvo", "cant_change_metadata_assets_count": "Nije moguće promijeniti metapodatke {count, plural, one {# asset} other {# assets}}", "cant_get_faces": "Ne mogu dobiti lica", @@ -629,7 +860,7 @@ "unable_to_add_exclusion_pattern": "Nije moguće dodati uzorak izuzimanja", "unable_to_add_import_path": "Nije moguće dodati putanju uvoza", "unable_to_add_partners": "Nije moguće dodati partnere", - "unable_to_add_remove_archive": "Nije moguće {arhivirano, odabrati, istinito {ukloniti sredstvo iz} druge {dodati sredstvo u}} arhivu", + "unable_to_add_remove_archive": "Ne moÅže se {archived, select, true {ukloniti resurs iz} ostalo {dodati resurs u}} arhivu", "unable_to_add_remove_favorites": "Nije moguće {favorite, select, true {add asset to} other {remove asset from}} favorite", "unable_to_archive_unarchive": "Nije moguće {arhivirati, odabrati, istinito {arhivirati} ostalo {dearhivirati}}", "unable_to_change_album_user_role": "Nije moguće promijeniti ulogu korisnika albuma", @@ -711,9 +942,21 @@ "unable_to_update_user": "Nije moguće aÅžurirati korisnika", "unable_to_upload_file": "Nije moguće učitati datoteku" }, - "exif": "Exif", + "exif_bottom_sheet_description": "Dodaj opis...", + "exif_bottom_sheet_details": "DETALJI", + "exif_bottom_sheet_location": "LOKACIJA", + "exif_bottom_sheet_people": "OSOBE", + "exif_bottom_sheet_person_add_person": "Dodaj ime", + "exif_bottom_sheet_person_age": "Dob {age}", + "exif_bottom_sheet_person_age_months": "Dob {months} mjeseci", + "exif_bottom_sheet_person_age_year_months": "Dob 1 godina, {months} mjeseci", + "exif_bottom_sheet_person_age_years": "Dob {years}", "exit_slideshow": "Izađi iz projekcije slideova", "expand_all": "ProÅĄiri sve", + "experimental_settings_new_asset_list_subtitle": "Rad u tijeku", + "experimental_settings_new_asset_list_title": "Omogući eksperimentalnu mreÅžu fotografija", + "experimental_settings_subtitle": "Koristite na vlastitu odgovornost!", + "experimental_settings_title": "Eksperimentalno", "expire_after": "Istječe nakon", "expired": "Isteklo", "expires_date": "Ističe {date}", @@ -724,10 +967,16 @@ "extension": "ProÅĄirenje (Extension)", "external": "Vanjski", "external_libraries": "Vanjske Biblioteke", + "external_network": "Vanjska mreÅža", + "external_network_sheet_info": "Kada niste na Åželjenoj Wi-Fi mreÅži, aplikacija će se povezati s posluÅžiteljem putem prve dostupne URL adrese s popisa ispod, redom od vrha prema dnu", "face_unassigned": "Nedodijeljeno", + "failed": "NeuspjeÅĄno", + "failed_to_load_assets": "Neuspjelo učitavanje stavki", + "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Omiljene", + "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", "feature_photo_updated": "Istaknuta fotografija aÅžurirana", "features": "Značajke (Features)", "features_setting_description": "Upravljajte značajkama aplikacije", @@ -735,22 +984,39 @@ "file_name_or_extension": "Naziv ili ekstenzija datoteke", "filename": "Naziv datoteke", "filetype": "Vrsta datoteke", + "filter": "Filtar", "filter_people": "Filtrirajte ljude", + "filter_places": "Filtriraj mjesta", "find_them_fast": "Pronađite ih brzo po imenu pomoću pretraÅživanja", "fix_incorrect_match": "Ispravite netočno podudaranje", + "folder": "Mapa", + "folder_not_found": "Mapa nije pronađena", "folders": "Mape", "folders_feature_description": "Pregledavanje prikaza mape za fotografije i videozapise u sustavu datoteka", "forward": "Naprijed", "general": "Općenito", "get_help": "PotraÅžite pomoć", + "get_wifiname_error": "Nije moguće dohvatiti naziv Wi-Fi mreÅže. Provjerite imate li potrebna dopuÅĄtenja i jeste li povezani na Wi-Fi mreÅžu", "getting_started": "Početak Rada", "go_back": "Idi natrag", + "go_to_folder": "Idi u mapu", "go_to_search": "Idi na pretragu", + "grant_permission": "Dodijeli dopuÅĄtenje", "group_albums_by": "Grupiraj albume po...", + "group_country": "Grupiraj po zemlji", "group_no": "Nema grupiranja", "group_owner": "Grupiraj po vlasniku", + "group_places_by": "Grupiraj mjesta po...", "group_year": "Grupiraj po godini", + "haptic_feedback_switch": "Omogući haptičku povratnu informaciju", + "haptic_feedback_title": "Haptička povratna informacija", "has_quota": "Ima kvotu", + "header_settings_add_header_tip": "Dodaj zaglavlje", + "header_settings_field_validator_msg": "Vrijednost ne moÅže biti prazna", + "header_settings_header_name_input": "Naziv zaglavlja", + "header_settings_header_value_input": "Vrijednost zaglavlja", + "headers_settings_tile_subtitle": "Definirajte proxy zaglavlja koja aplikacija treba slati sa svakim mreÅžnim zahtjevom", + "headers_settings_tile_title": "Prilagođena proxy zaglavlja", "hi_user": "Bok {name} ({email})", "hide_all_people": "Sakrij sve ljude", "hide_gallery": "Sakrij galeriju", @@ -758,8 +1024,24 @@ "hide_password": "Sakrij lozinku", "hide_person": "Sakrij osobu", "hide_unnamed_people": "Sakrij neimenovane osobe", + "home_page_add_to_album_conflicts": "Dodano {added} stavki u album {album}. {failed} stavki je već u albumu.", + "home_page_add_to_album_err_local": "Lokalne stavke joÅĄ nije moguće dodati u albume, preskačem", + "home_page_add_to_album_success": "Dodano {added} stavki u album {album}.", + "home_page_album_err_partner": "JoÅĄ nije moguće dodati partnerske stavke u album, preskačem", + "home_page_archive_err_local": "Lokalne stavke joÅĄ nije moguće arhivirati, preskačem", + "home_page_archive_err_partner": "Partnerske stavke nije moguće arhivirati, preskačem", + "home_page_building_timeline": "Izrada vremenske crte", + "home_page_delete_err_partner": "Nije moguće izbrisati partnerske stavke, preskačem", + "home_page_delete_remote_err_local": "Lokalne stavke su u odabiru za udaljeno brisanje, preskačem", + "home_page_favorite_err_local": "Lokalne stavke joÅĄ nije moguće označiti kao omiljene, preskačem", + "home_page_favorite_err_partner": "Partnerske stavke joÅĄ nije moguće označiti kao omiljene, preskačem", + "home_page_first_time_notice": "Ako prvi put koristite aplikaciju, svakako odaberite album za sigurnosnu kopiju kako bi vremenska crta mogla prikazati fotografije i videozapise", + "home_page_share_err_local": "Lokalne stavke nije moguće dijeliti putem poveznice, preskačem", + "home_page_upload_err_limit": "Moguće je prenijeti najviÅĄe 30 stavki odjednom, preskačem", "host": "Domaćin", "hour": "Sat", + "ignore_icloud_photos": "Ignoriraj iCloud fotografije", + "ignore_icloud_photos_description": "Fotografije pohranjene na iCloudu neće biti učitane na Immich posluÅžitelj", "image": "Slika", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} snimljeno {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} snimljeno s {person1} {date}", @@ -771,7 +1053,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1} i {person2} {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1}, {person2} i {person3} {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} s {person1}, {person2} i {additionalCount, number} drugih {date}", - "immich_logo": "Immich Logo", + "image_saved_successfully": "Slika je spremljena", + "image_viewer_page_state_provider_download_started": "Preuzimanje započelo", + "image_viewer_page_state_provider_download_success": "UspjeÅĄno Preuzimanje", + "image_viewer_page_state_provider_share_error": "GreÅĄka pri dijeljenju", "immich_web_interface": "Immich Web Sučelje", "import_from_json": "Uvoz iz JSON-a", "import_path": "Putanja uvoza", @@ -781,6 +1066,7 @@ "include_shared_albums": "Uključi dijeljene albume", "include_shared_partner_assets": "Uključite zajedničku imovinu partnera", "individual_share": "Pojedinačni udio", + "individual_shares": "Pojedinačna dijeljenja", "info": "Informacije", "interval": { "day_at_onepm": "Svaki dan u 13 sati", @@ -788,6 +1074,8 @@ "night_at_midnight": "Svaku večer u ponoć", "night_at_twoam": "Svake noći u 2 ujutro" }, + "invalid_date": "Neispravan datum", + "invalid_date_format": "Neispravan format datuma", "invite_people": "Pozovite ljude", "invite_to_album": "Pozovi u album", "items_count": "{count, plural, one {# datoteka} other {# datoteke}}", @@ -803,10 +1091,17 @@ "latest_version": "Najnovija verzija", "latitude": "Zemljopisna ÅĄirina", "leave": "Izađi", + "lens_model": "Model objektiva", "let_others_respond": "Dozvoli da drugi odgovore", "level": "Razina", "library": "Biblioteka", "library_options": "Mogućnosti biblioteke", + "library_page_device_albums": "Albumi na uređaju", + "library_page_new_album": "Novi album", + "library_page_sort_asset_count": "Broj resursa", + "library_page_sort_created": "Datum kreiranja", + "library_page_sort_last_modified": "Zadnja izmjena", + "library_page_sort_title": "Naslov albuma", "light": "Svjetlo", "like_deleted": "Like izbrisan", "link_motion_video": "PoveÅžite videozapis pokreta", @@ -816,12 +1111,41 @@ "list": "Popis", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretraÅživanja nije uspjelo", + "local_network": "Lokalna mreÅža", + "local_network_sheet_info": "Aplikacija će se povezati s posluÅžiteljem putem ovog URL-a kada koristi određenu Wi-Fi mreÅžu", + "location_permission": "Dozvola za lokaciju", + "location_permission_content": "Kako bi koristio značajku automatskog prebacivanja, Immich treba dozvolu za preciznu lokaciju kako bi mogao očitati naziv trenutne Wi-Fi mreÅže", + "location_picker_choose_on_map": "Odaberi na karti", + "location_picker_latitude_error": "Unesite valjanu geografsku ÅĄirinu", + "location_picker_latitude_hint": "Unesite ovdje svoju geografsku ÅĄirinu", + "location_picker_longitude_error": "Unesite valjanu geografsku duÅžinu", + "location_picker_longitude_hint": "Unesite ovdje svoju geografsku duÅžinu", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", + "login_disabled": "Prijava je onemogućena", + "login_form_api_exception": "API iznimka. Provjerite URL posluÅžitelja i pokuÅĄajte ponovno.", + "login_form_back_button_text": "Nazad", + "login_form_email_hint": "vasaemaiadresal@email.com", + "login_form_endpoint_url": "URL krajnje točke posluÅžitelja", + "login_form_err_http": "Molimo navedite http:// ili https://", + "login_form_err_invalid_email": "NevaÅžeća e-mail adresa", + "login_form_err_invalid_url": "NevaÅžeći URL", + "login_form_err_leading_whitespace": "Početni razmak", + "login_form_err_trailing_whitespace": "ZavrÅĄni razmak", + "login_form_failed_get_oauth_server_config": "PogreÅĄka pri prijavi putem OAuth-a, provjerite URL posluÅžitelja", + "login_form_failed_get_oauth_server_disable": "OAuth mogućnost nije dostupna na ovom posluÅžitelju", + "login_form_failed_login": "PogreÅĄka pri prijavi, provjerite URL posluÅžitelja, e-mail i lozinku", + "login_form_handshake_exception": "DoÅĄlo je do greÅĄke u uspostavi veze s posluÅžiteljem. Omogućite podrÅĄku za samopotpisane certifikate u postavkama ako koristite takav certifikat.", + "login_form_password_hint": "lozinka", + "login_form_save_login": "Ostani prijavljen", + "login_form_server_empty": "Unesite URL posluÅžitelja.", + "login_form_server_error": "Nije moguće povezivanje s posluÅžiteljem.", "login_has_been_disabled": "Prijava je onemogućena.", + "login_password_changed_error": "DoÅĄlo je do pogreÅĄke pri aÅžuriranju lozinke", + "login_password_changed_success": "Lozinka je uspjeÅĄno aÅžurirana", "logout_all_device_confirmation": "Jeste li sigurni da Åželite odjaviti sve uređaje?", "logout_this_device_confirmation": "Jeste li sigurni da se Åželite odjaviti s ovog uređaja?", "longitude": "Zemljopisna duÅžina", @@ -829,6 +1153,7 @@ "loop_videos": "Ponavljajte videozapise", "loop_videos_description": "Omogućite automatsko ponavljanje videozapisa u pregledniku detalja.", "main_branch_warning": "Koristite razvojnu verziju; strogo preporučamo koriÅĄtenje izdane verzije!", + "main_menu": "Glavni izbornik", "make": "Proizvođač", "manage_shared_links": "Upravljanje dijeljenim vezama", "manage_sharing_with_partners": "Upravljajte dijeljenjem s partnerima", @@ -838,13 +1163,38 @@ "manage_your_devices": "Upravljajte uređajima na kojima ste prijavljeni", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Karta", + "map_assets_in_bound": "{count} fotografija", + "map_assets_in_bounds": "{count} fotografija", + "map_cannot_get_user_location": "Nije moguće dohvatiti lokaciju korisnika", + "map_location_dialog_yes": "Da", + "map_location_picker_page_use_location": "Koristi ovu lokaciju", + "map_location_service_disabled_content": "Usluga lokacije mora biti omogućena za prikaz stavki s vaÅĄe trenutne lokacije. ÅŊelite li je sada omogućiti?", + "map_location_service_disabled_title": "Usluga lokacije onemogućena", "map_marker_for_images": "Oznaka karte za slike snimljene u {city}, {country}", "map_marker_with_image": "Oznaka karte sa slikom", + "map_no_assets_in_bounds": "Nema fotografija u ovom području", + "map_no_location_permission_content": "Potrebno je dopuÅĄtenje za lokaciju kako bi se prikazale stavke s vaÅĄe trenutne lokacije. ÅŊelite li ga sada omogućiti?", + "map_no_location_permission_title": "DopuÅĄtenje za lokaciju odbijeno", "map_settings": "Postavke karte", + "map_settings_dark_mode": "Tamni način rada", + "map_settings_date_range_option_day": "Posljednja 24 sata", + "map_settings_date_range_option_days": "Posljednjih {days} dana", + "map_settings_date_range_option_year": "ProÅĄla godina", + "map_settings_date_range_option_years": "Posljednjih {years} godina", + "map_settings_dialog_title": "Postavke karte", + "map_settings_include_show_archived": "Uključi arhivirane", + "map_settings_include_show_partners": "Uključi partnere", + "map_settings_only_show_favorites": "PrikaÅži samo omiljene", + "map_settings_theme_settings": "Tema karte", + "map_zoom_to_see_photos": "Umanjite prikaz za pregled fotografija", "matches": "Podudaranja", "media_type": "Vrsta medija", "memories": "Sjećanja", + "memories_all_caught_up": "Sve ste pregledali", + "memories_check_back_tomorrow": "Provjerite ponovno sutra za viÅĄe uspomena", "memories_setting_description": "Upravljajte onim ÅĄto vidite u svojim sjećanjima", + "memories_start_over": "Započni iznova", + "memories_swipe_to_close": "Prijeđite prstom prema gore za zatvaranje", "memory": "Memorija", "memory_lane_title": "Traka sjećanja {title}", "menu": "Izbornik", @@ -857,13 +1207,17 @@ "minimize": "Minimiziraj", "minute": "Minuta", "missing": "Nedostaje", - "model": "Model", "month": "Mjesec", "more": "ViÅĄe", "moved_to_trash": "PremjeÅĄteno u smeće", + "multiselect_grid_edit_date_time_err_read_only": "Nije moguće urediti datum stavki samo za čitanje, preskačem", + "multiselect_grid_edit_gps_err_read_only": "Nije moguće urediti lokaciju stavki samo za čitanje, preskačem", + "mute_memories": "Isključi uspomene", "my_albums": "Moji albumi", "name": "Ime", "name_or_nickname": "Ime ili nadimak", + "networking_settings": "UmreÅžavanje", + "networking_subtitle": "Upravljajte postavkama krajnje točke posluÅžitelja", "never": "Nikada", "new_album": "Novi Album", "new_api_key": "Novi API ključ", @@ -880,6 +1234,7 @@ "no_albums_yet": "Čini se da joÅĄ nemate nijedan album.", "no_archived_assets_message": "Arhivirajte fotografije i videozapise kako biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA PRENESETE SVOJU PRVU FOTOGRAFIJU", + "no_assets_to_show": "Nema stavki za prikaz", "no_duplicates_found": "Nisu pronađeni duplikati.", "no_exif_info_available": "Nema dostupnih exif podataka", "no_explore_results_message": "Prenesite viÅĄe fotografija da istraÅžite svoju zbirku.", @@ -891,18 +1246,22 @@ "no_results_description": "PokuÅĄajte sa sinonimom ili općenitijom ključnom riječi", "no_shared_albums_message": "Stvorite album za dijeljenje fotografija i videozapisa s osobama u svojoj mreÅži", "not_in_any_album": "Ni u jednom albumu", + "not_selected": "Nije odabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primijenili Oznaku za skladiÅĄtenje na prethodno prenesena sredstva, pokrenite", "notes": "BiljeÅĄke", + "notification_permission_dialog_content": "Da biste omogućili obavijesti, idite u Postavke i odaberite dopusti.", + "notification_permission_list_tile_content": "Dodijelite dopuÅĄtenje za omogućavanje obavijesti.", + "notification_permission_list_tile_enable_button": "Omogući obavijesti", + "notification_permission_list_tile_title": "DopuÅĄtenje za obavijesti", "notification_toggle_setting_description": "Omogući obavijesti putem e-poÅĄte", "notifications": "Obavijesti", "notifications_setting_description": "Upravljanje obavijestima", - "oauth": "OAuth", "official_immich_resources": "SluÅžbeni Immich resursi", "offline": "Izvan mreÅže", "offline_paths": "IzvanmreÅžne putanje", "offline_paths_description": "Ovi rezultati mogu biti posljedica ručnog brisanja datoteka koje nisu dio vanjske biblioteke.", - "ok": "Ok", "oldest_first": "Prvo najstarije", + "on_this_device": "Na ovom uređaju", "onboarding": "Uključivanje (Onboarding)", "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama administracije.", "onboarding_theme_description": "Odaberite temu boja za svoj primjer. To moÅžete kasnije promijeniti u postavkama.", @@ -910,22 +1269,29 @@ "onboarding_welcome_user": "Dobro doÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", + "open": "Otvori", "open_in_map_view": "Otvori u prikazu karte", "open_in_openstreetmap": "Otvori u OpenStreetMap", "open_the_search_filters": "Otvorite filtre pretraÅživanja", "options": "Opcije", "or": "ili", "organize_your_library": "Organizirajte svoju knjiÅžnicu", - "original": "original", "other": "Ostalo", "other_devices": "Ostali uređaji", "other_variables": "Ostale varijable", "owned": "VlasniÅĄtvo", "owner": "Vlasnik", - "partner": "Partner", "partner_can_access": "{partner} moÅže pristupiti", "partner_can_access_assets": "Sve vaÅĄe fotografije i videi osim onih u arhivi i smeću", "partner_can_access_location": "Mjesto otkuda je slika otkinuta", + "partner_list_user_photos": "{user} fotografije", + "partner_list_view_all": "PrikaÅži sve", + "partner_page_empty_message": "VaÅĄe fotografije joÅĄ nisu podijeljene ni s jednim partnerom.", + "partner_page_no_more_users": "Nema viÅĄe korisnika za dodavanje", + "partner_page_partner_add_failed": "Nije uspjelo dodavanje partnera", + "partner_page_select_partner": "Odaberi partnera", + "partner_page_shared_to_title": "Podijeljeno s", + "partner_page_stop_sharing_content": "{partner} viÅĄe neće moći pristupiti vaÅĄim fotografijama.", "partner_sharing": "Dijeljenje s partnerom", "partners": "Partneri", "password": "Zaporka", @@ -954,7 +1320,16 @@ "permanently_delete_assets_prompt": "Da li ste sigurni da Åželite trajni izbrisati {count, plural, one {ovu datoteku?} other {ove # datoteke?}}Ovo cˁe ih također ukloniti {count, plural, one {iz njihovog} other {iz njihovih}} albuma.", "permanently_deleted_asset": "Trajno izbrisano sredstvo", "permanently_deleted_assets_count": "Trajno izbrisano {count, plural, one {# datoteka} other {# datoteke}}", + "permission_onboarding_back": "Natrag", + "permission_onboarding_continue_anyway": "Nastavi svejedno", + "permission_onboarding_get_started": "Započni", + "permission_onboarding_go_to_settings": "Idi u postavke", + "permission_onboarding_permission_denied": "DopuÅĄtenje odbijeno. Za koriÅĄtenje Immicha, dodijelite dopuÅĄtenja za fotografije i videozapise u Postavkama.", + "permission_onboarding_permission_granted": "DopuÅĄtenje dodijeljeno! Sve je spremno.", + "permission_onboarding_permission_limited": "DopuÅĄtenje ograničeno. Da biste Immichu dopustili sigurnosno kopiranje i upravljanje cijelom galerijom, dodijelite dopuÅĄtenja za fotografije i videozapise u Postavkama.", + "permission_onboarding_request": "Immich zahtijeva dopuÅĄtenje za pregled vaÅĄih fotografija i videozapisa.", "person": "Osoba", + "person_birthdate": "Rođen/a {date}", "person_hidden": "{name}{hidden, select, true { (skriveno)} other {}}", "photo_shared_all_users": "Čini se da ste svoje fotografije podijelili sa svim korisnicima ili nemate nijednog korisnika s kojim biste ih podijelili.", "photos": "Fotografije", @@ -964,11 +1339,13 @@ "pick_a_location": "Odaberite lokaciju", "place": "Mjesto", "places": "Mjesta", + "places_count": "{count, plural, =1 {{count, number} Mjesto} few {{count, number} Mjesta} other {{count, number} Mjesta}}", "play": "Pokreni", "play_memories": "Pokreni sjećanja", "play_motion_photo": "Reproduciraj Pokretnu fotografiju", "play_or_pause_video": "Reproducirajte ili pauzirajte video", - "port": "Port", + "preferences_settings_subtitle": "Upravljajte postavkama aplikacije", + "preferences_settings_title": "Postavke", "preset": "Unaprijed postavljeno", "preview": "Pregled", "previous": "Prethodno", @@ -976,13 +1353,19 @@ "previous_or_next_photo": "Prethodna ili sljedeća fotografija", "primary": "Primarna (Primary)", "privacy": "Privatnost", + "profile_drawer_app_logs": "Zapisnici", + "profile_drawer_client_out_of_date_major": "Mobilna aplikacija je zastarjela. AÅžurirajte na najnoviju glavnu verziju.", + "profile_drawer_client_out_of_date_minor": "Mobilna aplikacija je zastarjela. AÅžurirajte na najnoviju manju verziju.", + "profile_drawer_client_server_up_to_date": "Klijent i posluÅžitelj su aÅžurirani", + "profile_drawer_server_out_of_date_major": "PosluÅžitelj je zastario. AÅžurirajte na najnoviju glavnu verziju.", + "profile_drawer_server_out_of_date_minor": "PosluÅžitelj je zastario. AÅžurirajte na najnoviju manju verziju.", "profile_image_of_user": "Profilna slika korisnika {user}", "profile_picture_set": "Profilna slika postavljena.", "public_album": "Javni album", "public_share": "Javno dijeljenje", "purchase_account_info": "PodrÅžava softver", "purchase_activated_subtitle": "Hvala ÅĄto podrÅžavate Immich i softver otvorenog koda", - "purchase_activated_time": "Aktivirano {date, date}", + "purchase_activated_time": "Aktivirano {date}", "purchase_activated_title": "VaÅĄ ključ je uspjeÅĄno aktiviran", "purchase_button_activate": "Aktiviraj", "purchase_button_buy": "Kupi", @@ -1023,7 +1406,12 @@ "reassigned_assets_to_new_person": "Ponovo dodijeljeno {count, plural, one {# datoteka} other {# datoteke}} novoj osobi", "reassing_hint": "Dodijelite odabrane datoteke postojećoj osobi", "recent": "Nedavno", + "recent-albums": "Nedavni albumi", "recent_searches": "Nedavne pretrage", + "recently_added": "Nedavno dodano", + "recently_added_page_title": "Nedavno dodano", + "recently_taken": "Nedavno snimljeno", + "recently_taken_page_title": "Nedavno snimljeno", "refresh": "OsvjeÅži", "refresh_encoded_videos": "OsvjeÅžite kodirane videozapise", "refresh_faces": "OsvjeÅžite lica", @@ -1044,11 +1432,16 @@ "remove_from_album": "Ukloni iz albuma", "remove_from_favorites": "Ukloni iz favorita", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", + "remove_memory": "Ukloni uspomenu", + "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", + "remove_url": "Ukloni URL", "remove_user": "Ukloni korisnika", "removed_api_key": "Uklonjen API ključ: {name}", "removed_from_archive": "Uklonjeno iz arhive", "removed_from_favorites": "Uklonjeno iz favorita", "removed_from_favorites_count": "{count, plural, other {Uklonjeno #}} iz omiljenih", + "removed_memory": "Uklonjena uspomena", + "removed_photo_from_memory": "Uklonjena fotografija iz uspomene", "removed_tagged_assets": "Uklonjena oznaka iz {count, plural, one {# datoteke} other {# datoteka}}", "rename": "Preimenuj", "repair": "Popravi", @@ -1057,7 +1450,7 @@ "repository": "SpremiÅĄte (Repository)", "require_password": "Zahtijevaj lozinku", "require_user_to_change_password_on_first_login": "Zahtijevajte od korisnika promjenu lozinke pri prvoj prijavi", - "reset": "Reset", + "rescan": "Ponovno skeniraj", "reset_password": "Resetiraj lozinku", "reset_people_visibility": "PoniÅĄti vidljivost ljudi", "reset_to_default": "Vrati na zadano", @@ -1074,10 +1467,12 @@ "role_editor": "Urednik", "role_viewer": "Gledatelj", "save": "Spremi", + "save_to_gallery": "Spremi u galeriju", "saved_api_key": "Spremljen API ključ", "saved_profile": "Spremljen profil", "saved_settings": "Spremljene postavke", "say_something": "Reci neÅĄto", + "scaffold_body_error_occurred": "DoÅĄlo je do pogreÅĄke", "scan_all_libraries": "Skeniraj sve KnjiÅžnice", "scan_library": "Skeniraj", "scan_settings": "Postavke skeniranja", @@ -1085,20 +1480,53 @@ "search": "PretraÅživanje", "search_albums": "TraÅži albume", "search_by_context": "PretraÅživanje po kontekstu", + "search_by_description": "PretraÅži po opisu", + "search_by_description_example": "Planinarenje u Sapi", "search_by_filename": "PretraÅžujte prema nazivu datoteke ili ekstenziji", "search_by_filename_example": "npr. IMG_1234.JPG ili PNG", "search_camera_make": "PretraÅžite marku kamere...", "search_camera_model": "PretraÅžite model kamere...", "search_city": "PretraÅžite grad...", "search_country": "PretraÅžite drÅžavu...", + "search_filter_apply": "Primijeni filtar", + "search_filter_camera_title": "Odaberi vrstu kamere", + "search_filter_date": "Datum", + "search_filter_date_interval": "{start} do {end}", + "search_filter_date_title": "Odaberi raspon datuma", + "search_filter_display_option_not_in_album": "Nije u albumu", + "search_filter_display_options": "Opcije prikaza", + "search_filter_filename": "PretraÅži po nazivu datoteke", + "search_filter_location": "Lokacija", + "search_filter_location_title": "Odaberi lokaciju", + "search_filter_media_type": "Vrsta medija", + "search_filter_media_type_title": "Odaberi vrstu medija", + "search_filter_people_title": "Odaberi osobe", + "search_for": "TraÅži", "search_for_existing_person": "PotraÅžite postojeću osobu", + "search_no_more_result": "Nema viÅĄe rezultata", "search_no_people": "Nema ljudi", "search_no_people_named": "Nema osoba s imenom \"{name}\"", + "search_no_result": "Nema rezultata, pokuÅĄajte s drugim pojmom za pretraÅživanje ili kombinacijom", "search_options": "Opcije pretraÅživanja", + "search_page_categories": "Kategorije", + "search_page_motion_photos": "Pokretne fotografije", + "search_page_no_objects": "Nema dostupnih informacija o objektima", + "search_page_no_places": "Nema dostupnih informacija o mjestima", + "search_page_screenshots": "Snimke zaslona", + "search_page_search_photos_videos": "PretraÅžite svoje fotografije i videozapise", + "search_page_selfies": "Selfiji", + "search_page_things": "Stvari", + "search_page_view_all_button": "PrikaÅži sve", + "search_page_your_activity": "VaÅĄa aktivnost", + "search_page_your_map": "VaÅĄa karta", "search_people": "TraÅži ljude", "search_places": "TraÅži mjesta", + "search_rating": "PretraÅži po ocjeni...", + "search_result_page_new_search_hint": "Nova pretraga", "search_settings": "Postavke pretraÅživanja", "search_state": "DrÅžava pretraÅživanja...", + "search_suggestion_list_smart_search_hint_1": "Pametna pretraga je omogućena prema zadanim postavkama, za pretraÅživanje metapodataka koristite sintaksu ", + "search_suggestion_list_smart_search_hint_2": "m:vaÅĄ-pojam-pretrage", "search_tags": "TraÅži oznake...", "search_timezone": "PretraÅži vremenske zone", "search_type": "Vrsta pretraÅživanja", @@ -1106,43 +1534,124 @@ "searching_locales": "TraÅženje lokaliteta...", "second": "Drugi", "see_all_people": "Vidi sve ljude", + "select": "Odaberi", "select_album_cover": "Odaberite omot albuma", "select_all": "Odaberi sve", "select_all_duplicates": "Odaberi sve duplikate", - "select_avatar_color": "", + "select_avatar_color": "Odaberi boju avatara", "select_face": "Odaberi lice", - "select_featured_photo": "", - "select_keep_all": "", - "select_library_owner": "", - "select_new_face": "", - "select_photos": "", - "select_trash_all": "", + "select_featured_photo": "Odaberi istaknutu fotografiju", + "select_from_computer": "Odaberi s računala", + "select_keep_all": "Odaberi zadrÅži sve", + "select_library_owner": "Odaberi vlasnika knjiÅžnice", + "select_new_face": "Odaberi novo lice", + "select_photos": "Odaberi fotografije", + "select_trash_all": "Odaberi izbriÅĄi sve", + "select_user_for_sharing_page_err_album": "Nije uspjelo kreiranje albuma", "selected": "Odabrano", - "send_message": "", + "selected_count": "{count, plural, =1 {# odabran} few {# odabrana} other {# odabranih}}", + "send_message": "PoÅĄalji poruku", "send_welcome_email": "PoÅĄalji email dobrodoÅĄlice", + "server_endpoint": "Krajnja točka posluÅžitelja", + "server_info_box_app_version": "Verzija aplikacije", + "server_info_box_server_url": "URL posluÅžitelja", "server_offline": "Server izvan mreÅže", "server_online": "Server na mreÅži", "server_stats": "Statistike servera", "server_version": "Verzija servera", "set": "Postavi", - "set_as_album_cover": "", + "set_as_album_cover": "Postavi kao naslovnicu albuma", + "set_as_featured_photo": "Postavi kao istaknutu fotografiju", "set_as_profile_picture": "Postavi kao profilnu sliku", "set_date_of_birth": "Postavi datum rođenja", "set_profile_picture": "Postavi profilnu sliku", - "set_slideshow_to_fullscreen": "", + "set_slideshow_to_fullscreen": "Postavi prezentaciju na cijeli zaslon", + "setting_image_viewer_help": "Preglednik detalja prvo učitava malu sličicu, zatim učitava pregled srednje veličine (ako je omogućen), te na kraju učitava original (ako je omogućen).", + "setting_image_viewer_original_subtitle": "Omogućite za učitavanje originalne slike pune rezolucije (velika!). Onemogućite za smanjenje potroÅĄnje podataka (i mreÅžne i na predmemoriji uređaja).", + "setting_image_viewer_original_title": "Učitaj originalnu sliku", + "setting_image_viewer_preview_subtitle": "Omogućite za učitavanje slike srednje rezolucije. Onemogućite za izravno učitavanje originala ili koriÅĄtenje samo sličice.", + "setting_image_viewer_preview_title": "Učitaj sliku za pregled", + "setting_image_viewer_title": "Slike", + "setting_languages_apply": "Primijeni", + "setting_languages_subtitle": "Promijeni jezik aplikacije", + "setting_languages_title": "Jezici", + "setting_notifications_notify_failures_grace_period": "Obavijesti o neuspjehu sigurnosnog kopiranja u pozadini: {duration}", + "setting_notifications_notify_hours": "{count} sati", + "setting_notifications_notify_immediately": "odmah", + "setting_notifications_notify_minutes": "{count} minuta", + "setting_notifications_notify_never": "nikad", + "setting_notifications_notify_seconds": "{count} sekundi", + "setting_notifications_single_progress_subtitle": "Detaljne informacije o napretku prijenosa po stavci", + "setting_notifications_single_progress_title": "PrikaÅži detaljni napredak sigurnosnog kopiranja u pozadini", + "setting_notifications_subtitle": "Prilagodite postavke obavijesti", + "setting_notifications_total_progress_subtitle": "Ukupni napredak prijenosa (zavrÅĄeno/ukupno stavki)", + "setting_notifications_total_progress_title": "PrikaÅži ukupni napredak sigurnosnog kopiranja u pozadini", + "setting_video_viewer_looping_title": "Ponavljanje", + "setting_video_viewer_original_video_subtitle": "Prilikom strujanja videozapisa s posluÅžitelja, reproducirajte original čak i kada je dostupna transkodirana verzija. MoÅže doći do međuspremanja. Videozapisi dostupni lokalno reproduciraju se u originalnoj kvaliteti bez obzira na ovu postavku.", + "setting_video_viewer_original_video_title": "Forsiraj originalni videozapis", "settings": "Postavke", + "settings_require_restart": "Ponovno pokrenite Immich da biste primijenili ovu postavku", "settings_saved": "Postavke su spremljene", "share": "Podijeli", + "share_add_photos": "Dodaj fotografije", + "share_assets_selected": "{count} odabrano", + "share_dialog_preparing": "Priprema...", "shared": "Podijeljeno", + "shared_album_activities_input_disable": "Komentiranje je onemogućeno", + "shared_album_activity_remove_content": "ÅŊelite li izbrisati ovu aktivnost?", + "shared_album_activity_remove_title": "IzbriÅĄi aktivnost", + "shared_album_section_people_action_error": "PogreÅĄka pri napuÅĄtanju/uklanjanju iz albuma", + "shared_album_section_people_action_leave": "Ukloni korisnika iz albuma", + "shared_album_section_people_action_remove_user": "Ukloni korisnika iz albuma", + "shared_album_section_people_title": "OSOBE", "shared_by": "Podijelio", "shared_by_user": "Podijelio {user}", "shared_by_you": "Podijelili vi", "shared_from_partner": "Fotografije od {partner}", - "shared_links": "", - "shared_with_partner": "", - "sharing": "", - "sharing_sidebar_description": "", - "show_album_options": "", + "shared_intent_upload_button_progress_text": "{current} / {total} Preneseno", + "shared_link_app_bar_title": "Dijeljene poveznice", + "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik", + "shared_link_clipboard_text": "Poveznica: {link}\nLozinka: {password}", + "shared_link_create_error": "PogreÅĄka pri kreiranju dijeljene poveznice", + "shared_link_edit_description_hint": "Unesite opis dijeljenja", + "shared_link_edit_expire_after_option_day": "1 dan", + "shared_link_edit_expire_after_option_days": "{count} dana", + "shared_link_edit_expire_after_option_hour": "1 sat", + "shared_link_edit_expire_after_option_hours": "{count} sati", + "shared_link_edit_expire_after_option_minute": "1 minuta", + "shared_link_edit_expire_after_option_minutes": "{count} minuta", + "shared_link_edit_expire_after_option_months": "{count} mjeseci", + "shared_link_edit_expire_after_option_year": "{count} godina", + "shared_link_edit_password_hint": "Unesite lozinku za dijeljenje", + "shared_link_edit_submit_button": "AÅžuriraj poveznicu", + "shared_link_error_server_url_fetch": "Nije moguće dohvatiti URL posluÅžitelja", + "shared_link_expires_day": "Istječe za {count} dan", + "shared_link_expires_days": "Istječe za {count} dana", + "shared_link_expires_hour": "Istječe za {count} sat", + "shared_link_expires_hours": "Istječe za {count} sati", + "shared_link_expires_minute": "Istječe za {count} minutu", + "shared_link_expires_minutes": "Istječe za {count} minuta", + "shared_link_expires_never": "Istječe ∞", + "shared_link_expires_second": "Istječe za {count} sekundu", + "shared_link_expires_seconds": "Istječe za {count} sekundi", + "shared_link_individual_shared": "Pojedinačno podijeljeno", + "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", + "shared_link_options": "Opcije dijeljene poveznice", + "shared_links": "Dijeljene poveznice", + "shared_links_description": "Podijelite fotografije i videozapise putem poveznice", + "shared_photos_and_videos_count": "{assetCount, plural, =1 {# podijeljena fotografija ili videozapis.} few {# podijeljene fotografije i videozapisa.} other {# podijeljenih fotografija i videozapisa.}}", + "shared_with_me": "Podijeljeno sa mnom", + "shared_with_partner": "Podijeljeno s {partner}", + "sharing": "Dijeljenje", + "sharing_enter_password": "Molimo unesite lozinku za pregled ove stranice.", + "sharing_page_album": "Dijeljeni albumi", + "sharing_page_description": "Kreirajte dijeljene albume za dijeljenje fotografija i videozapisa s ljudima u vaÅĄoj mreÅži.", + "sharing_page_empty_list": "PRAZAN POPIS", + "sharing_sidebar_description": "PrikaÅži poveznicu na Dijeljenje u bočnoj traci", + "sharing_silver_appbar_create_shared_album": "Novi dijeljeni album", + "sharing_silver_appbar_share_partner": "Podijeli s partnerom", + "shift_to_permanent_delete": "pritisnite ⇧ za trajno brisanje stavke", + "show_album_options": "PrikaÅži opcije albuma", "show_albums": "PrikaÅži albume", "show_all_people": "PrikaÅži sve osobe", "show_and_hide_people": "PrikaÅži i sakrij osobe", @@ -1150,105 +1659,216 @@ "show_gallery": "PrikaÅži galeriju", "show_hidden_people": "PrikaÅži skrivene osobe", "show_in_timeline": "PrikaÅži na vremenskoj crti", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", - "show_metadata": "", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", - "shuffle": "", - "sign_out": "", - "sign_up": "", - "size": "", - "skip_to_content": "", - "slideshow": "", - "slideshow_settings": "", - "sort_albums_by": "", - "stack": "", - "stack_selected_photos": "", - "stacktrace": "", - "start": "", - "start_date": "", - "state": "", - "status": "", - "stop_motion_photo": "", - "stop_photo_sharing": "", - "stop_photo_sharing_description": "", - "stop_sharing_photos_with_user": "", - "storage": "", - "storage_label": "", - "storage_usage": "", - "submit": "", + "show_in_timeline_setting_description": "PrikaÅži fotografije i videozapise ovog korisnika na vaÅĄoj vremenskoj crti", + "show_keyboard_shortcuts": "PrikaÅži tipkovničke prečace", + "show_metadata": "PrikaÅži metapodatke", + "show_or_hide_info": "PrikaÅži ili sakrij informacije", + "show_password": "PrikaÅži lozinku", + "show_person_options": "PrikaÅži opcije osobe", + "show_progress_bar": "PrikaÅži traku napretka", + "show_search_options": "PrikaÅži opcije pretraÅživanja", + "show_shared_links": "PrikaÅži dijeljene poveznice", + "show_slideshow_transition": "PrikaÅži prijelaz prezentacije", + "show_supporter_badge": "Značka podrÅžavatelja", + "show_supporter_badge_description": "PrikaÅži značku podrÅžavatelja", + "shuffle": "Nasumični redoslijed", + "sidebar": "Bočna traka", + "sidebar_display_description": "PrikaÅži poveznicu na prikaz u bočnoj traci", + "sign_out": "Odjava", + "sign_up": "Registriraj se", + "size": "Veličina", + "skip_to_content": "Preskoči na sadrÅžaj", + "skip_to_folders": "Preskoči na mape", + "skip_to_tags": "Preskoči na oznake", + "slideshow": "Prezentacija", + "slideshow_settings": "Postavke prezentacije", + "sort_albums_by": "Sortiraj albume po...", + "sort_created": "Datum kreiranja", + "sort_items": "Broj stavki", + "sort_modified": "Datum izmjene", + "sort_oldest": "Najstarija fotografija", + "sort_people_by_similarity": "Sortiraj osobe po sličnosti", + "sort_recent": "Najnovija fotografija", + "sort_title": "Naslov", + "source": "Izvor", + "stack": "SloÅži", + "stack_duplicates": "SloÅži duplikate", + "stack_select_one_photo": "Odaberi jednu glavnu fotografiju za slaganje", + "stack_selected_photos": "SloÅži odabrane fotografije", + "stacked_assets_count": "SloÅženo {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "stacktrace": "Pracenje stoga", + "start": "Početak", + "start_date": "Datum početka", + "state": "Stanje", + "stop_motion_photo": "Zaustavi pokretnu fotografiju", + "stop_photo_sharing": "Prestati dijeliti svoje fotografije?", + "stop_photo_sharing_description": "{partner} viÅĄe neće moći pristupiti vaÅĄim fotografijama.", + "stop_sharing_photos_with_user": "Prestani dijeliti svoje fotografije s ovim korisnikom", + "storage": "Prostor za pohranu", + "storage_label": "Oznaka pohrane", + "storage_usage": "{used} od {available} iskoriÅĄteno", + "submit": "PoÅĄalji", "suggestions": "Prijedlozi", - "sunrise_on_the_beach": "Sunrise on the beach", - "swap_merge_direction": "", + "support": "PodrÅĄka", + "support_and_feedback": "PodrÅĄka i povratne informacije", + "support_third_party_description": "VaÅĄa Immich instalacija je pakirana od strane treće strane. Problemi koje doÅživljavate mogu biti uzrokovani tim paketom, stoga vas molimo da probleme prvo prijavite njima putem poveznica u nastavku.", + "swap_merge_direction": "Zamijeni smjer spajanja", "sync": "Sink.", - "template": "", + "sync_albums": "Sinkroniziraj albume", + "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", + "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", + "tag": "Oznaka", + "tag_assets": "Označi stavke", + "tag_created": "Kreirana oznaka: {tag}", + "tag_feature_description": "Pregledavanje fotografija i videozapisa grupiranih po logičkim temama oznaka", + "tag_not_found_question": "Nije moguće pronaći oznaku? Napravite novu oznaku.", + "tag_people": "Označi osobe", + "tag_updated": "AÅžurirana oznaka: {tag}", + "tagged_assets": "Označena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "tags": "Oznake", + "template": "PredloÅžak", "theme": "Tema", "theme_selection": "Izbor teme", "theme_selection_description": "Automatski postavite temu na svijetlu ili tamnu ovisno o postavkama sustava vaÅĄeg preglednika", + "theme_setting_asset_list_storage_indicator_title": "PrikaÅži indikator pohrane na pločicama stavki", + "theme_setting_asset_list_tiles_per_row_title": "Broj stavki po retku ({count})", + "theme_setting_colorful_interface_subtitle": "Primijeni primarnu boju na pozadinske povrÅĄine.", + "theme_setting_colorful_interface_title": "Å areno sučelje", + "theme_setting_image_viewer_quality_subtitle": "Prilagodite kvalitetu preglednika detalja slike", + "theme_setting_image_viewer_quality_title": "Kvaliteta preglednika slika", + "theme_setting_primary_color_subtitle": "Odaberite boju za primarne radnje i naglaske.", + "theme_setting_primary_color_title": "Primarna boja", + "theme_setting_system_primary_color_title": "Koristi boju sustava", + "theme_setting_system_theme_switch": "Automatski (Prati postavke sustava)", + "theme_setting_theme_subtitle": "Odaberite postavku teme aplikacije", + "theme_setting_three_stage_loading_subtitle": "Trostupanjsko učitavanje moÅže poboljÅĄati performanse učitavanja, ali uzrokuje znatno veće opterećenje mreÅže", + "theme_setting_three_stage_loading_title": "Omogući trostupanjsko učitavanje", "they_will_be_merged_together": "Oni ću biti spojeni zajedno", + "third_party_resources": "Resursi trećih strana", "time_based_memories": "Uspomene temeljene na vremenu", + "timeline": "Vremenska crta", "timezone": "Vremenska zona", "to_archive": "Arhivaj", "to_change_password": "Promjeni lozinku", "to_favorite": "Omiljeni", "to_login": "Prijava", + "to_parent": "Idi na roditelja", "to_trash": "Smeće", "toggle_settings": "Uključi/isključi postavke", "toggle_theme": "Promjeni temu", + "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Smeće", "trash_all": "Stavi sve u smeće", + "trash_count": "Smeće {count, number}", + "trash_delete_asset": "Premjesti u smeće / IzbriÅĄi stavku", + "trash_emptied": "IspraÅžnjeno smeće", "trash_no_results_message": "Ovdje će se prikazati bačene fotografije i videozapisi.", + "trash_page_delete_all": "IzbriÅĄi sve", + "trash_page_empty_trash_dialog_content": "ÅŊelite li isprazniti svoje stavke u smeću? Ove stavke bit će trajno uklonjene iz Immicha", + "trash_page_info": "Stavke u smeću bit će trajno izbrisane nakon {days} dana", + "trash_page_no_assets": "Nema stavki u smeću", + "trash_page_restore_all": "Vrati sve", + "trash_page_select_assets_btn": "Odaberi stavke", + "trash_page_title": "Smeće ({count})", "trashed_items_will_be_permanently_deleted_after": "Stavke bačene u smeće trajno će se izbrisati nakon {days, plural, one {# day} other {# days}}.", "type": "Vrsta", - "unarchive": "", - "unfavorite": "", - "unhide_person": "", - "unknown": "", - "unknown_year": "", - "unlimited": "", - "unlink_oauth": "", - "unlinked_oauth_account": "", - "unselect_all": "", - "unstack": "", - "untracked_files": "", - "untracked_files_decription": "", - "up_next": "", - "updated_password": "", - "upload": "", - "upload_concurrency": "", - "url": "", - "usage": "", - "user": "", - "user_id": "", - "user_usage_detail": "", + "unarchive": "PoniÅĄti arhiviranje", + "unarchived_count": "{count, plural, =1 {PoniÅĄteno arhiviranje #} few {PoniÅĄteno arhiviranje #} other {PoniÅĄteno arhiviranje #}}", + "unfavorite": "Ukloni iz omiljenih", + "unhide_person": "PrikaÅži osobu", + "unknown": "Nepoznato", + "unknown_country": "Nepoznata drÅžava", + "unknown_year": "Nepoznata godina", + "unlimited": "Neograničeno", + "unlink_motion_video": "OdpoveÅži pokretni videozapis", + "unlink_oauth": "OdpoveÅži OAuth", + "unlinked_oauth_account": "Odpovezan OAuth račun", + "unmute_memories": "Uključi uspomene", + "unnamed_album": "Album bez imena", + "unnamed_album_delete_confirmation": "Jeste li sigurni da Åželite izbrisati ovaj album?", + "unnamed_share": "Dijeljenje bez imena", + "unsaved_change": "Nespremljena promjena", + "unselect_all": "PoniÅĄti odabir svih", + "unselect_all_duplicates": "PoniÅĄti odabir svih duplikata", + "unstack": "Razdvoji", + "unstacked_assets_count": "Razdvojena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "untracked_files": "Datoteke bez praćenja", + "untracked_files_decription": "Ove datoteke nisu praćene od strane aplikacije. Mogu biti rezultat neuspjelih premjeÅĄtanja, prekinutih prijenosa ili su ostale zbog greÅĄke", + "up_next": "Sljedeće", + "updated_password": "Lozinka aÅžurirana", + "upload": "Prijenos", + "upload_concurrency": "Istovremeni prijenosi", + "upload_dialog_info": "ÅŊelite li sigurnosno kopirati odabranu stavku(e) na posluÅžitelj?", + "upload_dialog_title": "Prenesi stavku", + "upload_errors": "Prijenos zavrÅĄen s {count, plural, =1 {# greÅĄkom} few {# greÅĄke} other {# greÅĄaka}}, osvjeÅžite stranicu da biste vidjeli nove prenesene stavke.", + "upload_progress": "Preostalo {remaining, number} - Obrađeno {processed, number}/{total, number}", + "upload_skipped_duplicates": "Preskočena {count, plural, =1 {# duplicirana stavka} few {# duplicirane stavke} other {# dupliciranih stavki}}", + "upload_status_duplicates": "Duplikati", + "upload_status_errors": "GreÅĄke", + "upload_status_uploaded": "Preneseno", + "upload_success": "Prijenos uspjeÅĄan, osvjeÅžite stranicu da biste vidjeli nove prenesene stavke.", + "upload_to_immich": "Prenesi na Immich ({count})", + "uploading": "Prijenos u tijeku", + "usage": "KoriÅĄtenje", + "use_current_connection": "koristi trenutnu vezu", + "use_custom_date_range": "Koristi prilagođeni raspon datuma", + "user": "Korisnik", + "user_id": "ID korisnika", + "user_liked": "{user} je označio/la sviđa mi se {type, select, photo {ovu fotografiju} video {ovaj videozapis} asset {ovu stavku} other {to}}", + "user_purchase_settings": "Kupnja", + "user_purchase_settings_description": "Upravljajte svojom kupnjom", + "user_role_set": "Postavi {user} kao {role}", + "user_usage_detail": "Detalji koriÅĄtenja korisnika", "user_usage_stats": "Statistika koriÅĄtenja računa", "user_usage_stats_description": "Pregledajte statistiku koriÅĄtenja računa", - "username": "", - "users": "", - "utilities": "", - "validate": "", - "variables": "", - "version": "", - "video": "", - "video_hover_setting": "", - "video_hover_setting_description": "", - "videos": "", - "videos_count": "", - "view_all": "", - "view_all_users": "", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", - "waiting": "", - "week": "", - "welcome_to_immich": "", - "year": "", - "yes": "", - "you_dont_have_any_shared_links": "", - "zoom_image": "" + "username": "Korisničko ime", + "users": "Korisnici", + "utilities": "Alati", + "validate": "Provjeri valjanost", + "validate_endpoint_error": "Molimo unesite valjanu URL adresu", + "variables": "Varijable", + "version": "Verzija", + "version_announcement_closing": "VaÅĄ prijatelj, Alex", + "version_announcement_message": "Bok! Dostupna je nova verzija Immicha. Odvojite malo vremena da pročitate biljeÅĄke o izdanju kako biste bili sigurni da je vaÅĄe postavljanje aÅžurno kako biste spriječili bilo kakve pogreÅĄne konfiguracije, pogotovo ako koristite WatchTower ili bilo koji mehanizam koji automatski upravlja aÅžuriranjem vaÅĄe instance Immicha.", + "version_announcement_overlay_release_notes": "napomene o izdanju", + "version_announcement_overlay_text_1": "Bok prijatelju, dostupno je novo izdanje", + "version_announcement_overlay_text_2": "molimo odvojite vrijeme da posjetite ", + "version_announcement_overlay_text_3": " i osigurajte da su vaÅĄe postavke docker-compose i .env aÅžurirane kako biste spriječili pogreÅĄne konfiguracije, posebno ako koristite WatchTower ili bilo koji mehanizam koji automatski aÅžurira vaÅĄu posluÅžiteljsku aplikaciju.", + "version_announcement_overlay_title": "Dostupna je nova verzija posluÅžitelja 🎉", + "version_history": "Povijest verzija", + "version_history_item": "Instalirana verzija {version} dana {date}", + "video": "Videozapis", + "video_hover_setting": "Reproduciraj sličicu videozapisa pri prelasku miÅĄem", + "video_hover_setting_description": "Reproduciraj sličicu videozapisa kada miÅĄ prelazi preko stavke. Čak i kada je onemogućeno, reprodukcija se moÅže pokrenuti prelaskom miÅĄa preko ikone za reprodukciju.", + "videos": "Videozapisi", + "videos_count": "{count, plural, =1 {# Videozapis} few {# Videozapisa} other {# Videozapisa}}", + "view": "Prikaz", + "view_album": "PrikaÅži album", + "view_all": "PrikaÅži sve", + "view_all_users": "PrikaÅži sve korisnike", + "view_in_timeline": "PrikaÅži na vremenskoj crti", + "view_link": "PrikaÅži poveznicu", + "view_links": "PrikaÅži poveznice", + "view_name": "Prikaz", + "view_next_asset": "PrikaÅži sljedeću stavku", + "view_previous_asset": "PrikaÅži prethodnu stavku", + "view_qr_code": "PrikaÅži QR kod", + "view_stack": "PrikaÅži sloÅžene", + "viewer_remove_from_stack": "Ukloni iz sloÅženih", + "viewer_stack_use_as_main_asset": "Koristi kao glavnu stavku", + "viewer_unstack": "Razdvoji", + "visibility_changed": "Vidljivost promijenjena za {count, plural, =1 {# osobu} few {# osobe} other {# osoba}}", + "waiting": "Čekanje", + "warning": "Upozorenje", + "week": "Tjedan", + "welcome": "DobrodoÅĄli", + "welcome_to_immich": "DobrodoÅĄli u Immich", + "wifi_name": "Naziv Wi-Fi mreÅže", + "year": "Godina", + "years_ago": "prije {years, plural, =1 {# godinu} few {# godine} other {# godina}}", + "yes": "Da", + "you_dont_have_any_shared_links": "Nemate nijednu dijeljenu poveznicu", + "your_wifi_name": "Naziv vaÅĄe Wi-Fi mreÅže", + "zoom_image": "Povećaj sliku" } diff --git a/i18n/hu.json b/i18n/hu.json index 68dec7d036..fb2d895e3d 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Biztosan letiltod az Ãļsszes bejelentkezÊsi mÃŗdot? A bejelentkezÊs teljesen le lesz tiltva.", "authentication_settings_reenable": "Az ÃējbÃŗli engedÊlyezÊshez hasznÃĄlj egySzerver Parancsot.", "background_task_job": "HÃĄttÊrfeladatok", - "backup_database": "AdatbÃĄzis BiztonsÃĄgi MentÊse", - "backup_database_enable_description": "AdatbÃĄzis biztonsÃĄgi mentÊsek engedÊlyezÊse", - "backup_keep_last_amount": "Megőrizendő korÃĄbbi biztonsÃĄgi mentÊsek szÃĄma", - "backup_settings": "BiztonsÃĄgi mentÊs beÃĄllítÃĄsai", - "backup_settings_description": "AdatbÃĄzis mentÊsi beÃĄllítÃĄsainak kezelÊse", + "backup_database": "AdatbÃĄzis lementÊse", + "backup_database_enable_description": "AdatbÃĄzis mentÊsek engedÊlyezÊse", + "backup_keep_last_amount": "Megőrizendő korÃĄbbi mentÊsek szÃĄma", + "backup_settings": "AdatbÃĄzis mentÊs beÃĄllítÃĄsai", + "backup_settings_description": "AdatbÃĄzis mentÊs beÃĄllítÃĄsainak kezelÊse. MegjegyzÊs: Ezek a feladatok nincsenek felÃŧgyelve, így nem kapsz ÊrtesítÊs meghiÃēsulÃĄs esetÊn.", "check_all": "Összes KipiÃĄlÃĄsa", "cleanup": "TakarítÃĄs", "cleared_jobs": "{job}: feladatai tÃļrÃļlve", @@ -53,6 +53,7 @@ "confirm_email_below": "A megerősítÊshez írd be, hogy \"{email}\"", "confirm_reprocess_all_faces": "Biztos vagy benne, hogy Ãējra fel szeretnÊd dolgozni az Ãļsszes arcot? Ez a mÃĄr elnevezett szemÊlyeket is tÃļrli.", "confirm_user_password_reset": "Biztosan vissza szeretnÊd ÃĄllítani {user} jelszavÃĄt?", + "confirm_user_pin_code_reset": "Biztos, hogy vissza akarod ÃĄllítani {user} PIN-kÃŗdjÃĄt?", "create_job": "Feladat lÊtrehozÃĄsa", "cron_expression": "Cron kifejezÊs", "cron_expression_description": "A beolvasÃĄsi időkÃļz beÃĄllítÃĄsa a cron formÃĄtummal. TovÃĄbbi informÃĄciÃŗÃŠrt lÃĄsd pl. Crontab Guru", @@ -192,26 +193,22 @@ "oauth_auto_register": "Automatikus regisztrÃĄciÃŗ", "oauth_auto_register_description": "Új felhasznÃĄlÃŗk automatikus regisztrÃĄlÃĄsa az OAuth hasznÃĄlatÃĄval tÃļrtÊnő bejelentkezÊs utÃĄn", "oauth_button_text": "Gomb szÃļvege", - "oauth_client_id": "Kliens ID", - "oauth_client_secret": "Kliens Titok", + "oauth_client_secret_description": "KÃļtelező, ha az OAuth szolgÃĄltatÃŗ nem tÃĄmogatja a PKCE-t (Proof Key for Code Exchange)", "oauth_enable_description": "BejelentkezÊs OAuth hasznÃĄlatÃĄval", - "oauth_issuer_url": "KibocsÃĄtÃŗ URL", "oauth_mobile_redirect_uri": "Mobil ÃĄtirÃĄnyítÃĄsi URI", "oauth_mobile_redirect_uri_override": "Mobil ÃĄtirÃĄnyítÃĄsi URI felÃŧlírÃĄs", "oauth_mobile_redirect_uri_override_description": "EngedÊlyezd, ha az OAuth szolgÃĄltatÃŗ tiltja a mobil URI-t, mint pÊldÃĄul '{callback}'", - "oauth_profile_signing_algorithm": "Profil alÃĄÃ­rÃŗ algoritmus", - "oauth_profile_signing_algorithm_description": "A felhasznÃĄlÃŗi profil alÃĄÃ­rÃĄsÃĄhoz hasznÃĄlt algoritmus.", - "oauth_scope": "HatÃŗkÃļr", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth bejelentkezÊsi beÃĄllítÃĄsok kezelÊse", "oauth_settings_more_details": "Erről a funkciÃŗrÃŗl tovÃĄbbi informÃĄciÃŗt a dokumentÃĄciÃŗban talÃĄlsz.", - "oauth_signing_algorithm": "AlÃĄÃ­rÃĄs algoritmusa", "oauth_storage_label_claim": "TÃĄrhely címke igÊnylÊs", "oauth_storage_label_claim_description": "A felhasznÃĄlÃŗ tÃĄrhely címkÊjÊnek automatikus beÃĄllítÃĄsa az igÊnyeltre.", "oauth_storage_quota_claim": "TÃĄrhelykvÃŗta igÊnylÊse", "oauth_storage_quota_claim_description": "A felhasznÃĄlÃŗ tÃĄrhelykvÃŗtÃĄjÃĄnak automatikus beÃĄllítÃĄsa ennek az igÊnyeltre.", "oauth_storage_quota_default": "AlapÊrtelmezett tÃĄrhelykvÃŗta (GiB)", "oauth_storage_quota_default_description": "AlapÊrtelmezett tÃĄrhely kvÃŗta GiB-ban, amennyiben a felhasznÃĄlÃŗ nem jelezte az igÊnyÊt (A korlÃĄtlan tÃĄrhelyhez 0-t adj meg).", + "oauth_timeout": "KÊrÊs időkorlÃĄtja", + "oauth_timeout_description": "KÊrÊsek időkorlÃĄtja milliszekundumban", "offline_paths": "Offline Útvonalak", "offline_paths_description": "Ezek az eredmÊnyek olyan fÃĄjlok kÊzi tÃļrlÊsÊnek tudhatÃŗk be, amelyek nem rÊszei kÃŧlső kÊptÃĄrnak.", "password_enable_description": "BejelentkezÊs emaillel Ês jelszÃŗval", @@ -367,17 +364,19 @@ "video_conversion_job": "VideÃŗk ÁtkÃŗdolÃĄsa", "video_conversion_job_description": "VideÃŗk ÃĄtkÃŗdolÃĄsa bÃļngÊszőkkel Ês eszkÃļzÃļkkel valÃŗ szÊleskÃļrÅą kompatibilitÃĄs ÊrdekÊben" }, - "admin_email": "Admin Email", "admin_password": "Admin JelszÃŗ", "administration": "AdminisztrÃĄciÃŗ", "advanced": "HaladÃŗ", - "advanced_settings_log_level_title": "NaplÃŗzÃĄs szintje: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beÃĄllítÃĄssal a szinkronizÃĄlÃĄs sorÃĄn alternatív kritÊriumok alapjÃĄn szÅąrheted a fÃĄjlokat. Csak akkor prÃŗbÃĄld ki, ha problÊmÃĄid vannak azzal, hogy az alkalmazÃĄs nem ismeri fel az Ãļsszes albumot.", + "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszkÃļz album szinkronizÃĄlÃĄsi szÅąrő hasznÃĄlata", + "advanced_settings_log_level_title": "NaplÃŗzÃĄs szintje: {level}", "advanced_settings_prefer_remote_subtitle": "NÊhÃĄny eszkÃļz fÃĄjdalmasan lassan tÃļlti be az eszkÃļzÃļn lÊvő bÊlyegkÊpeket. Ez a beÃĄllítÃĄs inkÃĄbb a tÃĄvoli kÊpeket tÃļlti be helyettÃŧk.", "advanced_settings_prefer_remote_title": "TÃĄvoli kÊpek előnyben rÊszesítÊse", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejlÊceket, amiket az app elkÃŧldjÃļn minden hÃĄlÃŗzati kÊrÊsnÊl", "advanced_settings_proxy_headers_title": "Proxy FejlÊcek", "advanced_settings_self_signed_ssl_subtitle": "Nem ellenőrzi a szerver SSL tanÃēsítvÃĄnyÃĄt. ÖnalÃĄÃ­rt tanÃēsítvÃĄny esetÊn szÃŧksÊges beÃĄllítÃĄs.", "advanced_settings_self_signed_ssl_title": "ÖnalÃĄÃ­rt SSL tanÃēsítvÃĄnyok engedÊlyezÊse", + "advanced_settings_sync_remote_deletions_subtitle": "Automatikusan tÃļrÃļlni vagy visszaÃĄllítani egy elemet ezen az eszkÃļzÃļn, ha az adott mÅąveletet a weben hajtottÃĄk vÊgre", "advanced_settings_tile_subtitle": "HaladÃŗ felhasznÃĄlÃŗi beÃĄllítÃĄsok", "advanced_settings_troubleshooting_subtitle": "TovÃĄbbi funkciÃŗk engedÊlyezÊse hibaelhÃĄrítÃĄs cÊljÃĄbÃŗl", "advanced_settings_troubleshooting_title": "HibaelhÃĄrítÃĄs", @@ -400,9 +399,9 @@ "album_remove_user_confirmation": "Biztos, hogy el szeretnÊd tÃĄvolítani {user} felhasznÃĄlÃŗt?", "album_share_no_users": "Úgy tÅąnik, hogy mÃĄr minden felhasznÃĄlÃŗval megosztottad ezt az albumot, vagy nincs senki, akivel meg tudnÃĄd osztani.", "album_thumbnail_card_item": "1 elem", - "album_thumbnail_card_items": "{} elem", + "album_thumbnail_card_items": "{count} elem", "album_thumbnail_card_shared": "¡ Megosztott", - "album_thumbnail_shared_by": "Megosztotta: {}", + "album_thumbnail_shared_by": "Megosztotta: {user}", "album_updated": "Album frissÃŧlt", "album_updated_setting_description": "KÃŧldjÃļn email Êrtesítőt, amikor egy megosztott albumhoz Ãēj elemeket adnak hozzÃĄ", "album_user_left": "KilÊptÊl a(z) {album} albumbÃŗl", @@ -440,7 +439,7 @@ "archive": "Archívum", "archive_or_unarchive_photo": "FotÃŗ archivÃĄlÃĄsa vagy archivÃĄlÃĄsÃĄnak visszavonÃĄsa", "archive_page_no_archived_assets": "Nem talÃĄlhatÃŗ archivÃĄlt elem", - "archive_page_title": "Archívum ({})", + "archive_page_title": "Archívum ({count})", "archive_size": "Archívum mÊrete", "archive_size_description": "BeÃĄllítja letÃļltÊsnÊl az archívum mÊretÊt (GiB)", "archived": "ArchivÃĄlt", @@ -477,18 +476,18 @@ "assets_added_to_album_count": "{count, plural, other {# elem}} hozzÃĄadva az albumhoz", "assets_added_to_name_count": "{count, plural, other {# elem}} hozzÃĄadva {hasName, select, true {a(z) {name}} other {az Ãēj}} albumhoz", "assets_count": "{count, plural, other {# elem}}", - "assets_deleted_permanently": "{} elem vÊglegesen tÃļrÃļlve", - "assets_deleted_permanently_from_server": "{} elem vÊglegesen tÃļrÃļlve az Immich szerverről", + "assets_deleted_permanently": "{count} elem vÊglegesen tÃļrÃļlve", + "assets_deleted_permanently_from_server": "{count} elem vÊglegesen tÃļrÃļlve az Immich szerverről", "assets_moved_to_trash_count": "{count, plural, other {# elem}} ÃĄthelyezve a lomtÃĄrba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} vÊglegesen tÃļrÃļlve", "assets_removed_count": "{count, plural, other {# elem}} eltÃĄvolítva", - "assets_removed_permanently_from_device": "{} elem vÊglegesen tÃļrÃļlve az eszkÃļzÃļdről", + "assets_removed_permanently_from_device": "{count} elem vÊglegesen tÃļrÃļlve az eszkÃļzÃļdről", "assets_restore_confirmation": "Biztos, hogy visszaÃĄllítod a lomtÃĄrban lÊvő Ãļsszes elemet? Ez a mÅąvelet nem visszavonhatÃŗ! MegjegyzÊs: az offline elemeket nem lehet így visszaÃĄllítani.", "assets_restored_count": "{count, plural, other {# elem}} visszaÃĄllítva", - "assets_restored_successfully": "{} elem sikeresen helyreÃĄllítva", - "assets_trashed": "{} elem lomtÃĄrba helyezve", + "assets_restored_successfully": "{count} elem sikeresen helyreÃĄllítva", + "assets_trashed": "{count} elem lomtÃĄrba helyezve", "assets_trashed_count": "{count, plural, other {# elem}} a lomtÃĄrba helyezve", - "assets_trashed_from_server": "{} elem lomtÃĄrba helyezve az Immich szerveren", + "assets_trashed_from_server": "{count} elem lomtÃĄrba helyezve az Immich szerveren", "assets_were_part_of_album_count": "{count, plural, other {# elem}} mÃĄr eleve szerepelt az albumban", "authorized_devices": "EngedÊlyezett EszkÃļzÃļk", "automatic_endpoint_switching_subtitle": "A megadott WiFi-n keresztÃŧl helyi hÃĄlÃŗzaton keresztÃŧl kapcsolÃŗdolik, egyÊbkÊnt az alternatív címeket hasznÃĄlja", @@ -497,7 +496,7 @@ "back_close_deselect": "Vissza, bezÃĄrÃĄs, vagy kijelÃļlÊs tÃļrlÊse", "background_location_permission": "HÃĄttÊrben tÃļrtÊnő helymeghatÃĄrozÃĄsi engedÊly", "background_location_permission_content": "HÃĄlÃŗzatok automatikus vÃĄltÃĄsÃĄhoz az Immich-nek *mindenkÊppen* hozzÃĄ kell fÊrnie a pontos helyzethez, hogy az alkalmazÃĄs le tudja kÊrni a Wi-Fi hÃĄlÃŗzat nevÊt", - "backup_album_selection_page_albums_device": "Ezen az eszkÃļzÃļn lÊvő albumok ({})", + "backup_album_selection_page_albums_device": "Ezen az eszkÃļzÃļn lÊvő albumok ({count})", "backup_album_selection_page_albums_tap": "Koppints a hozzÃĄadÃĄshoz, duplÃĄn koppints az eltÃĄvolítÃĄshoz", "backup_album_selection_page_assets_scatter": "Egy elem tÃļbb albumban is lehet. EzÊrt a mentÊshez albumokat lehet hozzÃĄadni vagy azokat a mentÊsből kihagyni.", "backup_album_selection_page_select_albums": "VÃĄlassz albumokat", @@ -506,22 +505,21 @@ "backup_all": "Összes", "backup_background_service_backup_failed_message": "Az elemek mentÊse sikertelen. ÚjraprÃŗbÃĄlkozÃĄs...", "backup_background_service_connection_failed_message": "A szerverhez csatlakozÃĄs sikertelen. ÚjraprÃŗbÃĄlkozÃĄs...", - "backup_background_service_current_upload_notification": "FeltÃļltÊs {}", + "backup_background_service_current_upload_notification": "FeltÃļltÊs {filename}", "backup_background_service_default_notification": "Új elemek ellenőrzÊse...", "backup_background_service_error_title": "Hiba a mentÊs kÃļzben", "backup_background_service_in_progress_notification": "Elemek mentÊse folyamatbanâ€Ļ", - "backup_background_service_upload_failure_notification": "A feltÃļltÊs sikertelen {}", + "backup_background_service_upload_failure_notification": "A feltÃļltÊs sikertelen {filename}", "backup_controller_page_albums": "Albumok MentÊse", "backup_controller_page_background_app_refresh_disabled_content": "EngedÊlyezd a hÃĄttÊrben tÃļrtÊnő frissítÊst a BeÃĄllítÃĄsok > ÁltalÃĄnos > HÃĄttÊrben FrissítÊs menÃŧpontban.", "backup_controller_page_background_app_refresh_disabled_title": "HÃĄttÊrben frissítÊs kikapcsolva", "backup_controller_page_background_app_refresh_enable_button_text": "BeÃĄllítÃĄsok megnyitÃĄsa", "backup_controller_page_background_battery_info_link": "Mutasd meg hogyan", "backup_controller_page_background_battery_info_message": "A sikeres hÃĄttÊrben tÃļrtÊnő mentÊshez kÊrjÃŧk, tiltsd le az Immich akkumulÃĄtor optimalizÃĄlÃĄsÃĄt.\n\nMivel ezt a kÃŧlÃļnfÊle eszkÃļzÃļkÃļn mÃĄshogy kell, ezÊrt kÊrjÃŧk, az eszkÃļzÃļd gyÃĄrtÃŗjÃĄtÃŗl tudd meg, hogyan kell.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "AkkumulÃĄtor optimalizÃĄlÃĄs", "backup_controller_page_background_charging": "Csak tÃļltÊs kÃļzben", "backup_controller_page_background_configure_error": "A hÃĄttÊrszolgÃĄltatÃĄs beÃĄllítÃĄsa sikertelen", - "backup_controller_page_background_delay": "Új elemek mentÊsÊnek kÊsleltetÊse: {}", + "backup_controller_page_background_delay": "Új elemek mentÊsÊnek kÊsleltetÊse: {duration}", "backup_controller_page_background_description": "Kapcsold be a hÃĄttÊrfolyamatot, hogy automatikusan mentsen elemeket az applikÃĄciÃŗ megnyitÃĄsa nÊlkÃŧl", "backup_controller_page_background_is_off": "Automatikus mentÊs a hÃĄttÊrben ki van kapcsolva", "backup_controller_page_background_is_on": "Automatikus mentÊs a hÃĄttÊrben be van kapcsolva", @@ -529,14 +527,14 @@ "backup_controller_page_background_turn_on": "HÃĄttÊrszolgÃĄltatÃĄs bekapcsolÃĄsa", "backup_controller_page_background_wifi": "Csak WiFi-n", "backup_controller_page_backup": "MentÊs", - "backup_controller_page_backup_selected": "KivÃĄlasztva:", + "backup_controller_page_backup_selected": "KivÃĄlasztva: ", "backup_controller_page_backup_sub": "Mentett fotÃŗk Ês videÃŗk", - "backup_controller_page_created": "LÊtrehozva: {}", + "backup_controller_page_created": "LÊtrehozva: {date}", "backup_controller_page_desc_backup": "Ha bekapcsolod az előtÊrben mentÊst, akkor az Ãēj elemek automatikusan feltÃļltődnek a szerverre, amikor megyitod az alkalmazÃĄst.", - "backup_controller_page_excluded": "KivÊve:", - "backup_controller_page_failed": "Sikertelen ({})", - "backup_controller_page_filename": "FÃĄjlnÊv: {}[{}]", - "backup_controller_page_id": "AzonosítÃŗ: {}", + "backup_controller_page_excluded": "KivÊve: ", + "backup_controller_page_failed": "Sikertelen ({count})", + "backup_controller_page_filename": "FÃĄjlnÊv: {filename}[{size}]", + "backup_controller_page_id": "AzonosítÃŗ: {id}", "backup_controller_page_info": "MentÊsi InformÃĄciÃŗk", "backup_controller_page_none_selected": "Egy sincs kivÃĄlasztva", "backup_controller_page_remainder": "HÃĄtralÊvő", @@ -545,7 +543,7 @@ "backup_controller_page_start_backup": "MentÊs IndítÃĄsa", "backup_controller_page_status_off": "Automatikus mentÊs az előtÊrben ki van kapcsolva", "backup_controller_page_status_on": "Automatikus mentÊs az előtÊrben be van kapcsolva", - "backup_controller_page_storage_format": "{} / {} felhasznÃĄlva", + "backup_controller_page_storage_format": "{used} / {total} felhasznÃĄlva", "backup_controller_page_to_backup": "MentÊsre kijelÃļlt albumok", "backup_controller_page_total_sub": "Minden egyedi fotÃŗ Ês videÃŗ a kijelÃļlt albumokbÃŗl", "backup_controller_page_turn_off": "ElőtÊrben mentÊs kikapcsolÃĄsa", @@ -564,27 +562,26 @@ "birthdate_set_description": "A szÃŧletÊs napjÃĄt a rendszer arra hasznÃĄlja, hogy kiírja, hogy a fÊnykÊp kÊszítÊsekor a szemÊly hÃĄny Êves volt.", "blurred_background": "HomÃĄlyos hÃĄttÊr", "bugs_and_feature_requests": "HibabejelentÊs Ês Új FunkciÃŗ KÊrÊse", - "build": "Build", "build_image": "Build KÊp", "bulk_delete_duplicates_confirmation": "Biztosan kitÃļrÃļlsz {count, plural, one {# duplikÃĄlt elemet} other {# duplikÃĄlt elemet}}? A mÅąvelet a legnagyobb mÊretÅą elemet tartja meg minden hasonlÃŗ csoportbÃŗl Ês minden mÃĄsik duplikÃĄlt elemet kitÃļrÃļl. Ez a mÅąvelet nem visszavonhatÃŗ!", "bulk_keep_duplicates_confirmation": "Biztosan meg szeretnÊl tartani {count, plural, other {# egyező elemet}}? Ez a mÅąvelet az elemek tÃļrlÊse nÊlkÃŧl megszÃŧnteti az Ãļsszes duplikÃĄlt csoportosítÃĄst.", "bulk_trash_duplicates_confirmation": "Biztosan kitÃļrÃļlsz {count, plural, one {# duplikÃĄlt fÃĄjlt} other {# duplikÃĄlt fÃĄjlt}}? Ez a mÅąvelet megtartja minden csoportbÃŗl a legnagyobb mÊretÅą elemet, Ês kitÃļrÃļl minden mÃĄsik duplikÃĄltat.", "buy": "Immich MegvÃĄsÃĄrlÃĄsa", - "cache_settings_album_thumbnails": "KÊptÃĄr oldalankÊnti bÊlyegkÊpei ({} elem)", + "cache_settings_album_thumbnails": "KÊptÃĄr oldalankÊnti bÊlyegkÊpei ({count} elem)", "cache_settings_clear_cache_button": "GyorsítÃŗtÃĄr kiÃŧrítÊse", "cache_settings_clear_cache_button_title": "KiÃŧríti az alkalmazÃĄs gyorsítÃŗtÃĄrÃĄt. Ez jelentősen kihat az alkalmazÃĄs teljesítmÊnyÊre, amíg a gyorsítÃŗtÃĄr Ãējra nem ÊpÃŧl.", "cache_settings_duplicated_assets_clear_button": "KIÜRÍT", "cache_settings_duplicated_assets_subtitle": "FotÃŗk Ês videÃŗk, amiket az alkalmazÃĄs fekete listÃĄra tett", - "cache_settings_duplicated_assets_title": "DuplikÃĄlt Elemek ({})", - "cache_settings_image_cache_size": "KÊp gyorsítÃŗtÃĄr mÊrete ({} elem)", + "cache_settings_duplicated_assets_title": "DuplikÃĄlt Elemek ({count})", + "cache_settings_image_cache_size": "KÊp gyorsítÃŗtÃĄr mÊrete ({count} elem)", "cache_settings_statistics_album": "KÊptÃĄr bÊlyegkÊpei", - "cache_settings_statistics_assets": "{} elem ({})", + "cache_settings_statistics_assets": "{count} elem ({size})", "cache_settings_statistics_full": "Teljes mÊretÅą kÊpek", "cache_settings_statistics_shared": "Megosztott album bÊlyegkÊpei", "cache_settings_statistics_thumbnail": "BÊlyegkÊpek", "cache_settings_statistics_title": "GyorsítÃŗtÃĄr hasznÃĄlata", "cache_settings_subtitle": "Az Immich mobilalkalmazÃĄs gyorsítÃŗtÃĄr viselkedÊsÊnek beÃĄllítÃĄsa", - "cache_settings_thumbnail_size": "BÊlyegkÊp gyorsítÃŗtÃĄr mÊrete ({} elem)", + "cache_settings_thumbnail_size": "BÊlyegkÊp gyorsítÃŗtÃĄr mÊrete ({count} elem)", "cache_settings_tile_subtitle": "Helyi tÃĄrhely viselkedÊsÊnek beÃĄllítÃĄsa", "cache_settings_tile_title": "Helyi TÃĄrhely", "cache_settings_title": "GyorsítÃŗtÃĄr BeÃĄllítÃĄsok", @@ -610,6 +607,7 @@ "change_password_form_new_password": "Új JelszÃŗ", "change_password_form_password_mismatch": "A beírt jelszavak nem egyeznek", "change_password_form_reenter_new_password": "JelszÃŗ (MÊg Egyszer)", + "change_pin_code": "PIN kÃŗd megvÃĄltoztatÃĄsa", "change_your_password": "Jelszavad megvÃĄltoztatÃĄsa", "changed_visibility_successfully": "LÃĄthatÃŗsÃĄg sikeresen megvÃĄltoztatva", "check_all": "Mind KijelÃļl", @@ -624,7 +622,6 @@ "clear_all_recent_searches": "LegutÃŗbbi keresÊsek tÃļrlÊse", "clear_message": "Üzenet tÃļrlÊse", "clear_value": "ÉrtÊk tÃļrlÊse", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "JelszÃŗ MegadÃĄsa", "client_cert_import": "ImportÃĄlÃĄs", "client_cert_import_success_msg": "Kliens tanÃēsítvÃĄny importÃĄlva", @@ -650,17 +647,17 @@ "confirm_delete_face": "Biztos, hogy tÃļrÃļlni szeretnÊd a(z) {name} arcÃĄt az elemről?", "confirm_delete_shared_link": "Biztosan tÃļrÃļlni szeretnÊd ezt a megosztott linket?", "confirm_keep_this_delete_others": "Minden mÃĄs elem a kÊszletben tÃļrlÊsre kerÃŧl, kivÊve ezt az elemet. Biztosan folytatni szeretnÊd?", + "confirm_new_pin_code": "Új PIN kÃŗd megerősítÊse", "confirm_password": "JelszÃŗ megerősítÊse", "contain": "BelÃŧl", "context": "Kontextus", "continue": "FolytatÃĄs", - "control_bottom_app_bar_album_info_shared": "{} elemek ¡ Megosztva", + "control_bottom_app_bar_album_info_shared": "{count} elemek ¡ Megosztva", "control_bottom_app_bar_create_new_album": "Új album lÊtrehozÃĄsa", "control_bottom_app_bar_delete_from_immich": "TÃļrlÊs az Immich-ből", "control_bottom_app_bar_delete_from_local": "TÃļrlÊs az eszkÃļzről", "control_bottom_app_bar_edit_location": "Hely MÃŗdosítÃĄsa", "control_bottom_app_bar_edit_time": "DÃĄtum Ês Idő MÃŗdosítÃĄsa", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "MegosztÃĄs Ide", "control_bottom_app_bar_trash_from_immich": "LomtÃĄrba Helyez", "copied_image_to_clipboard": "KÊp a vÃĄgÃŗlapra mÃĄsolva.", @@ -695,6 +692,7 @@ "crop": "KivÃĄgÃĄs", "curated_object_page_title": "Dolgok", "current_device": "Ez az eszkÃļz", + "current_pin_code": "AktuÃĄlis PIN kÃŗd", "current_server_address": "Jelenlegi szerver cím", "custom_locale": "EgyÊni TerÃŧleti BeÃĄllítÃĄs", "custom_locale_description": "DÃĄtumok Ês szÃĄmok formÃĄzÃĄsa a nyelv Ês terÃŧlet szerint", @@ -746,7 +744,6 @@ "direction": "IrÃĄny", "disabled": "Letiltott", "disallow_edits": "MÃŗdosítÃĄsok letiltÃĄsa", - "discord": "Discord", "discover": "Felfedez", "dismiss_all_errors": "Minden hiba elvetÊse", "dismiss_error": "Hiba elvetÊse", @@ -763,7 +760,7 @@ "download_enqueue": "LetÃļltÊs sorba ÃĄllítva", "download_error": "LetÃļltÊsi Hiba", "download_failed": "Sikertelen letÃļltÊs", - "download_filename": "fÃĄjl: {}", + "download_filename": "fÃĄjl: {filename}", "download_finished": "LetÃļltÊs kÊsz", "download_include_embedded_motion_videos": "BeÃĄgyazott videÃŗk", "download_include_embedded_motion_videos_description": "MozgÃŗ kÊpekbe beÃĄgyazott videÃŗk mutatÃĄsa kÃŧlÃļn fÃĄjlkÊnt", @@ -806,8 +803,6 @@ "editor_close_without_save_title": "Szerkesztő bezÃĄrÃĄsa?", "editor_crop_tool_h2_aspect_ratios": "OldalarÃĄnyok", "editor_crop_tool_h2_rotation": "ForgatÃĄs", - "email": "Email", - "empty_folder": "This folder is empty", "empty_trash": "LomtÃĄr ÃŧrítÊse", "empty_trash_confirmation": "Biztosan kiÃŧríted a lomtÃĄrat? Ez az Immich lomtÃĄrÃĄban lÊvő Ãļsszes elemet vÊglegesen tÃļrli.\nEz a mÅąvelet nem visszavonhatÃŗ!", "enable": "EngedÊlyezÊs", @@ -819,7 +814,7 @@ "error_change_sort_album": "Album sorbarendezÊsÊnek megvÃĄltoztatÃĄsa sikertelen", "error_delete_face": "Hiba az arc tÃļrlÊse sorÃĄn", "error_loading_image": "Hiba a kÊp betÃļltÊse kÃļzben", - "error_saving_image": "Hiba: {}", + "error_saving_image": "Hiba: {error}", "error_title": "Hiba - valami fÊlresikerÃŧlt", "errors": { "cannot_navigate_next_asset": "Nem lehet a kÃļvetkező elemhez navigÃĄlni", @@ -947,16 +942,11 @@ "unable_to_update_user": "FelhasznÃĄlÃŗ mÃŗdosítÃĄsa sikertelen", "unable_to_upload_file": "FÃĄjlfeltÃļltÊs sikertelen" }, - "exif": "Exif", "exif_bottom_sheet_description": "LeírÃĄs HozzÃĄadÃĄsa...", "exif_bottom_sheet_details": "RÉSZLETEK", "exif_bottom_sheet_location": "HELY", "exif_bottom_sheet_people": "EMBEREK", "exif_bottom_sheet_person_add_person": "Elnevez", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "KilÊpÊs a DiavetítÊsből", "expand_all": "Összes kinyitÃĄsa", "experimental_settings_new_asset_list_subtitle": "FejlesztÊs alatt", @@ -978,7 +968,6 @@ "face_unassigned": "Nincs hozzÃĄrendelve", "failed": "Sikertelen", "failed_to_load_assets": "Nem sikerÃŧlt betÃļlteni az elemeket", - "failed_to_load_folder": "Failed to load folder", "favorite": "Kedvenc", "favorite_or_unfavorite_photo": "FotÃŗ kedvencnek jelÃļlÊse vagy annak visszavonÃĄsa", "favorites": "Kedvencek", @@ -994,8 +983,6 @@ "filter_people": "SzemÊlyek szÅąrÊse", "find_them_fast": "NÊv alapjÃĄn keresÊssel gyorsan megtalÃĄlhatÃŗak", "fix_incorrect_match": "HibÃĄs talÃĄlat javítÃĄsa", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "MappÃĄk", "folders_feature_description": "A fÃĄjlrendszerben lÊvő fÊnykÊpek Ês videÃŗk mappanÊzetben valÃŗ bÃļngÊszÊse", "forward": "Előre", @@ -1170,8 +1157,8 @@ "manage_your_devices": "Bejelentkezett eszkÃļzÃļk kezelÊse", "manage_your_oauth_connection": "OAuth kapcsolÃŗdÃĄs kezelÊse", "map": "TÊrkÊp", - "map_assets_in_bound": "{} fotÃŗ", - "map_assets_in_bounds": "{} fotÃŗ", + "map_assets_in_bound": "{count} fotÃŗ", + "map_assets_in_bounds": "{count} fotÃŗ", "map_cannot_get_user_location": "A helymeghatÃĄrozÃĄs nem sikerÃŧlt", "map_location_dialog_yes": "Igen", "map_location_picker_page_use_location": "KivÃĄlasztott hely hasznÃĄlata", @@ -1185,9 +1172,9 @@ "map_settings": "TÊrkÊp beÃĄllítÃĄsok", "map_settings_dark_mode": "SÃļtÊt tÊma", "map_settings_date_range_option_day": "ElmÃēlt 24 Ãŗra", - "map_settings_date_range_option_days": "ElmÃēlt {} nap", + "map_settings_date_range_option_days": "ElmÃēlt {days} nap", "map_settings_date_range_option_year": "ElmÃēlt Êv", - "map_settings_date_range_option_years": "ElmÃēlt {} Êv", + "map_settings_date_range_option_years": "ElmÃēlt {years} Êv", "map_settings_dialog_title": "TÊrkÊp BeÃĄllítÃĄsok", "map_settings_include_show_archived": "Archívokkal EgyÃŧtt", "map_settings_include_show_partners": "PartnerÊvel EgyÃŧtt", @@ -1202,8 +1189,6 @@ "memories_setting_description": "Állítsd be, hogy mik jelenjenek meg az emlÊkeid kÃļzt", "memories_start_over": "ÚjrakezdÊs", "memories_swipe_to_close": "BezÃĄrÃĄshoz sÃļpÃļrd ki felfelÊ", - "memories_year_ago": "Egy Êve", - "memories_years_ago": "{} Êve", "memory": "EmlÊk", "memory_lane_title": "EmlÊkek {title}", "menu": "MenÃŧ", @@ -1234,6 +1219,7 @@ "new_api_key": "Új API Kulcs", "new_password": "Új jelszÃŗ", "new_person": "Új szemÊly", + "new_pin_code": "Új PIN kÃŗd", "new_user_created": "Új felhasznÃĄlÃŗ lÊtrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "LegÃējabb előszÃļr", @@ -1267,9 +1253,7 @@ "notification_toggle_setting_description": "Email ÊrtesítÊsek engedÊlyezÊse", "notifications": "ÉrtesítÊsek", "notifications_setting_description": "ÉrtesítÊsek kezelÊse", - "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich ForrÃĄsok", - "offline": "Offline", "offline_paths": "Offline Ãētvonalak", "offline_paths_description": "Ezek az eredmÊnyek annak lehetnek kÃļszÃļnhetők, hogy manuÃĄlisan tÃļrÃļltÊk azokat a fÃĄjlokat, amik nem rÊszei egy kÃŧlső kÊptÃĄrnak.", "ok": "Rendben", @@ -1280,7 +1264,6 @@ "onboarding_theme_description": "VÃĄlassz egy színtÊmÃĄt. Ezt bÃĄrmikor megvÃĄltoztathatod a beÃĄllítÃĄsokban.", "onboarding_welcome_description": "Állítsunk be nÊhÃĄny gyakori beÃĄllítÃĄst.", "onboarding_welcome_user": "ÜdvÃļzÃļllek {user}", - "online": "Online", "only_favorites": "Csak kedvencek", "open_in_map_view": "MegnyitÃĄs tÊrkÊp nÊzetben", "open_in_openstreetmap": "MegnyitÃĄs OpenStreetMap-ben", @@ -1294,7 +1277,6 @@ "other_variables": "EgyÊb vÃĄltozÃŗk", "owned": "Tulajdonos", "owner": "Tulajdonos", - "partner": "Partner", "partner_can_access": "{partner} hozzÃĄfÊrhet", "partner_can_access_assets": "Minden fÊnykÊped Ês videÃŗd, kivÊve az ArchivÃĄltak Ês a TÃļrÃļltek", "partner_can_access_location": "A helyszín, ahol a fotÃŗkat kÊszítettÊk", @@ -1305,7 +1287,7 @@ "partner_page_partner_add_failed": "Partner hozzÃĄadÃĄsa sikertelen", "partner_page_select_partner": "Partner kivÃĄlasztÃĄsa", "partner_page_shared_to_title": "Megosztva: ", - "partner_page_stop_sharing_content": "{} nem fog tÃļbbÊ hozzÃĄfÊrni a fotÃŗidhoz.", + "partner_page_stop_sharing_content": "{partner} nem fog tÃļbbÊ hozzÃĄfÊrni a fotÃŗidhoz.", "partner_sharing": "Partner MegosztÃĄs", "partners": "Partnerek", "password": "JelszÃŗ", @@ -1351,6 +1333,9 @@ "photos_count": "{count, plural, one {{count, number} FotÃŗ} other {{count, number} FotÃŗ}}", "photos_from_previous_years": "FÊnykÊpek az előző Êvekből", "pick_a_location": "Hely vÃĄlasztÃĄsa", + "pin_code_changed_successfully": "Sikeres PIN kÃŗd vÃĄltoztatÃĄs", + "pin_code_reset_successfully": "Sikeres PIN kÃŗd visszaÃĄllítÃĄs", + "pin_code_setup_successfully": "Sikeres PIN kÃŗd beÃĄllítÃĄs", "place": "Hely", "places": "Helyek", "places_count": "{count, plural, one {{count, number} Helyszín} other {{count, number} Helyszín}}", @@ -1358,7 +1343,6 @@ "play_memories": "EmlÊkek lejÃĄtszÃĄsa", "play_motion_photo": "MozgÃŗkÊp lejÃĄtszÃĄsa", "play_or_pause_video": "VideÃŗ elindítÃĄsa vagy megÃĄllítÃĄsa", - "port": "Port", "preferences_settings_subtitle": "AlkalmazÃĄsbeÃĄllítÃĄsok kezelÊse", "preferences_settings_title": "BeÃĄllítÃĄsok", "preset": "Sablon", @@ -1372,7 +1356,6 @@ "profile_drawer_client_out_of_date_major": "A mobilalkalmazÃĄs elavult. KÊrjÃŧk, frissítsd a legfrisebb főverziÃŗra.", "profile_drawer_client_out_of_date_minor": "A mobilalkalmazÃĄs elavult. KÊrjÃŧk, frissítsd a legfrisebb alverziÃŗra.", "profile_drawer_client_server_up_to_date": "A Kliens Ês a Szerver is naprakÊsz", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "A szerver elavult. KÊrjÃŧk, frissítsd a legfrisebb főverziÃŗra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. KÊrjÃŧk, frissítsd a legfrisebb alverziÃŗra.", "profile_image_of_user": "{user} profilkÊpe", @@ -1381,7 +1364,7 @@ "public_share": "NyilvÃĄnos MegosztÃĄs", "purchase_account_info": "TÃĄmogatÃŗ", "purchase_activated_subtitle": "KÃļszÃļnjÃŧk, hogy tÃĄmogattad az Immich-et Ês a nyílt forrÃĄskÃŗdÃē szoftvereket", - "purchase_activated_time": "AktivÃĄlva ekkor: {date, date}", + "purchase_activated_time": "AktivÃĄlva ekkor: {date}", "purchase_activated_title": "Kulcs sikeresen aktivÃĄlva", "purchase_button_activate": "AktivÃĄlÃĄs", "purchase_button_buy": "VÃĄsÃĄrlÃĄs", @@ -1468,6 +1451,7 @@ "reset": "VisszaÃĄllítÃĄs", "reset_password": "JelszÃŗ visszaÃĄllítÃĄsa", "reset_people_visibility": "SzemÊlyek lÃĄthatÃŗsÃĄgÃĄnak visszaÃĄllítÃĄsa", + "reset_pin_code": "PIN kÃŗd visszaÃĄllítÃĄsa", "reset_to_default": "VisszaÃĄllítÃĄs alapÃĄllapotba", "resolve_duplicates": "DuplikÃĄtumok feloldÃĄsa", "resolved_all_duplicates": "Minden duplikÃĄtum feloldÃĄsa", @@ -1540,7 +1524,7 @@ "search_result_page_new_search_hint": "Új KeresÊs", "search_settings": "KeresÊsi beÃĄllítÃĄsok", "search_state": "Megye/Állam keresÊse...", - "search_suggestion_list_smart_search_hint_1": "Az intelligens keresÊs alapÊrtelmezetten be van kapcsolva, metaadatokat így kereshetsz:", + "search_suggestion_list_smart_search_hint_1": "Az intelligens keresÊs alapÊrtelmezetten be van kapcsolva, metaadatokat így kereshetsz: ", "search_suggestion_list_smart_search_hint_2": "m:keresÊsi-kifejezÊs", "search_tags": "CímkÊk keresÊse...", "search_timezone": "IdőzÃŗna keresÊse...", @@ -1590,12 +1574,12 @@ "setting_languages_apply": "Alkalmaz", "setting_languages_subtitle": "Az alkalmazÃĄs nyelvÊnek megvÃĄltoztatÃĄsa", "setting_languages_title": "Nyelvek", - "setting_notifications_notify_failures_grace_period": "ÉrtesítÊs a hÃĄttÊrben tÃļrtÊnő mentÊs hibÃĄirÃŗl: {}", - "setting_notifications_notify_hours": "{} Ãŗra", + "setting_notifications_notify_failures_grace_period": "ÉrtesítÊs a hÃĄttÊrben tÃļrtÊnő mentÊs hibÃĄirÃŗl: {duration}", + "setting_notifications_notify_hours": "{count} Ãŗra", "setting_notifications_notify_immediately": "azonnal", - "setting_notifications_notify_minutes": "{} perc", + "setting_notifications_notify_minutes": "{count} perc", "setting_notifications_notify_never": "soha", - "setting_notifications_notify_seconds": "{} mÃĄsodperc", + "setting_notifications_notify_seconds": "{count} mÃĄsodperc", "setting_notifications_single_progress_subtitle": "RÊszletes feltÃļltÊsi folyamat informÃĄciÃŗ minden elemről", "setting_notifications_single_progress_title": "Mutassa a hÃĄttÊrben tÃļrtÊnő mentÊs rÊszletes folyamatÃĄt", "setting_notifications_subtitle": "ÉrtesítÊsi beÃĄllítÃĄsok mÃŗdosítÃĄsa", @@ -1607,9 +1591,10 @@ "settings": "BeÃĄllítÃĄsok", "settings_require_restart": "Ennek a beÃĄllítÃĄsnak az ÊrvÊnybe lÊpÊsÊhez indítsd Ãējra az Immich-et", "settings_saved": "BeÃĄllítÃĄsok elmentve", + "setup_pin_code": "PIN kÃŗd beÃĄllítÃĄsa", "share": "MegosztÃĄs", "share_add_photos": "FotÃŗk hozzÃĄadÃĄsa", - "share_assets_selected": "{} kivÃĄlasztva", + "share_assets_selected": "{count} kivÃĄlasztva", "share_dialog_preparing": "ElőkÊszítÊs...", "shared": "Megosztva", "shared_album_activities_input_disable": "HozzÃĄszÃŗlÃĄsok kikapcsolva", @@ -1623,34 +1608,33 @@ "shared_by_user": "{user} osztotta meg", "shared_by_you": "Te osztottad meg", "shared_from_partner": "{partner} fÊnykÊpei", - "shared_intent_upload_button_progress_text": "{} / {} FeltÃļltve", + "shared_intent_upload_button_progress_text": "{current} / {total} FeltÃļltve", "shared_link_app_bar_title": "Megosztott Linkek", "shared_link_clipboard_copied_massage": "VÃĄgÃŗlapra mÃĄsolva", - "shared_link_clipboard_text": "Link: {}\nJelszÃŗ: {}", + "shared_link_clipboard_text": "Link: {link}\nJelszÃŗ: {password}", "shared_link_create_error": "Hiba a megosztott link lÊtrehozÃĄsakor", "shared_link_edit_description_hint": "Add meg a megosztÃĄs leírÃĄsÃĄt", "shared_link_edit_expire_after_option_day": "1 nap", - "shared_link_edit_expire_after_option_days": "{} nap", + "shared_link_edit_expire_after_option_days": "{count} nap", "shared_link_edit_expire_after_option_hour": "1 Ãŗra", - "shared_link_edit_expire_after_option_hours": "{} Ãŗra", + "shared_link_edit_expire_after_option_hours": "{count} Ãŗra", "shared_link_edit_expire_after_option_minute": "1 perc", - "shared_link_edit_expire_after_option_minutes": "{} perc", - "shared_link_edit_expire_after_option_months": "{} hÃŗnap", - "shared_link_edit_expire_after_option_year": "{} Êv", + "shared_link_edit_expire_after_option_minutes": "{count} perc", + "shared_link_edit_expire_after_option_months": "{count} hÃŗnap", + "shared_link_edit_expire_after_option_year": "{count} Êv", "shared_link_edit_password_hint": "Add meg a megosztÃĄshoz tartozÃŗ jelszÃŗt", "shared_link_edit_submit_button": "Link frissítÊse", "shared_link_error_server_url_fetch": "A szerver címÊt nem lehet betÃļlteni", - "shared_link_expires_day": "{} nap mÃēlva lejÃĄr", - "shared_link_expires_days": "{} nap mÃēlva lejÃĄr", - "shared_link_expires_hour": "{} Ãŗra mÃēlva lejÃĄr", - "shared_link_expires_hours": "{} Ãŗra mÃēlva lejÃĄr", - "shared_link_expires_minute": "{} perc mÃēlva lejÃĄr", - "shared_link_expires_minutes": "{} perc mÃēlva lejÃĄr", + "shared_link_expires_day": "{count} nap mÃēlva lejÃĄr", + "shared_link_expires_days": "{count} nap mÃēlva lejÃĄr", + "shared_link_expires_hour": "{count} Ãŗra mÃēlva lejÃĄr", + "shared_link_expires_hours": "{count} Ãŗra mÃēlva lejÃĄr", + "shared_link_expires_minute": "{count} perc mÃēlva lejÃĄr", + "shared_link_expires_minutes": "{count} perc mÃēlva lejÃĄr", "shared_link_expires_never": "Nem jÃĄr le", - "shared_link_expires_second": "{} mÃĄsodperc mÃēlva lejÃĄr", - "shared_link_expires_seconds": "{} mÃĄsodperc mÃēlva lejÃĄr", + "shared_link_expires_second": "{count} mÃĄsodperc mÃēlva lejÃĄr", + "shared_link_expires_seconds": "{count} mÃĄsodperc mÃēlva lejÃĄr", "shared_link_individual_shared": "EgyÊnileg megosztva", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Megosztott linkek kezelÊse", "shared_link_options": "Megosztott link beÃĄllítÃĄsai", "shared_links": "Megosztott linkek", @@ -1712,7 +1696,6 @@ "stack_select_one_photo": "VÃĄlassz egy fő kÊpet a csoportbÃŗl", "stack_selected_photos": "KivÃĄlasztott fÊnykÊpek csoportosítÃĄsa", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", - "stacktrace": "Stacktrace", "start": "Elindít", "start_date": "Kezdő dÃĄtum", "state": "Megye/Állam", @@ -1749,7 +1732,7 @@ "theme_selection": "TÊmavÃĄlasztÃĄs", "theme_selection_description": "A bÃļngÊsző beÃĄllítÃĄsÃĄnak megfelelően automatikusan hasznÃĄljon vilÃĄgos vagy sÃļtÊt tÊmÃĄt", "theme_setting_asset_list_storage_indicator_title": "TÃĄrhely ikon mutatÃĄsa az elemeken", - "theme_setting_asset_list_tiles_per_row_title": "Elemek szÃĄma soronkÊnt ({})", + "theme_setting_asset_list_tiles_per_row_title": "Elemek szÃĄma soronkÊnt ({count})", "theme_setting_colorful_interface_subtitle": "AlapÊrtelmezett szín hasznÃĄlata a hÃĄttÊrben lÊvő felÃŧletekhez", "theme_setting_colorful_interface_title": "Színes felhasznÃĄlÃŗi felÃŧlet", "theme_setting_image_viewer_quality_subtitle": "RÊszletes kÊpmegjelenítő minősÊgÊnek beÃĄllítÃĄsa", @@ -1784,13 +1767,15 @@ "trash_no_results_message": "Itt lesznek lÃĄthatÃŗak a lomtÃĄrba tett kÊpek Ês videÃŗk.", "trash_page_delete_all": "Mindet TÃļrÃļl", "trash_page_empty_trash_dialog_content": "Ki szeretnÊd Ãŧríteni a lomtÃĄrban lÊvő elemeket? Ezeket vÊglegesen eltÃĄvolítjuk az Immich-ből", - "trash_page_info": "A LomÃĄtrba helyezett elemek {} nap utÃĄn vÊglegesen tÃļrlődnek", + "trash_page_info": "A LomÃĄtrba helyezett elemek {days} nap utÃĄn vÊglegesen tÃļrlődnek", "trash_page_no_assets": "A LomtÃĄr Ãŧres", "trash_page_restore_all": "Mindet VisszaÃĄllít", "trash_page_select_assets_btn": "Elemek kivÃĄlasztÃĄsa", - "trash_page_title": "LomtÃĄr ({})", + "trash_page_title": "LomtÃĄr ({count})", "trashed_items_will_be_permanently_deleted_after": "A lomtÃĄrban lÊvő elemek vÊglegesen tÃļrlÊsre kerÃŧlnek {days, plural, other {# nap}} mÃēlva.", "type": "Típus", + "unable_to_change_pin_code": "Sikertelen PIN kÃŗd vÃĄltoztatÃĄs", + "unable_to_setup_pin_code": "Sikertelen PIN kÃŗd beÃĄllítÃĄs", "unarchive": "ArchívumbÃŗl kivesz", "unarchived_count": "{count, plural, other {# elem kivÊve az archívumbÃŗl}}", "unfavorite": "Kedvenc kÃļzÃŧl kivesz", @@ -1826,15 +1811,16 @@ "upload_status_errors": "HibÃĄk", "upload_status_uploaded": "FeltÃļltve", "upload_success": "FeltÃļltÊs sikeres, frissítsd az oldalt az Ãējonnan feltÃļltÃļtt elemek megtekintÊsÊhez.", - "upload_to_immich": "FeltÃļltÊs Immich-be ({})", + "upload_to_immich": "FeltÃļltÊs Immich-be ({count})", "uploading": "FeltÃļltÊs folyamatban", - "url": "URL", "usage": "HasznÃĄlat", "use_current_connection": "Jelenlegi kapcsolat hasznÃĄlata", "use_custom_date_range": "Szabadon megadott időintervallum hasznÃĄlata", "user": "FelhasznÃĄlÃŗ", "user_id": "FelhasznÃĄlÃŗ azonosítÃŗja", "user_liked": "{user} felhasznÃĄlÃŗnak {type, select, photo {ez a fÊnykÊp} video {ez a videÃŗ} asset {ez az elem} other {ez}} tetszik", + "user_pin_code_settings": "PIN kÃŗd", + "user_pin_code_settings_description": "PIN kÃŗd kezelÊse", "user_purchase_settings": "MegvÃĄsÃĄrlÃĄs", "user_purchase_settings_description": "VÃĄsÃĄrlÃĄs kezelÊse", "user_role_set": "{user} felhasznÃĄlÃŗnak {role} jogkÃļr biztosítÃĄsa", diff --git a/i18n/hy.json b/i18n/hy.json index 000fcaa2e1..76adfb3778 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -1,867 +1,74 @@ { - "account": "", - "account_settings": "", - "acknowledge": "", - "action": "", - "actions": "", - "active": "", - "activity": "", - "add": "", - "add_a_description": "", - "add_a_location": "", - "add_a_name": "", - "add_a_title": "", - "add_exclusion_pattern": "", - "add_import_path": "", - "add_location": "", - "add_more_users": "", - "add_partner": "", - "add_path": "", - "add_photos": "", - "add_to": "", - "add_to_album": "", - "add_to_shared_album": "", - "admin": { - "add_exclusion_pattern_description": "", - "authentication_settings": "", - "authentication_settings_description": "", - "background_task_job": "", - "check_all": "", - "config_set_by_file": "", - "confirm_delete_library": "", - "confirm_delete_library_assets": "", - "confirm_email_below": "", - "confirm_reprocess_all_faces": "", - "confirm_user_password_reset": "", - "disable_login": "", - "duplicate_detection_job_description": "", - "exclusion_pattern_description": "", - "external_library_created_at": "", - "external_library_management": "", - "face_detection": "", - "face_detection_description": "", - "facial_recognition_job_description": "", - "force_delete_user_warning": "", - "forcing_refresh_library_files": "", - "image_format_description": "", - "image_prefer_embedded_preview": "", - "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", - "image_prefer_wide_gamut_setting_description": "", - "image_quality": "", - "image_settings": "", - "image_settings_description": "", - "job_concurrency": "", - "job_not_concurrency_safe": "", - "job_settings": "", - "job_settings_description": "", - "job_status": "", - "jobs_delayed": "", - "jobs_failed": "", - "library_created": "", - "library_deleted": "", - "library_import_path_description": "", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", - "library_settings_description": "", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", - "machine_learning_clip_model": "", - "machine_learning_duplicate_detection": "", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled_description": "", - "machine_learning_facial_recognition": "", - "machine_learning_facial_recognition_description": "", - "machine_learning_facial_recognition_model": "", - "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", - "machine_learning_settings": "", - "machine_learning_settings_description": "", - "machine_learning_smart_search": "", - "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_concurrency": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", - "map_settings": "", - "map_settings_description": "", - "map_style_description": "", - "metadata_extraction_job": "", - "metadata_extraction_job_description": "", - "migration_job": "", - "migration_job_description": "", - "no_paths_added": "", - "no_pattern_added": "", - "note_apply_storage_label_previous_assets": "", - "note_cannot_be_changed_later": "", - "notification_email_from_address": "", - "notification_email_from_address_description": "", - "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", - "notification_email_ignore_certificate_errors_description": "", - "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", - "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", - "oauth_mobile_redirect_uri_override": "", - "oauth_mobile_redirect_uri_override_description": "", - "oauth_scope": "", - "oauth_settings": "", - "oauth_settings_description": "", - "oauth_signing_algorithm": "", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", - "oauth_storage_quota_default": "", - "oauth_storage_quota_default_description": "", - "offline_paths": "", - "offline_paths_description": "", - "password_enable_description": "", - "password_settings": "", - "password_settings_description": "", - "paths_validated_successfully": "", - "quota_size_gib": "", - "refreshing_all_libraries": "", - "repair_all": "", - "repair_matched_items": "", - "repaired_items": "", - "require_password_change_on_login": "", - "reset_settings_to_default": "", - "reset_settings_to_recent_saved": "", - "send_welcome_email": "", - "server_external_domain_settings": "", - "server_external_domain_settings_description": "", - "server_settings": "", - "server_settings_description": "", - "server_welcome_message": "", - "server_welcome_message_description": "", - "sidecar_job": "", - "sidecar_job_description": "", - "slideshow_duration_description": "", - "smart_search_job_description": "", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", - "storage_template_migration": "", - "storage_template_migration_job": "", - "storage_template_settings": "", - "storage_template_settings_description": "", - "system_settings": "", - "theme_custom_css_settings": "", - "theme_custom_css_settings_description": "", - "theme_settings": "", - "theme_settings_description": "", - "these_files_matched_by_checksum": "", - "thumbnail_generation_job": "", - "thumbnail_generation_job_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", - "transcoding_acceleration_qsv": "", - "transcoding_acceleration_rkmpp": "", - "transcoding_acceleration_vaapi": "", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", - "transcoding_audio_codec": "", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", - "transcoding_constant_quality_mode": "", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", - "transcoding_hardware_acceleration_description": "", - "transcoding_hardware_decoding": "", - "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_transcode_policy_description": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", - "transcoding_video_codec": "", - "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", - "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", - "untracked_files": "", - "untracked_files_description": "", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", - "user_management": "", - "user_password_has_been_reset": "", - "user_password_reset_description": "", - "user_settings": "", - "user_settings_description": "", - "user_successfully_removed": "", - "version_check_enabled_description": "", - "version_check_settings": "", - "version_check_settings_description": "", - "video_conversion_job": "", - "video_conversion_job_description": "" - }, - "admin_email": "", - "admin_password": "", - "administration": "", - "advanced": "", - "album_added": "", - "album_added_notification_setting_description": "", - "album_cover_updated": "", - "album_info_updated": "", - "album_name": "", - "album_options": "", - "album_updated": "", - "album_updated_setting_description": "", - "albums": "", - "albums_count": "", - "all": "", - "all_people": "", - "allow_dark_mode": "", - "allow_edits": "", - "api_key": "", - "api_keys": "", - "app_settings": "", - "appears_in": "", - "archive": "", - "archive_or_unarchive_photo": "", - "asset_offline": "", - "assets": "", - "authorized_devices": "", - "back": "", - "backward": "", - "blurred_background": "", - "camera": "", - "camera_brand": "", - "camera_model": "", - "cancel": "", - "cancel_search": "", - "cannot_merge_people": "", - "cannot_update_the_description": "", - "change_date": "", - "change_expiration_time": "", - "change_location": "", - "change_name": "", - "change_name_successfully": "", - "change_password": "", - "change_your_password": "", - "changed_visibility_successfully": "", - "check_all": "", - "check_logs": "", - "choose_matching_people_to_merge": "", - "city": "", - "clear": "", - "clear_all": "", - "clear_message": "", - "clear_value": "", - "close": "", - "collapse_all": "", - "color_theme": "", - "comment_options": "", - "comments_are_disabled": "", - "confirm": "", - "confirm_admin_password": "", - "confirm_delete_shared_link": "", - "confirm_password": "", - "contain": "", - "context": "", - "continue": "", - "copied_image_to_clipboard": "", - "copied_to_clipboard": "", - "copy_error": "", - "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", - "country": "", - "cover": "", - "covers": "", - "create": "", - "create_album": "", - "create_library": "", - "create_link": "", - "create_link_to_share": "", - "create_new_person": "", - "create_new_user": "", - "create_user": "", - "created": "", - "current_device": "", - "custom_locale": "", - "custom_locale_description": "", - "dark": "", - "date_after": "", - "date_and_time": "", - "date_before": "", - "date_range": "", - "day": "", - "default_locale": "", - "default_locale_description": "", - "delete": "", - "delete_album": "", - "delete_api_key_prompt": "", - "delete_key": "", - "delete_library": "", - "delete_link": "", - "delete_shared_link": "", - "delete_user": "", - "deleted_shared_link": "", - "description": "", - "details": "", - "direction": "", - "disabled": "", - "disallow_edits": "", - "discover": "", - "dismiss_all_errors": "", - "dismiss_error": "", - "display_options": "", - "display_order": "", - "display_original_photos": "", - "display_original_photos_setting_description": "", - "done": "", - "download": "", - "downloading": "", - "duplicates": "", - "duration": "", - "edit_album": "", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", - "edit_key": "", - "edit_link": "", - "edit_location": "", - "edit_name": "", - "edit_people": "", - "edit_title": "", - "edit_user": "", - "edited": "", - "editor": "", - "email": "", - "empty_trash": "", - "enable": "", - "enabled": "", - "end_date": "", - "error": "", - "error_loading_image": "", - "errors": { - "cleared_jobs": "", - "exclusion_pattern_already_exists": "", - "failed_job_command": "", - "import_path_already_exists": "", - "paths_validation_failed": "", - "quota_higher_than_disk_size": "", - "repair_unable_to_check_items": "", - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_exclusion_pattern": "", - "unable_to_add_import_path": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_change_password": "", - "unable_to_copy_to_clipboard": "", - "unable_to_create_api_key": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", - "unable_to_delete_exclusion_pattern": "", - "unable_to_delete_import_path": "", - "unable_to_delete_shared_link": "", - "unable_to_delete_user": "", - "unable_to_edit_exclusion_pattern": "", - "unable_to_edit_import_path": "", - "unable_to_empty_trash": "", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", - "unable_to_hide_person": "", - "unable_to_link_oauth_account": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_api_key": "", - "unable_to_remove_deleted_assets": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_api_key": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_timeline_display_status": "", - "unable_to_update_user": "" - }, - "exit_slideshow": "", - "expand_all": "", - "expire_after": "", - "expired": "", - "explore": "", - "export": "", - "export_as_json": "", - "extension": "", - "external": "", - "external_libraries": "", - "favorite": "", - "favorite_or_unfavorite_photo": "", - "favorites": "", - "feature_photo_updated": "", - "file_name": "", - "file_name_or_extension": "", - "filename": "", - "filetype": "", - "filter_people": "", - "find_them_fast": "", - "fix_incorrect_match": "", - "forward": "", - "general": "", - "get_help": "", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "group_albums_by": "", - "has_quota": "", - "hide_gallery": "", - "hide_password": "", - "hide_person": "", - "host": "", - "hour": "", - "image": "", - "immich_logo": "", - "immich_web_interface": "", - "import_from_json": "", - "import_path": "", - "in_archive": "", - "include_archived": "", - "include_shared_albums": "", - "include_shared_partner_assets": "", - "individual_share": "", - "info": "", - "interval": { - "day_at_onepm": "", - "hours": "", - "night_at_midnight": "", - "night_at_twoam": "" - }, - "invite_people": "", - "invite_to_album": "", - "jobs": "", - "keep": "", - "keyboard_shortcuts": "", - "language": "", - "language_setting_description": "", - "last_seen": "", - "leave": "", - "let_others_respond": "", - "level": "", - "library": "", - "library_options": "", - "light": "", - "link_options": "", - "link_to_oauth": "", - "linked_oauth_account": "", - "list": "", - "loading": "", - "loading_search_results_failed": "", - "log_out": "", - "log_out_all_devices": "", - "login_has_been_disabled": "", - "look": "", - "loop_videos": "", - "loop_videos_description": "", - "make": "", - "manage_shared_links": "", - "manage_sharing_with_partners": "", - "manage_the_app_settings": "", - "manage_your_account": "", - "manage_your_api_keys": "", - "manage_your_devices": "", - "manage_your_oauth_connection": "", - "map": "", - "map_marker_with_image": "", - "map_settings": "", - "matches": "", - "media_type": "", - "memories": "", - "memories_setting_description": "", - "menu": "", - "merge": "", - "merge_people": "", - "merge_people_successfully": "", - "minimize": "", - "minute": "", - "missing": "", - "model": "", - "month": "", - "more": "", - "moved_to_trash": "", - "my_albums": "", - "name": "", - "name_or_nickname": "", - "never": "", - "new_api_key": "", - "new_password": "", - "new_person": "", - "new_user_created": "", - "newest_first": "", - "next": "", - "next_memory": "", - "no": "", - "no_albums_message": "", - "no_archived_assets_message": "", - "no_assets_message": "", - "no_duplicates_found": "", - "no_exif_info_available": "", - "no_explore_results_message": "", - "no_favorites_message": "", - "no_libraries_message": "", - "no_name": "", - "no_places": "", - "no_results": "", - "no_shared_albums_message": "", - "not_in_any_album": "", - "note_apply_storage_label_to_previously_uploaded assets": "", - "notes": "", - "notification_toggle_setting_description": "", - "notifications": "", - "notifications_setting_description": "", - "oauth": "", - "offline": "", - "offline_paths": "", - "offline_paths_description": "", - "ok": "", - "oldest_first": "", - "online": "", - "only_favorites": "", - "open_the_search_filters": "", - "options": "", - "organize_your_library": "", - "other": "", - "other_devices": "", - "other_variables": "", - "owned": "", - "owner": "", - "partner_can_access": "", - "partner_can_access_assets": "", - "partner_can_access_location": "", - "partner_sharing": "", - "partners": "", - "password": "", - "password_does_not_match": "", - "password_required": "", - "password_reset_success": "", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, - "path": "", - "pattern": "", - "pause": "", - "pause_memories": "", - "paused": "", - "pending": "", - "people": "", - "people_sidebar_description": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", - "permanently_delete": "", - "permanently_deleted_asset": "", - "photos": "", - "photos_count": "", - "photos_from_previous_years": "", - "pick_a_location": "", - "place": "", - "places": "", - "play": "", - "play_memories": "", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", - "profile_picture_set": "", - "public_share": "", - "reaction_options": "", - "read_changelog": "", - "recent": "", - "recent_searches": "", - "refresh": "", - "refreshed": "", - "refreshes_every_file": "", - "remove": "", - "remove_deleted_assets": "", - "remove_from_album": "", - "remove_from_favorites": "", - "remove_from_shared_link": "", - "removed_api_key": "", - "rename": "", - "repair": "", - "repair_no_results_message": "", - "replace_with_upload": "", - "require_password": "", - "require_user_to_change_password_on_first_login": "", - "reset": "", - "reset_password": "", - "reset_people_visibility": "", - "restore": "", - "restore_all": "", - "restore_user": "", - "resume": "", - "retry_upload": "", - "review_duplicates": "", - "role": "", - "save": "", - "saved_api_key": "", - "saved_profile": "", - "saved_settings": "", - "say_something": "", - "scan_all_libraries": "", - "scan_settings": "", - "search": "", - "search_albums": "", - "search_by_context": "", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", - "search_for_existing_person": "", - "search_people": "", - "search_places": "", - "search_state": "", - "search_timezone": "", - "search_type": "", - "search_your_photos": "", - "searching_locales": "", - "second": "", - "select_album_cover": "", - "select_all": "", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_keep_all": "", - "select_library_owner": "", - "select_new_face": "", - "select_photos": "", - "select_trash_all": "", - "selected": "", - "send_message": "", - "send_welcome_email": "", - "server_stats": "", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", - "settings": "", - "settings_saved": "", - "share": "", - "shared": "", - "shared_by": "", - "shared_by_you": "", - "shared_from_partner": "", - "shared_links": "", - "shared_photos_and_videos_count": "", - "shared_with_partner": "", - "sharing": "", - "sharing_sidebar_description": "", - "show_album_options": "", - "show_and_hide_people": "", - "show_file_location": "", - "show_gallery": "", - "show_hidden_people": "", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", - "show_metadata": "", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", - "shuffle": "", - "sign_out": "", - "sign_up": "", - "size": "", - "skip_to_content": "", - "slideshow": "", - "slideshow_settings": "", - "sort_albums_by": "", - "stack": "", - "stack_selected_photos": "", - "stacktrace": "", - "start": "", - "start_date": "", - "state": "", - "status": "", - "stop_motion_photo": "", - "stop_photo_sharing": "", - "stop_photo_sharing_description": "", - "stop_sharing_photos_with_user": "", - "storage": "", - "storage_label": "", - "storage_usage": "", - "submit": "", - "suggestions": "", - "sunrise_on_the_beach": "", - "swap_merge_direction": "", - "sync": "", - "template": "", - "theme": "", - "theme_selection": "", - "theme_selection_description": "", - "time_based_memories": "", - "timezone": "", - "to_archive": "", - "to_favorite": "", - "toggle_settings": "", - "toggle_theme": "", - "total_usage": "", - "trash": "", - "trash_all": "", - "trash_no_results_message": "", - "trashed_items_will_be_permanently_deleted_after": "", - "type": "", - "unarchive": "", - "unfavorite": "", - "unhide_person": "", - "unknown": "", - "unknown_year": "", - "unlimited": "", - "unlink_oauth": "", - "unlinked_oauth_account": "", - "unselect_all": "", - "unstack": "", - "untracked_files": "", - "untracked_files_decription": "", - "up_next": "", - "updated_password": "", - "upload": "", - "upload_concurrency": "", - "url": "", - "usage": "", - "user": "", - "user_id": "", - "user_usage_detail": "", - "username": "", - "users": "", - "utilities": "", - "validate": "", - "variables": "", - "version": "", - "video": "", - "video_hover_setting": "", - "video_hover_setting_description": "", - "videos": "", - "videos_count": "", - "view_all": "", - "view_all_users": "", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", - "waiting": "", - "week": "", - "welcome": "", - "welcome_to_immich": "", - "year": "", - "yes": "", - "you_dont_have_any_shared_links": "", - "zoom_image": "" + "about": "Õ„ÕĄÕŊÕĢÕļ", + "action": "ÔŗÕ¸Ö€ÕŽÕ¸Õ˛Õ¸Ö‚ÕŠÕĩուÕļ", + "add": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ", + "add_a_location": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŋÕĨÕ˛", + "add_a_name": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕĄÕļուÕļ", + "add_location": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŋÕĨÕ˛", + "add_photos": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "back": "ՀÕĨÕŋ", + "backup_all": "Ô˛Õ¸ÕŦոր", + "backup_controller_page_background_battery_info_link": "ՑուÕĩց Õŋուր ÕĢÕļÕšÕēÕĨÕŊ", + "backup_controller_page_background_battery_info_ok": "ÔŧÕĄÕž", + "change_location": "ՓոխÕĨÕŦ ÕŋÕĨÕ˛Õ¨", + "change_name": "ՓոխÕĨÕŦ ÕĄÕļուÕļ", + "city": "Õ”ÕĄÕ˛ÕĄÖ„", + "client_cert_dialog_msg_confirm": "ÔŧÕĄÕž", + "color": "ÔŗÕ¸Ö‚ÕĩÕļ", + "control_bottom_app_bar_edit_location": "ՓոխÕĨÕŦ ՏÕĨÕ˛Õ¨", + "country": "ÔĩրկÕĢր", + "create_new": "ՍՏÔĩÕ‚ÔžÔĩÔŧ ՆՈՐ", + "create_new_person": "ՍÕŋÕĨÕ˛ÕŽÕĨÕŦ Õļոր ÕĄÕļÕą", + "create_shared_album_page_share_select_photos": "Ô¸ÕļÕŋրÕĨ Õ†Õ¯ÕĄÖ€ÕļÕĨր", + "curated_object_page_title": "Ô˛ÕĄÕļÕĨր", + "dark": "Õ„Õ¸Ö‚ÕŠ", + "day": "Օր", + "delete": "ՋÕļÕģÕĨÕŦ", + "edit_location": "ՓոխÕĨÕŦ ÕŋÕĨÕ˛Õ¨", + "exif_bottom_sheet_person_add_person": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕĄÕļուÕļ", + "exif_bottom_sheet_person_age": "ÕÕĄÖ€ÕĢք {age}", + "exif_bottom_sheet_person_age_years": "ÕÕĄÖ€ÕĢք {years}", + "hi_user": "Ô˛ÕĄÖ€ÕĨւ {name} ({email})", + "map_assets_in_bound": "{count} ÕļÕ¯ÕĄÖ€", + "map_assets_in_bounds": "{count} ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "partner_list_user_photos": "{}-ÕĢÕļ ÕļÕ¯ÕĄÖ€ÕļÕĨրը", + "photos": "Õ†Õ¯ÕĄÖ€ÕļÕĨր", + "save": "ÕŠÕĄÕ°ÕĨ", + "scan_library": "Õ†ÕĄÕĩÕĨ", + "search": "ՓÕļÕŋրÕĨ", + "search_city": "ՈրոÕļÕĨ Ö„ÕĄÕ˛ÕĄÖ„â€Ļ", + "search_filter_date": "ÔąÕ´ÕŊÕĄÕŠÕĢÕž", + "search_filter_date_interval": "{start} Õ´ÕĢÕļÕšÕĨւ {end}", + "search_filter_location": "ՏÕĨÕ˛", + "search_filter_location_title": "Ô¸ÕļÕŋրÕĨ ÕŋÕĨÕ˛", + "search_no_people": "ÕˆÕš Õ´ÕĢ ÕĄÕļÕą", + "search_page_motion_photos": "Õ‡ÕĄÖ€ÕĒÕžÕ¸Õ˛ Õ†Õ¯ÕĄÖ€ÕļÕĨր", + "select_photos": "Ô¸ÕļÕŋրÕĨ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "setting_notifications_notify_never": "ÕĨրÕĸÕĨք", + "setting_notifications_notify_seconds": "{count} ÕžÕĄÕĩրկÕĩÕĄÕļ", + "share_add_photos": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "shared_link_edit_expire_after_option_day": "1 օր", + "shared_link_edit_expire_after_option_days": "{count} օր", + "shared_link_edit_expire_after_option_hour": "1 ÕĒÕĄÕ´", + "shared_link_edit_expire_after_option_hours": "{count} ÕĒÕĄÕ´", + "shared_link_edit_expire_after_option_minute": "1 րոÕēÕĨ", + "shared_link_edit_expire_after_option_minutes": "{count} րոÕēÕĨ", + "shared_link_edit_expire_after_option_months": "{count} ÕĄÕ´ÕĢÕŊ", + "shared_link_edit_expire_after_option_year": "{count} ÕŋÕĄÖ€ÕĢ", + "sort_oldest": "ÔąÕ´ÕĨÕļÕĄÕ°ÕĢÕļ ÕļÕ¯ÕĄÖ€Õ¨", + "sort_recent": "ÔąÕ´ÕĨÕļÕĄÕļոր ÕļÕ¯ÕĄÖ€Õ¨", + "timezone": "ÔēÕĄÕ´ÕĄÕĩÕĢÕļ ÕŖÕ¸ÕŋÕĢ", + "to_trash": "ÔąÕ˛Õĸ", + "trash": "ÔąÕ˛Õĸ", + "trash_page_title": "ÔąÕ˛Õĸ ({count})", + "type": "ՏÕĨÕŊÕĄÕ¯", + "unknown": "ÔąÕļÕ°ÕĄÕĩÕŋ", + "unknown_country": "ÔąÕļÕ°ÕĄÕĩÕŋ ÔĩրկÕĢր", + "unknown_year": "ÔąÕļÕ°ÕĄÕĩÕŋ ÕÕĄÖ€ÕĢ", + "upload_status_errors": "ÕÕ­ÕĄÕŦÕļÕĨր", + "version_announcement_closing": "Քո Õ¨ÕļÕ¯ÕĨրը՝ ÔąÕŦÕĨքÕŊÕ¨", + "week": "Õ‡ÕĄÕĸÕĄÕŠ", + "welcome": "Ô˛ÕĄÖ€ÕĢ ÕŖÕĄÕŦուÕŊÕŋ", + "year": "ÕÕĄÖ€ÕĢ", + "yes": "ÔąÕĩÕ¸" } diff --git a/i18n/id.json b/i18n/id.json index e07e79faff..36dd6a953e 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -14,7 +14,7 @@ "add_a_location": "Tambahkan lokasi", "add_a_name": "Tambahkan nama", "add_a_title": "Tambahkan judul", - "add_endpoint": "Add endpoint", + "add_endpoint": "Tambahkan titik akhir", "add_exclusion_pattern": "Tambahkan pola pengecualian", "add_import_path": "Tambahkan jalur impor", "add_location": "Tambahkan lokasi", @@ -24,8 +24,9 @@ "add_photos": "Tambahkan foto", "add_to": "Tambahkan keâ€Ļ", "add_to_album": "Tambahkan ke album", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_added": "Ditambahkan ke {album}", + "add_to_album_bottom_sheet_already_exists": "Sudah ada di {album}", + "add_to_locked_folder": "Tambahkan ke Folder Terkunci", "add_to_shared_album": "Tambahkan ke album terbagi", "add_url": "Tambahkan URL", "added_to_archive": "Ditambahkan ke arsip", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Anda yakin untuk menonaktifkan semua cara login? Login akan dinonaktikan secara menyeluruh.", "authentication_settings_reenable": "Untuk mengaktifkan ulang, gunakan Perintah Server.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Basis Data Cadangan", + "backup_database": "Buat Cadangan Basis Data", "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", - "backup_settings": "Pengaturan Pencadangan", - "backup_settings_description": "Kelola pengaturan pencadangan basis data", + "backup_settings": "Pengaturan Pencadangan Basis Data", + "backup_settings_description": "Kelola pengaturan pencadangan basis data. Catatan: Tugas ini tidak dipantau dan Anda tidak akan diberi tahu jika ada kesalahan.", "check_all": "Periksa Semua", "cleanup": "Pembersihan", "cleared_jobs": "Tugas terselesaikan untuk: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Untuk mengonfirmasi, ketik \"{email}\" di bawah", "confirm_reprocess_all_faces": "Apakah Anda yakin ingin memproses semua wajah? Ini juga akan menghapus nama orang.", "confirm_user_password_reset": "Apakah Anda yakin ingin mengatur ulang kata sandi {user}?", + "confirm_user_pin_code_reset": "Apakah Anda yakin ingin mereset kode PIN milik {user}?", "create_job": "Buat tugas", "cron_expression": "Ekspresi cron", "cron_expression_description": "Tetapkan interval pemindaian menggunakan format cron. Untuk informasi lebih lanjut, silakan merujuk misalnya ke Crontab Guru", @@ -70,8 +72,13 @@ "forcing_refresh_library_files": "Memaksakan penyegaran semua berkas pustaka", "image_format": "Format", "image_format_description": "WebP menghasilkan ukuran berkas yang lebih kecil daripada JPEG, tetapi lebih lambat untuk dienkode.", + "image_fullsize_description": "Gambar berukuran penuh tanpa metadata, digunakan ketika diperbesar", + "image_fullsize_enabled": "Aktifkan pembuatan gambar berukuran penuh", + "image_fullsize_enabled_description": "Buat gambar berukuran penuh untuk format yang tidak ramah web. Ketika \"Utamakan pratinjau tersemat\" diaktifkan, pratinjau tersema digunakan secara langsung tanpa konversi. Tidak memengaruhi format ramah web seperti JPEG.", + "image_fullsize_quality_description": "Kualitas gambar berukuran penuh dari 1-100. Lebih tinggi lebih baik, tetapi menghasilkan berkas lebih besar.", + "image_fullsize_title": "Pengaturan Gambar Berukuran Penuh", "image_prefer_embedded_preview": "Utamakan pratinjau tersemat", - "image_prefer_embedded_preview_setting_description": "Gunakan pratinjau tersemat dalam foto RAW sebagai masukan dalam pemrosesan gambar ketika tersedia. Ini dapat menghasilkan warna yang lebih akurat untuk beberapa gambar, tetapi kualitas pratinjau bergantung pada kamera dan gambarnya dapat memiliki lebih banyak artefak kompresi.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratinjau tersemat dalam foto RAW sebagai masukan dalam pemrosesan gambar dan ketika tersedia. Ini dapat menghasilkan warna yang lebih akurat untuk beberapa gambar, tetapi kualitas pratinjau bergantung pada kamera dan gambarnya dapat memiliki lebih banyak artefak kompresi.", "image_prefer_wide_gamut": "Utamakan gamut luas", "image_prefer_wide_gamut_setting_description": "Gunakan Display P3 untuk gambar kecil. Ini menjaga kecerahan gambar dengan ruang warna yang luas, tetapi gambar dapat terlihat beda pada perangkat lawas dengan versi peramban yang lawas. Gambar sRGB tetap dalam sRGB untuk menghindari perubahan warna.", "image_preview_description": "Gambar berukuran sedang tanpa metadata, digunakan ketika melihat aset satuan dan untuk pembelajaran mesin", @@ -187,26 +194,22 @@ "oauth_auto_register": "Pendaftaran otomatis", "oauth_auto_register_description": "Daftar pengguna baru secara otomatis setelah log masuk dengan OAuth", "oauth_button_text": "Teks tombol", - "oauth_client_id": "ID Klien", - "oauth_client_secret": "Rahasia Klien", + "oauth_client_secret_description": "Diperlukan jika PKCE (Proof Key for Code Exchange) tidak didukung oleh penyedia OAuth", "oauth_enable_description": "Log masuk dengan OAuth", - "oauth_issuer_url": "URL Penerbit", "oauth_mobile_redirect_uri": "URI pengalihan ponsel", "oauth_mobile_redirect_uri_override": "Penimpaan URI penerusan ponsel", "oauth_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti '{callback}'", - "oauth_profile_signing_algorithm": "Algoritma penandatanganan profil", - "oauth_profile_signing_algorithm_description": "Algoritma yang digunakan untuk menandatangani profil pengguna.", - "oauth_scope": "Cakupan", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", - "oauth_signing_algorithm": "Algoritma penandatanganan", "oauth_storage_label_claim": "Klaim label penyimpanan", "oauth_storage_label_claim_description": "Atur label penyimpanan pengguna menjadi nilai klaim ini secara otomatis.", "oauth_storage_quota_claim": "Klaim kuota penyimpanan", "oauth_storage_quota_claim_description": "Atur kuota penyimpanan pengguna menjadi nilai klaim ini secara otomatis.", "oauth_storage_quota_default": "Kuota penyimpanan bawaan (GiB)", "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan ketika tidak ada klaim yang disediakan (Masukkan 0 untuk kuota tidak terbatas).", + "oauth_timeout": "Waktu Permintaan Habis", + "oauth_timeout_description": "Waktu habis untuk permintaan dalam milidetik", "offline_paths": "Jalur Luring", "offline_paths_description": "Hasil ini dapat terjadi karena penghapusan berkas manual yang tidak menjadi bagian dari pustaka eksternal.", "password_enable_description": "Masuk dengan surel dan kata sandi", @@ -347,6 +350,7 @@ "user_delete_delay_settings_description": "Jumlah hari setelah penghapusan untuk menghapus akun dan aset pengguna secara permanen. Tugas penghapusan pengguna berjalan pada tengah malam untuk memeriksa pengguna yang siap untuk dihapus. Perubahan pengaturan ini akan dievaluasi pada eksekusi berikutnya.", "user_delete_immediately": "Akun dan aset {user} akan diantrekan untuk penghapusan permanen segera.", "user_delete_immediately_checkbox": "Masukkan pengguna dan aset dalam antrean untuk penghapusan segera", + "user_details": "Detail Pengguna", "user_management": "Pengelolaan Pengguna", "user_password_has_been_reset": "Kata sandi pengguna telah diatur ulang:", "user_password_reset_description": "Silakan sediakan kata sandi sementara untuk pengguna dan beri tahu bahwa pengguna tersebut harus mengubah kata sandinya pada log masuk berikutnya.", @@ -366,16 +370,13 @@ "admin_password": "Kata Sandi Admin", "administration": "Administrasi", "advanced": "Tingkat lanjut", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat thumbnail dengan cepat.\nMenyalakan ini akan memuat thumbnail dari server.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan opsi ini untuk menyaring media saat sinkronisasi berdasarkan kriteria alternatif. Hanya coba ini dengan aplikasi mendeteksi semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan saringan sinkronisasi album perangkat alternatif", + "advanced_settings_log_level_title": "Tingkat log: {level}", + "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat gambar kecil dengan cepat. Menyalakan ini akan memuat gambar kecil dari server.", + "advanced_settings_prefer_remote_title": "Prioritaskan gambar dari server", + "advanced_settings_sync_remote_deletions_subtitle": "Hapus atau pulihkan aset pada perangkat ini secara otomatis ketika tindakan dilakukan di web", + "advanced_settings_sync_remote_deletions_title": "Sinkronisasi penghapusan jarak jauh [EKSPERIMENTAL]", "age_months": "Umur {months, plural, one {# bulan} other {# bulan}}", "age_year_months": "Umur 1 tahun, {months, plural, one {# bulan} other {# bulan}}", "age_years": "{years, plural, other {Umur #}}", @@ -394,10 +395,9 @@ "album_remove_user": "Keluarkan pengguna?", "album_remove_user_confirmation": "Apakah Anda yakin ingin mengeluarkan {user}?", "album_share_no_users": "Sepertinya Anda telah membagikan album ini dengan semua pengguna atau tidak memiliki pengguna siapa pun untuk dibagikan.", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} item", + "album_thumbnail_card_items": "{count} item", "album_thumbnail_card_shared": " ¡ Dibagikan", - "album_thumbnail_shared_by": "Dibagikan oleh {}", + "album_thumbnail_shared_by": "Dibagikan oleh {user}", "album_updated": "Album diperbarui", "album_updated_setting_description": "Terima notifikasi surel ketika album terbagi memiliki aset baru", "album_user_left": "Keluar dari {album}", @@ -435,22 +435,18 @@ "archive": "Arsip", "archive_or_unarchive_photo": "Arsipkan atau batalkan pengarsipan foto", "archive_page_no_archived_assets": "Tidak ada aset diarsipkan", - "archive_page_title": "Arsip ({})", + "archive_page_title": "Arsip ({count})", "archive_size": "Ukuran arsip", "archive_size_description": "Atur ukuran arsip untuk unduhan (dalam GiB)", - "archived": "Archived", "archived_count": "{count, plural, other {# terarsip}}", "are_these_the_same_person": "Apakah ini adalah orang yang sama?", "are_you_sure_to_do_this": "Apakah Anda yakin ingin melakukan ini?", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", "asset_added_to_album": "Telah ditambahkan ke album", "asset_adding_to_album": "Menambahkan ke albumâ€Ļ", "asset_description_updated": "Deskripsi aset telah diperbarui", "asset_filename_is_offline": "Aset {filename} sedang luring", "asset_has_unassigned_faces": "Aset memiliki wajah yang belum ditetapkan", "asset_hashing": "Memilahâ€Ļ", - "asset_list_group_by_sub_title": "Group by", "asset_list_layout_settings_dynamic_layout_title": "Tata dinamis", "asset_list_layout_settings_group_automatically": "Otomatis", "asset_list_layout_settings_group_by": "Kelompokkan aset berdasarkan", @@ -460,78 +456,66 @@ "asset_list_settings_title": "Grid Foto", "asset_offline": "Aset Luring", "asset_offline_description": "Aset eksternal ini tidak ada lagi di diska. Silakan hubungi administrator Immich Anda untuk bantuan.", - "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "Dilewati", "asset_skipped_in_trash": "Dalam sampah", "asset_uploaded": "Sudah diunggah", "asset_uploading": "Mengunggahâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", "asset_viewer_settings_title": "Penampil Aset", "assets": "Aset", "assets_added_count": "{count, plural, one {# aset} other {# aset}} ditambahkan", "assets_added_to_album_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke album", "assets_added_to_name_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke {hasName, select, true {{name}} other {album baru}}", "assets_count": "{count, plural, one {# aset} other {# aset}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} aset dihapus secara permanen", + "assets_deleted_permanently_from_server": "{count} aset dihapus secara permanen dari server Immich", "assets_moved_to_trash_count": "Dipindahkan {count, plural, one {# aset} other {# aset}} ke sampah", "assets_permanently_deleted_count": "{count, plural, one {# aset} other {# aset}} dihapus secara permanen", "assets_removed_count": "{count, plural, one {# aset} other {# aset}} dihapus", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{count} aset dihapus secara permanen dari perangkat Anda", "assets_restore_confirmation": "Apakah Anda yakin ingin memulihkan semua aset yang dibuang? Anda tidak dapat mengurungkan tindakan ini! Perlu diingat bahwa aset luring tidak dapat dipulihkan.", "assets_restored_count": "{count, plural, one {# aset} other {# aset}} dipulihkan", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{count} aset berhasil dipulihkan", + "assets_trashed": "{count} aset dipindahkan ke sampah", "assets_trashed_count": "{count, plural, one {# aset} other {# aset}} dibuang ke sampah", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{count} aset dipindahkan ke sampah dari server Immich", "assets_were_part_of_album_count": "{count, plural, one {Aset telah} other {Aset telah}} menjadi bagian dari album", "authorized_devices": "Perangkat Terautentikasi", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "Kembali", "back_close_deselect": "Kembali, tutup, atau batalkan pemilihan", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Album perangkat ({})", + "backup_album_selection_page_albums_device": "Album di perangkat ({count})", "backup_album_selection_page_albums_tap": "Sentuh untuk memilih, sentuh 2x untuk mengecualikan", "backup_album_selection_page_assets_scatter": "Aset dapat tersebar dalam banyak album. Sehingga album dapat dipilih atau dikecualikan saat proses pencadangan.", "backup_album_selection_page_select_albums": "Pilih album", - "backup_album_selection_page_selection_info": "Album terpilih: ", + "backup_album_selection_page_selection_info": "Info Pilihan", "backup_album_selection_page_total_assets": "Total aset unik", "backup_all": "Semua", - "backup_background_service_backup_failed_message": "Gagal mencadangkan aset. Mencoba lagi...", - "backup_background_service_connection_failed_message": "Koneksi ke server gagal. Mencoba ulang...", - "backup_background_service_current_upload_notification": "Mengunggah {}", - "backup_background_service_default_notification": "Memeriksa aset baru...", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Mencadangkan asetmu...", - "backup_background_service_upload_failure_notification": "Gagal unggah {}", + "backup_background_service_backup_failed_message": "Gagal mencadangkan aset. Mencoba lagiâ€Ļ", + "backup_background_service_connection_failed_message": "Koneksi ke server gagal. Mencoba ulangâ€Ļ", + "backup_background_service_current_upload_notification": "Mengunggah {filename}", + "backup_background_service_default_notification": "Memeriksa aset baruâ€Ļ", + "backup_background_service_in_progress_notification": "Mencadangkan asetmuâ€Ļ", + "backup_background_service_upload_failure_notification": "Gagal mengunggah {filename}", "backup_controller_page_albums": "Cadangkan album", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", "backup_controller_page_background_app_refresh_enable_button_text": "Ke setelan", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimisasi baterai", "backup_controller_page_background_charging": "Hanya saat mengisi daya", "backup_controller_page_background_configure_error": "Gagal mengatur layanan latar belakang", - "backup_controller_page_background_delay": "Tunda cadangkan aset baru: {}", + "backup_controller_page_background_delay": "Tunda pencadangan aset baru: {duration}", "backup_controller_page_background_description": "Aktifkan layanan latar belakang untuk mencadangkan aset baru secara otomatis tanpa perlu membuka app", "backup_controller_page_background_is_off": "Pencadangan otomatis di latar belakang nonaktif", "backup_controller_page_background_is_on": "Pencadangan otomatis di latar belakang aktif", "backup_controller_page_background_turn_off": "Matikan layanan latar belakang", "backup_controller_page_background_turn_on": "Nyalakan layanan latar belakang", - "backup_controller_page_background_wifi": "Hanya melalui WiFi", + "backup_controller_page_background_wifi": "Hanya melalui Wi-Fi", "backup_controller_page_backup": "Cadangkan", - "backup_controller_page_backup_selected": "Terpilih:", + "backup_controller_page_backup_selected": "Terpilih: ", "backup_controller_page_backup_sub": "Foto dan video yang dicadangkan", - "backup_controller_page_created": "Dibuat pada: {}", + "backup_controller_page_created": "Dibuat pada: {date}", "backup_controller_page_desc_backup": "Aktifkan pencadangan di latar depan untuk mengunggah otomatis aset baru ke server secara otomatis saat aplikasi terbuka.", - "backup_controller_page_excluded": "Dikecualikan:", - "backup_controller_page_failed": "Gagal ({})", - "backup_controller_page_filename": "Nama file: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Dikecualikan: ", + "backup_controller_page_failed": "Gagal ({count})", + "backup_controller_page_filename": "Nama file: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informasi Cadangan", "backup_controller_page_none_selected": "Tidak ada dipilih", "backup_controller_page_remainder": "Sisa", @@ -540,7 +524,7 @@ "backup_controller_page_start_backup": "Mulai Cadangkan", "backup_controller_page_status_off": "Pencadangan otomatis di latar depan nonaktif", "backup_controller_page_status_on": "Pencadangan otomatis di latar depan aktif", - "backup_controller_page_storage_format": "{} dari {} terpakai", + "backup_controller_page_storage_format": "{used} dari {total} digunakan", "backup_controller_page_to_backup": "Album untuk dicadangkan", "backup_controller_page_total_sub": "Semua foto dan video unik dari album terpilih", "backup_controller_page_turn_off": "Nonaktifkan pencadangan latar depan", @@ -553,8 +537,11 @@ "backup_manual_success": "Sukses", "backup_manual_title": "Status unggah", "backup_options_page_title": "Setelan cadangan", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Maju", + "biometric_auth_enabled": "Autentikasi biometrik diaktifkan", + "biometric_locked_out": "Anda terkunci oleh autentikasi biometrik", + "biometric_no_options": "Opsi biometrik tidak tersedia", + "biometric_not_available": "Autentikasi biometrik tidak tersedia di perangkat ini", "birthdate_saved": "Tanggal lahir berhasil disimpan", "birthdate_set_description": "Tanggal lahir digunakan untuk menghitung umur orang ini pada saat foto diambil.", "blurred_background": "Latar belakang buram", @@ -565,22 +552,22 @@ "bulk_keep_duplicates_confirmation": "Apakah Anda yakin ingin menyimpan {count, plural, one {# aset duplikat} other {# aset duplikat}}? Ini akan menyelesaikan semua kelompok duplikat tanpa menghapus apa pun.", "bulk_trash_duplicates_confirmation": "Apakah Anda yakin ingin membuang {count, plural, one {# aset duplikat} other {# aset duplikat}} secara bersamaan? Ini akan menyimpan aset terbesar dari setiap kelompok dan membuang semua duplikat lainnya.", "buy": "Beli Immich", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", + "cache_settings_album_thumbnails": "Thumbnail halaman pustaka ({count} aset)", "cache_settings_clear_cache_button": "Hapus cache", "cache_settings_clear_cache_button_title": "Membersihkan cache aplikasi. Performa aplikasi akan terpengaruh hingga cache dibuat kembali.", "cache_settings_duplicated_assets_clear_button": "BERSIHKAN", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Aset Ganda ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", + "cache_settings_duplicated_assets_subtitle": "Foto dan video yang masuk dalam daftar hitam oleh aplikasi", + "cache_settings_duplicated_assets_title": "Aset Duplikat ({count})", + "cache_settings_image_cache_size": "Ukuran cache gambar ({count} aset)", "cache_settings_statistics_album": "Pustaka thumbnail", - "cache_settings_statistics_assets": "{} aset ({})", - "cache_settings_statistics_full": "Full images", + "cache_settings_statistics_assets": "{count} aset ({size})", + "cache_settings_statistics_full": "Gambar penuh", "cache_settings_statistics_shared": "Thumbnail album berbagi", "cache_settings_statistics_thumbnail": "Thumbnail", "cache_settings_statistics_title": "Penggunaan cache", "cache_settings_subtitle": "Menyetel proses cache aplikasi Immich", - "cache_settings_thumbnail_size": "Ukuran cache thumbnail ({} aset)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", + "cache_settings_thumbnail_size": "Ukuran cache thumbnail ({count} aset)", + "cache_settings_tile_subtitle": "Mengatur perilaku penyimpanan lokal", "cache_settings_tile_title": "Penyimpanan Lokal", "cache_settings_title": "Setelan Cache", "camera": "Kamera", @@ -588,12 +575,14 @@ "camera_model": "Model kamera", "cancel": "Batal", "cancel_search": "Batalkan pencarian", - "canceled": "Canceled", + "canceled": "Dibatalkan", "cannot_merge_people": "Tidak dapat menggabungkan orang", "cannot_undo_this_action": "Anda tidak dapat mengurungkan tindakan ini!", "cannot_update_the_description": "Tidak dapat memperbarui deskripsi", + "cast": "Pencerminan", "change_date": "Ubah tanggal", - "change_display_order": "Change display order", + "change_description": "Ganti deskripsi", + "change_display_order": "Mengubah urutan tampilan", "change_expiration_time": "Ubah waktu kedaluwarsa", "change_location": "Ubah lokasi", "change_name": "Ubah nama", @@ -601,16 +590,17 @@ "change_password": "Ubah Kata Sandi", "change_password_description": "Ini merupakan pertama kali Anda masuk ke sistem atau ada permintaan untuk mengubah kata sandi Anda. Silakan masukkan kata sandi baru di bawah.", "change_password_form_confirm_password": "Konfirmasi Sandi", - "change_password_form_description": "Halo {},\n\nIni pertama kali anda masuk ke dalam sistem atau terdapat permintaan penggantian password.\nHarap masukkan password baru.", + "change_password_form_description": "Halo {name},\n\nIni pertama kali anda masuk ke dalam sistem atau terdapat permintaan penggantian kata sandi. Harap masukkan password baru.", "change_password_form_new_password": "Sandi Baru", "change_password_form_password_mismatch": "Sandi tidak cocok", "change_password_form_reenter_new_password": "Masukkan Ulang Sandi Baru", + "change_pin_code": "Ubah kode PIN", "change_your_password": "Ubah kata sandi Anda", "changed_visibility_successfully": "Keterlihatan berhasil diubah", "check_all": "Periksa Semua", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "Periksa cadangan aset yang rusak", + "check_corrupt_asset_backup_button": "Lakukan pemeriksaan", + "check_corrupt_asset_backup_description": "Jalankan pemeriksaan ini hanya melalui Wi-Fi dan setelah semua aset dicadangkan. Prosedur ini mungkin memerlukan waktu beberapa menit.", "check_logs": "Periksa Log", "choose_matching_people_to_merge": "Pilih orang yang cocok untuk digabungkan", "city": "Kota", @@ -620,13 +610,13 @@ "clear_message": "Hapus pesan", "clear_value": "Hapus nilai", "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_enter_password": "Masukkan kata Sandi", + "client_cert_import": "Impor", + "client_cert_import_success_msg": "Sertifikat klien telah diimpor", + "client_cert_invalid_msg": "File sertifikat tidak valid atau kata sandi salah", + "client_cert_remove_msg": "Sertifikat klien dihapus", + "client_cert_subtitle": "Hanya mendukung format PKCS12 (.p12, .pfx). Impor/Hapus Sertifikat hanya tersedia sebelum login", + "client_cert_title": "Sertifikat SSL klien", "clockwise": "Searah jarum jam", "close": "Tutup", "collapse": "Tutup", @@ -639,23 +629,25 @@ "comments_are_disabled": "Komentar dinonaktifkan", "common_create_new_album": "Buat album baru", "common_server_error": "Koneksi gagal, pastikan server dapat diakses dan memiliki versi yang kompatibel.", - "completed": "Completed", + "completed": "Selesai", "confirm": "Konfirmasi", "confirm_admin_password": "Konfirmasi Kata Sandi Admin", "confirm_delete_face": "Apakah Anda yakin ingin menghapus wajah {name} dari aset?", "confirm_delete_shared_link": "Apakah Anda yakin ingin menghapus tautan terbagi ini?", "confirm_keep_this_delete_others": "Semua aset lain di dalam stack akan dihapus kecuali aset ini. Anda yakin untuk melanjutkan?", + "confirm_new_pin_code": "Konfirmasi kode PIN baru", "confirm_password": "Konfirmasi kata sandi", + "connected_to": "Tersambung ke", "contain": "Berisi", "context": "Konteks", "continue": "Lanjutkan", - "control_bottom_app_bar_album_info_shared": "{} item ¡ Dibagikan", + "control_bottom_app_bar_album_info_shared": "{count} item ¡ Dibagikan", "control_bottom_app_bar_create_new_album": "Buat album baru", "control_bottom_app_bar_delete_from_immich": "Hapus dari Immich", "control_bottom_app_bar_delete_from_local": "Hapus dari perangkat", "control_bottom_app_bar_edit_location": "Edit Lokasi", "control_bottom_app_bar_edit_time": "Edit Tanggal & Waktu", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Bagikan tautan", "control_bottom_app_bar_share_to": "Bagikan Ke", "control_bottom_app_bar_trash_from_immich": "Pindah ke Sampah", "copied_image_to_clipboard": "Gambar disalin ke papan klip.", @@ -668,8 +660,8 @@ "copy_password": "Salin kata sandi", "copy_to_clipboard": "Salin ke Papan Klip", "country": "Negara", - "cover": "Penuhi", - "covers": "Kover", + "cover": "Penutup", + "covers": "Penutup", "create": "Buat", "create_album": "Buat album", "create_album_page_untitled": "Tak berjudul", @@ -677,7 +669,7 @@ "create_link": "Buat tautan", "create_link_to_share": "Buat tautan untuk dibagikan", "create_link_to_share_description": "Biarkan siapa pun dengan tautan melihat foto yang dipilih", - "create_new": "CREATE NEW", + "create_new": "BUAT BARU", "create_new_person": "Buat orang baru", "create_new_person_hint": "Tetapkan aset yang dipilih ke orang yang baru", "create_new_user": "Buat pengguna baru", @@ -687,10 +679,12 @@ "create_tag_description": "Buat tag baru. Untuk tag bersarang, harap input jalur tag secara lengkap termasuk tanda garis miring ke depan.", "create_user": "Buat pengguna", "created": "Dibuat", - "crop": "Crop", + "created_at": "Dibuat", + "crop": "Pangkas", "curated_object_page_title": "Benda", "current_device": "Perangkat saat ini", - "current_server_address": "Current server address", + "current_pin_code": "Kode PIN saat ini", + "current_server_address": "Alamat server saat ini", "custom_locale": "Lokal Khusus", "custom_locale_description": "Format tanggal dan angka berdasarkan bahasa dan wilayah", "daily_title_text_date": "E, dd MMM", @@ -703,29 +697,29 @@ "date_of_birth_saved": "Tanggal lahir berhasil disimpan", "date_range": "Jangka tanggal", "day": "Hari", - "deduplicate_all": "Deduplikat Semua", + "deduplicate_all": "Hapus semua duplikat", "deduplication_criteria_1": "Ukuran gambar dalam bita", "deduplication_criteria_2": "Hitungan data EXIF", "deduplication_info": "Info deduplikasi", "deduplication_info_description": "Untuk memilih aset secara otomatis dan menghapus duplikat secara massal, kami melihat:", "default_locale": "Lokal Bawaan", - "default_locale_description": "Format tanggal dan angka berdasarkan lokal peramban Anda", + "default_locale_description": "Format tanggal dan angka berdasarkan peramban lokal Anda", "delete": "Hapus", "delete_album": "Hapus album", "delete_api_key_prompt": "Apakah Anda yakin ingin menghapus kunci API ini?", "delete_dialog_alert": "Item ini akan dihapus permanen dari Immich dan perangkat", - "delete_dialog_alert_local": "Item ini akan dihapus secara permanen dari perangkatmu, namun masih akan tersedia di server Immich", - "delete_dialog_alert_local_non_backed_up": "Beberapa item belum dicadangkan ke Immich dan akan dihapus secara permanen dari perangkatmu", + "delete_dialog_alert_local": "Item ini akan dihapus secara permanen dari perangkat Anda, tapi masih akan tetap tersedia di server Immich", + "delete_dialog_alert_local_non_backed_up": "Beberapa item belum dicadangkan ke Immich dan akan dihapus secara permanen dari perangkat Anda", "delete_dialog_alert_remote": "Item ini akan dihapus secara permanen dari server Immich", - "delete_dialog_ok_force": "Delete Anyway", + "delete_dialog_ok_force": "Lanjutkan Hapus", "delete_dialog_title": "Hapus Permanen", "delete_duplicates_confirmation": "Apakah Anda yakin ingin menghapus duplikat ini secara permanen?", "delete_face": "Hapus wajah", "delete_key": "Hapus kunci", "delete_library": "Hapus Pustaka", "delete_link": "Hapus tautan", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", + "delete_local_dialog_ok_backed_up_only": "Hapus hanya yang telah dicadangkan", + "delete_local_dialog_ok_force": "Lanjutkan Hapus", "delete_others": "Hapus lainnya", "delete_shared_link": "Hapus tautan terbagi", "delete_shared_link_dialog_title": "Hapus Link Berbagi", @@ -733,18 +727,17 @@ "delete_tag_confirmation_prompt": "Apakah Anda yakin ingin menghapus label tag {tagName}?", "delete_user": "Hapus pengguna", "deleted_shared_link": "Tautan terbagi dihapus", - "deletes_missing_assets": "Menghapus aset yang hilang dari diska", + "deletes_missing_assets": "Menghapus aset yang hilang dari disk", "description": "Deskripsi", "description_input_hint_text": "Tambah deskripsi...", - "description_input_submit_error": "Error updating description, check the log for more details", + "description_input_submit_error": "Terjadi kesalahan saat memperbarui deskripsi, periksa log untuk detail selengkapnya", "details": "Detail", "direction": "Arah", "disabled": "Dinonaktifkan", - "disallow_edits": "Jangan perbolehkan penyuntingan", - "discord": "Discord", + "disallow_edits": "Jangan izinkan penyuntingan", "discover": "Jelajahi", - "dismiss_all_errors": "Abaikan semua eror", - "dismiss_error": "Abaikan eror", + "dismiss_all_errors": "Abaikan semua kesalahan", + "dismiss_error": "Abaikan kesalahan", "display_options": "Opsi penampilan", "display_order": "Urutan penampilan", "display_original_photos": "Tampilkan foto asli", @@ -753,35 +746,36 @@ "documentation": "Dokumentasi", "done": "Selesai", "download": "Unduh", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_include_embedded_motion_videos": "Video tersematkan", - "download_include_embedded_motion_videos_description": "Sertakan video yg tersematkan dalam foto gerak sebagai file terpisah", - "download_notfound": "Download not found", - "download_paused": "Download paused", - "download_settings": "Pengunduhan", + "download_canceled": "Unduhan dibatalkan", + "download_complete": "Unduhan selesai", + "download_enqueue": "Unduhan diantrikan", + "download_error": "Kesalahan unduh", + "download_failed": "Unduhan gagal", + "download_filename": "berkas: {filename}", + "download_finished": "Unduhan selesai", + "download_include_embedded_motion_videos": "Video tertanam", + "download_include_embedded_motion_videos_description": "Sertakan video yang di sematkan dalam foto bergerak sebagai file terpisah", + "download_notfound": "Unduhan tidak ditemukan", + "download_paused": "Unduhan dijeda", + "download_settings": "Unduhan", "download_settings_description": "Kelola pengaturan berkaitan dengan pengunduhan aset", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", + "download_started": "Unduhan dimulai", + "download_sucess": "Unduhan sukses", + "download_sucess_android": "Media ini telah diunduh ke DCIM/Immich", + "download_waiting_to_retry": "Menunggu untuk mencoba lagi", "downloading": "Mengunduh", "downloading_asset_filename": "Mengunduh aset {filename}", - "downloading_media": "Downloading media", "drop_files_to_upload": "Lepaskan berkas di mana saja untuk mengunggah", "duplicates": "Duplikat", - "duplicates_description": "Selesaikan setiap kelompok dengan menandakan yang mana yang duplikat, jika ada", + "duplicates_description": "Selesaikan setiap kelompok dengan menunjukkan mana, jika ada, yang merupakan duplikat", "duration": "Durasi", "edit": "Sunting", "edit_album": "Sunting album", "edit_avatar": "Sunting avatar", "edit_date": "Tanggal penyuntingan", "edit_date_and_time": "Sunting tanggal dan waktu", + "edit_description": "Sunting deskripsi", + "edit_description_prompt": "Silakan pilih deskripsi baru:", "edit_exclusion_pattern": "Sunting pola pengecualian", "edit_faces": "Sunting wajah", "edit_import_path": "Sunting jalur pengimporan", @@ -796,25 +790,26 @@ "edit_title": "Sunting Judul", "edit_user": "Sunting pengguna", "edited": "Disunting", - "editor": "Editor", "editor_close_without_save_prompt": "Perubahan tidak akan di simpan", "editor_close_without_save_title": "Tutup editor?", "editor_crop_tool_h2_aspect_ratios": "Perbandingan aspek", "editor_crop_tool_h2_rotation": "Rotasi", "email": "Surel", + "email_notifications": "Notifikasi surel", "empty_folder": "This folder is empty", "empty_trash": "Kosongkan sampah", "empty_trash_confirmation": "Apakah Anda yakin ingin mengosongkan sampah? Ini akan menghapus semua aset dalam sampah secara permanen dari Immich.\nAnda tidak dapat mengurungkan tindakan ini!", "enable": "Aktifkan", + "enable_biometric_auth_description": "Masukkan kode PIN Anda untuk mengaktifkan autentikasi biometrik", "enabled": "Diaktifkan", "end_date": "Tanggal akhir", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enter_wifi_name": "Masukkan nama Wi-Fi", + "enter_your_pin_code": "Masukkan kode PIN Anda", + "enter_your_pin_code_subtitle": "Masukkan kode PIN Anda untuk mengakses folder terkunci", "error": "Eror", - "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Terjadi kesalahan menghapus wajah dari aset", "error_loading_image": "Terjadi eror memuat gambar", - "error_saving_image": "Error: {}", + "error_saving_image": "Kesalahan: {error}", "error_title": "Eror - Ada yang salah", "errors": { "cannot_navigate_next_asset": "Tidak dapat menuju ke aset berikutnya", @@ -844,10 +839,12 @@ "failed_to_keep_this_delete_others": "Gagal mempertahankan aset ini dan hapus aset-aset lainnya", "failed_to_load_asset": "Gagal membuka aset", "failed_to_load_assets": "Gagal membuka aset-aset", + "failed_to_load_notifications": "Gagal memuat notifikasi", "failed_to_load_people": "Gagal mengunggah orang", "failed_to_remove_product_key": "Gagal menghapus kunci produk", "failed_to_stack_assets": "Gagal menumpuk aset", "failed_to_unstack_assets": "Gagal membatalkan penumpukan aset", + "failed_to_update_notification_status": "Gagal membarui status notifikasi", "import_path_already_exists": "Jalur pengimporan ini sudah ada.", "incorrect_email_or_password": "Surel atau kata sandi tidak benar", "paths_validation_failed": "{paths, plural, one {# jalur} other {# jalur}} gagal validasi", @@ -865,6 +862,7 @@ "unable_to_archive_unarchive": "Tidak dapat {archived, select, true {mengarsipkan} other {membatalkan pengarsipan}}", "unable_to_change_album_user_role": "Tidak dapat mengubah peran pengguna album", "unable_to_change_date": "Tidak dapat mengubah tanggal", + "unable_to_change_description": "Tidak dapat mengubah deskripsi", "unable_to_change_favorite": "Tidak dapat mengubah favorit untuk aset", "unable_to_change_location": "Tidak dapat mengubah lokasi", "unable_to_change_password": "Tidak dapat mengubah kata sandi", @@ -902,6 +900,7 @@ "unable_to_log_out_all_devices": "Tidak dapat mengeluarkan dari semua perangkat", "unable_to_log_out_device": "Tidak dapat mengeluarkan perangkat", "unable_to_login_with_oauth": "Tidak dapat log masuk dengan OAuth", + "unable_to_move_to_locked_folder": "Tidak dapat memindahkan ke folder terkunci", "unable_to_play_video": "Tidak dapat memutar video", "unable_to_reassign_assets_existing_person": "Tidak dapat menetapkan aset kepada {name, select, null {orang yang sudah ada} other {{name}}}", "unable_to_reassign_assets_new_person": "Tidak dapat menetapkan ulang aset ke orang baru", @@ -915,6 +914,7 @@ "unable_to_remove_reaction": "Tidak dapat menghapus reaksi", "unable_to_repair_items": "Tidak dapat memperbaiki item", "unable_to_reset_password": "Tidak dapat mengatur ulang kata sandi", + "unable_to_reset_pin_code": "Tidak dapat mereset kode PIN", "unable_to_resolve_duplicate": "Tidak dapat menyelesaikan duplikat", "unable_to_restore_assets": "Tidak dapat memulihkan aset", "unable_to_restore_trash": "Tidak dapat memulihkan sampah", @@ -948,10 +948,10 @@ "exif_bottom_sheet_location": "LOKASI", "exif_bottom_sheet_people": "ORANG", "exif_bottom_sheet_person_add_person": "Tambah nama", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Umur {age}", + "exif_bottom_sheet_person_age_months": "Umur {months} bulan", + "exif_bottom_sheet_person_age_year_months": "Umur 1 tahun, {months} bulan", + "exif_bottom_sheet_person_age_years": "Umur {years}", "exit_slideshow": "Keluar dari Salindia", "expand_all": "Buka semua", "experimental_settings_new_asset_list_subtitle": "Memproses", @@ -968,12 +968,10 @@ "extension": "Ekstensi", "external": "Eksternal", "external_libraries": "Pustaka Eksternal", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network_sheet_info": "Ketika tidak berada di jaringan Wi-Fi yang disukai, aplikasi akan terhubung ke server melalui URL pertama di bawah ini yang dapat dijangkaunya, mulai dari atas ke bawah", "face_unassigned": "Tidak ada nama", - "failed": "Failed", + "failed_to_authenticate": "Autentikasi gagal", "failed_to_load_assets": "Gagal memuat aset", - "failed_to_load_folder": "Failed to load folder", "favorite": "Favorit", "favorite_or_unfavorite_photo": "Favorit atau batalkan pemfavoritan foto", "favorites": "Favorit", @@ -985,23 +983,19 @@ "file_name_or_extension": "Nama berkas atau ekstensi", "filename": "Nama berkas", "filetype": "Jenis berkas", - "filter": "Filter", "filter_people": "Saring orang", + "filter_places": "Saring tempat", "find_them_fast": "Temukan dengan cepat berdasarkan nama dengan pencarian", "fix_incorrect_match": "Perbaiki pencocokan salah", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Berkas", "folders_feature_description": "Menjelajahi tampilan folder untuk foto dan video pada sistem file", "forward": "Maju", "general": "Umum", "get_help": "Dapatkan Bantuan", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Memulai", "go_back": "Kembali", "go_to_folder": "Pergi ke folder", "go_to_search": "Pergi ke pencarian", - "grant_permission": "Grant permission", "group_albums_by": "Kelompokkan album berdasarkan...", "group_country": "Kelompokkan berdasarkan negara", "group_no": "Tidak ada pengelompokan", @@ -1011,12 +1005,6 @@ "haptic_feedback_switch": "Nyalakan getar", "haptic_feedback_title": "Getar", "has_quota": "Memiliki kuota", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "Hai {name} ({email})", "hide_all_people": "Sembunyikan semua orang", "hide_gallery": "Sembunyikan galeri", @@ -1025,21 +1013,15 @@ "hide_person": "Sembunyikan orang", "hide_unnamed_people": "Sembunyikan orang tanpa nama", "home_page_add_to_album_conflicts": "Aset {added} telah ditambahkan ke album {album}. Aset {failed} sudah ada dalam album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", "home_page_add_to_album_success": "Aset {added} telah ditambahkan ke {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", "home_page_building_timeline": "Memuat linimasa", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "Jika ini pertama kali Anda menggunakan aplikasi, pastikan untuk memilik album untuk dicadangkan agar linimasa anda terisi oleh foto dan video dalam album.", - "home_page_share_err_local": "Can not share local assets via link, skipping", + "home_page_first_time_notice": "Jika ini pertama kali Anda menggunakan aplikasi, pastikan untuk memiliki album untuk dicadangkan agar lini masa anda terisi oleh foto dan video dalam album", + "home_page_locked_error_local": "Tidak dapat memindahkan aset lokal ke folder terkunci, lewati", + "home_page_locked_error_partner": "Tidak dapat memindahkan aset partner ke folder terkunci, lewati", "home_page_upload_err_limit": "Hanya dapat mengunggah maksimal 30 aset dalam satu waktu, melewatkan", "host": "Hos", "hour": "Jam", + "id": "ID", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Gambar", @@ -1053,10 +1035,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1} dan {person2} pada {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {person3} pada {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {additionalCount, number} lainnya pada {date}", - "image_saved_successfully": "Image saved", "image_viewer_page_state_provider_download_started": "Unduh dimulai", "image_viewer_page_state_provider_download_success": "Unduh Sukses", - "image_viewer_page_state_provider_share_error": "Share Error", "immich_logo": "Logo Immich", "immich_web_interface": "Antarmuka Web Immich", "import_from_json": "Impor dari JSON", @@ -1068,15 +1048,12 @@ "include_shared_partner_assets": "Termasuk aset terbagi dengan partner", "individual_share": "Bagikan individu", "individual_shares": "Pembagian individu", - "info": "Info", "interval": { "day_at_onepm": "Setiap hari pada 13.00", "hours": "Setiap {hours, plural, one {jam} other {{hours, number} jam}}", "night_at_midnight": "Setiap malam pada 00.00", "night_at_twoam": "Setiap malam pada 02.00" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "Undang Orang", "invite_to_album": "Undang ke album", "items_count": "{count, plural, one {# item} other {# item}}", @@ -1112,15 +1089,14 @@ "list": "Daftar", "loading": "Memuat", "loading_search_results_failed": "Pemuatan hasil pencarian gagal", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "location_permission_content": "Untuk menggunakan fitur pengalihan otomatis, Immich memerlukan izin lokasi yang akurat agar dapat membaca nama jaringan Wi-Fi saat ini", "location_picker_choose_on_map": "Pilih di peta", "location_picker_latitude_error": "Masukkan lintang yang sah", "location_picker_latitude_hint": "Masukkan lintang di sini", "location_picker_longitude_error": "Masukkan bujur yang sah", "location_picker_longitude_hint": "Masukkan bujur di sini", + "lock": "Kunci", + "locked_folder": "Folder Terkunci", "log_out": "Log keluar", "log_out_all_devices": "Keluar dari Semua Perangkat", "logged_out_all_devices": "Semua perangkat telah dikeluarkan", @@ -1135,12 +1111,9 @@ "login_form_err_http": "Harap tentukan http:// atau https://", "login_form_err_invalid_email": "Email Tidak Valid", "login_form_err_invalid_url": "URL Tidak Valid", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", "login_form_failed_get_oauth_server_config": "Gagal logging menggunakan OAuth, periksa URL server", "login_form_failed_get_oauth_server_disable": "Fitur OAuth tidak tersedia di server ini", - "login_form_failed_login": "Login gagal. Periksa URL server, email dan password.", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", + "login_form_failed_login": "Login gagal, periksa URL server, email dan kata sandi", "login_form_password_hint": "sandi", "login_form_save_login": "Ingat saya", "login_form_server_empty": "Masukkan URL server.", @@ -1155,6 +1128,7 @@ "loop_videos": "Ulangi video", "loop_videos_description": "Aktifkan untuk mengulangi video secara otomatis dalam penampil detail.", "main_branch_warning": "Anda menggunakan versi pengembangan; kami sangat menyarankan menggunakan versi rilis!", + "main_menu": "Menu utama", "make": "Merek", "manage_shared_links": "Kelola tautan terbagi", "manage_sharing_with_partners": "Kelola pembagian dengan partner", @@ -1164,8 +1138,8 @@ "manage_your_devices": "Kelola perangkat Anda yang masuk", "manage_your_oauth_connection": "Kelola koneksi OAuth Anda", "map": "Peta", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} foto", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} foto", "map_cannot_get_user_location": "Tidak dapat memeroleh lokasi pengguna", "map_location_dialog_yes": "Ya", "map_location_picker_page_use_location": "Gunakan lokasi ini", @@ -1179,15 +1153,16 @@ "map_settings": "Pengaturan peta", "map_settings_dark_mode": "Mode gelap", "map_settings_date_range_option_day": "24 jam terakhir", - "map_settings_date_range_option_days": "{} hari terakhir", + "map_settings_date_range_option_days": "{days} hari terakhir", "map_settings_date_range_option_year": "1 tahun terakhir", - "map_settings_date_range_option_years": "{} tahun terakhir", + "map_settings_date_range_option_years": "{years} tahun terakhir", "map_settings_dialog_title": "Pengaturan Peta", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", "map_settings_only_show_favorites": "Tampilkan Hanya Favorit", "map_settings_theme_settings": "Tema Peta", "map_zoom_to_see_photos": "Perkecil untuk lihat foto", + "mark_all_as_read": "Tandai semua sebagai telah dibaca", + "mark_as_read": "Tandai sebagai telah dibaca", + "marked_all_as_read": "Semua telah ditandai sebagai telah dibaca", "matches": "Cocokan", "media_type": "Jenis media", "memories": "Kenangan", @@ -1196,11 +1171,8 @@ "memories_setting_description": "Kelola apa yang Anda lihat dalam kenangan Anda", "memories_start_over": "Ulang Dari Awal", "memories_swipe_to_close": "Geser ke atas untuk menutup", - "memories_year_ago": "Satu tahun lalu", - "memories_years_ago": "{} tahun lalu", "memory": "Kenangan", "memory_lane_title": "Jalur Kenangan {title}", - "menu": "Menu", "merge": "Gabungkan", "merge_people": "Gabungkan orang", "merge_people_limit": "Anda hanya dapat menggabungkan sampai 5 wajah sekaligus", @@ -1210,24 +1182,26 @@ "minimize": "Kecilkan", "minute": "Menit", "missing": "Hilang", - "model": "Model", "month": "Bulan", - "monthly_title_text_date_format": "MMMM y", "more": "Lainnya", + "move": "Pindah", + "move_off_locked_folder": "Dikeluarkan dari Folder Terkunci", + "move_to_locked_folder": "Dipindahkan ke Folder Terkunci", + "move_to_locked_folder_confirmation": "Foto dan video ini akan dihapus dari seluruh album, dan hanya dapat dilihat melalui Folder Terkunci", + "moved_to_archive": "Dipindahkan {count, plural, one {# asset} other {# assets}} ke arsip", + "moved_to_library": "Dipindahkan {count, plural, one {# asset} other {# assets}} ke pustaka", "moved_to_trash": "Dipindahkan ke sampah", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", "mute_memories": "Nonaktifkan Kenangan", "my_albums": "Album saya", "name": "Nama", "name_or_nickname": "Nama atau nama panggilan", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "Tidak pernah", "new_album": "Album baru", "new_api_key": "Kunci API Baru", "new_password": "Kata sandi baru", "new_person": "Orang baru", + "new_pin_code": "Kode PIN baru", + "new_pin_code_subtitle": "Ini adalah akses pertama Anda ke folder terkunci. Buat kode PIN untuk mengamankan akses ke halaman ini", "new_user_created": "Pengguna baru dibuat", "new_version_available": "VERSI BARU TERSEDIA", "newest_first": "Terkini dahulu", @@ -1245,15 +1219,18 @@ "no_explore_results_message": "Unggah lebih banyak foto untuk menjelajahi koleksi Anda.", "no_favorites_message": "Tambahkan favorit untuk mencari foto dan video terbaik Anda dengan cepat", "no_libraries_message": "Buat pustaka eksternal untuk menampilkan foto dan video Anda", + "no_locked_photos_message": "Foto dan video di Folder Terkunci disembunyikan dan tidak akan muncul saat Anda menelusuri pustaka.", "no_name": "Tidak Ada Nama", + "no_notifications": "Tidak ada notifikasi", + "no_people_found": "Orang tidak ditemukan", "no_places": "Tidak ada tempat", "no_results": "Tidak ada hasil", "no_results_description": "Coba sinonim atau kata kunci yang lebih umum", "no_shared_albums_message": "Buat sebuah album untuk membagikan foto dan video dengan orang-orang dalam jaringan Anda", "not_in_any_album": "Tidak ada dalam album apa pun", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Catatan: Untuk menerapkan Label Penyimpanan pada aset yang sebelumnya telah diunggah, jalankan", "notes": "Catatan", + "nothing_here_yet": "Masih kosong", "notification_permission_dialog_content": "Untuk mengaktifkan notifikasi, buka Pengaturan lalu berikan izin.", "notification_permission_list_tile_content": "Berikan izin untuk mengaktifkan notifikasi.", "notification_permission_list_tile_enable_button": "Aktifkan Notifikasi", @@ -1261,14 +1238,12 @@ "notification_toggle_setting_description": "Aktifkan notifikasi surel", "notifications": "Notifikasi", "notifications_setting_description": "Kelola notifikasi", - "oauth": "OAuth", "official_immich_resources": "Sumber Daya Immich Resmi", "offline": "Luring", "offline_paths": "Jalur luring", "offline_paths_description": "Hasil berikut dapat diakibatkan oleh penghapusan berkas manual yang bukan bagian dari pustaka eksternal.", "ok": "Oke", "oldest_first": "Terlawas dahulu", - "on_this_device": "On this device", "onboarding": "Memulai", "onboarding_privacy_description": "Fitur berikut (opsional) bergantung pada layanan eksternal, dan dapat dinonaktifkan kapan saja di pengaturan administrasi.", "onboarding_theme_description": "Pilih tema warna untuk server Anda. Ini dapat diubah lagi dalam pengaturan Anda.", @@ -1276,6 +1251,7 @@ "onboarding_welcome_user": "Selamat datang, {user}", "online": "Daring", "only_favorites": "Hanya favorit", + "open": "Buka", "open_in_map_view": "Buka dalam tampilan peta", "open_in_openstreetmap": "Buka di OpenStreetMap", "open_the_search_filters": "Buka saringan pencarian", @@ -1299,7 +1275,7 @@ "partner_page_partner_add_failed": "Gagal menambahkan partner", "partner_page_select_partner": "Pilih partner", "partner_page_shared_to_title": "Dibagikan dengan", - "partner_page_stop_sharing_content": "{} tidak akan bisa mengakses foto kamu lagi.", + "partner_page_stop_sharing_content": "{partner} tidak akan bisa mengakses foto Anda lagi.", "partner_sharing": "Pembagian Partner", "partners": "Partner", "password": "Kata sandi", @@ -1330,7 +1306,6 @@ "permanently_deleted_assets_count": "{count, plural, one {# aset} other {# aset}} dihapus secara permanen", "permission_onboarding_back": "Kembali", "permission_onboarding_continue_anyway": "Lanjutkan saja", - "permission_onboarding_get_started": "Get started", "permission_onboarding_go_to_settings": "Buka setelan", "permission_onboarding_permission_denied": "Izin ditolak. Untuk menggunakan Immich, berikan izin akses foto dan video di Setelan.", "permission_onboarding_permission_granted": "Izin diberikan! Semua sudah siap.", @@ -1345,6 +1320,10 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Foto}}", "photos_from_previous_years": "Foto dari tahun lalu", "pick_a_location": "Pilih lokasi", + "pin_code_changed_successfully": "Berhasil mengubah kode PIN", + "pin_code_reset_successfully": "Berhasil mereset kode PIN", + "pin_code_setup_successfully": "Berhasil memasang kode PIN", + "pin_verification": "Verifikasi kode PIN", "place": "Tempat", "places": "Tempat", "places_count": "{count, plural, one {{count, number} Tempat} other {{count, number} Tempat}}", @@ -1352,8 +1331,8 @@ "play_memories": "Putar kenangan", "play_motion_photo": "Putar Foto Gerak", "play_or_pause_video": "Putar atau jeda video", + "please_auth_to_access": "Silakan autentikasi untuk mengakses", "port": "Porta", - "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "Preferensi", "preset": "Prasetel", "preview": "Pratinjau", @@ -1362,11 +1341,11 @@ "previous_or_next_photo": "Foto sebelumnya atau berikutnya", "primary": "Utama", "privacy": "Privasi", + "profile": "Profil", "profile_drawer_app_logs": "Log", "profile_drawer_client_out_of_date_major": "Versi app seluler ini sudah kedaluwarsa. Silakan perbarui ke versi major terbaru.", "profile_drawer_client_out_of_date_minor": "Versi app seluler ini sudah kedaluwarsa. Silakan perbarui ke versi minor terbaru.", "profile_drawer_client_server_up_to_date": "Klien dan server menjalankan versi terbaru", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Versi server ini telah kedaluwarsa. Silakan perbarui ke versi major terbaru.", "profile_drawer_server_out_of_date_minor": "Versi server ini telah kedaluwarsa. Silakan perbarui ke versi minor terbaru.", "profile_image_of_user": "Foto profil dari {user}", @@ -1375,7 +1354,7 @@ "public_share": "Pembagian Publik", "purchase_account_info": "Pendukung", "purchase_activated_subtitle": "Terima kasih telah mendukung Immich dan perangkat lunak sumber terbuka", - "purchase_activated_time": "Di aktivasi pada {date, date}", + "purchase_activated_time": "Di aktivasi pada {date}", "purchase_activated_title": "Kunci kamu telah sukses di aktivasi", "purchase_button_activate": "Aktifkan", "purchase_button_buy": "Beli", @@ -1395,7 +1374,6 @@ "purchase_panel_info_1": "Membangun Immich membutuhkan banyak waktu dan upaya, dan kami memiliki insinyur penuh waktu yang bekerja untuk membuatnya sebaik mungkin. Misi kami adalah agar perangkat lunak sumber terbuka dan praktik bisnis yang beretika menjadi sumber pendapatan yang berkelanjutan bagi para pengembang dan menciptakan ekosistem yang menghargai privasi dengan alternatif nyata untuk layanan cloud yang eksploitatif.", "purchase_panel_info_2": "Karena kami berkomitmen untuk tidak menambahkan paywall, pembelian ini tidak akan memberi kamu fitur tambahan apa pun di Immich. Kami mengandalkan pengguna seperti kamu untuk mendukung pengembangan Immich yang sedang berlangsung.", "purchase_panel_title": "Dukung proyek ini", - "purchase_per_server": "Per server", "purchase_per_user": "Per pengguna", "purchase_remove_product_key": "Hapus Kunci Produk", "purchase_remove_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk?", @@ -1403,7 +1381,6 @@ "purchase_remove_server_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk Server?", "purchase_server_description_1": "Untuk keseluruhan server", "purchase_server_description_2": "Status pendukung", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Kunci produk server dikelola oleh admin", "rating": "Peringkat bintang", "rating_clear": "Hapus peringkat", @@ -1418,8 +1395,9 @@ "recent": "Terkini", "recent-albums": "Album terkini", "recent_searches": "Pencarian terkini", - "recently_added": "Recently added", "recently_added_page_title": "Baru Ditambahkan", + "recently_taken": "Diambil terkini", + "recently_taken_page_title": "Diambil Terkini", "refresh": "Segarkan", "refresh_encoded_videos": "Segarkan video terenkode", "refresh_faces": "Segarkan wajah", @@ -1439,6 +1417,8 @@ "remove_deleted_assets": "Hapus Berkas Luring", "remove_from_album": "Hapus dari album", "remove_from_favorites": "Hapus dari favorit", + "remove_from_locked_folder": "Hapus dari Folder Terkunci", + "remove_from_locked_folder_confirmation": "Apakah Anda yakin ingin mengeluarkan foto dan video dari Folder Terkunci? Semua itu akan terlihat di pustaka Anda", "remove_from_shared_link": "Hapus dari tautan terbagi", "remove_memory": "Hapus kenangan", "remove_photo_from_memory": "Hapus foto dari kenangan ini", @@ -1462,6 +1442,7 @@ "reset": "Atur ulang", "reset_password": "Atur ulang kata sandi", "reset_people_visibility": "Atur ulang keterlihatan orang", + "reset_pin_code": "Reset kode PIN", "reset_to_default": "Atur ulang ke bawaan", "resolve_duplicates": "Mengatasi duplikat", "resolved_all_duplicates": "Semua duplikat terselesaikan", @@ -1476,7 +1457,6 @@ "role_editor": "Penyunting", "role_viewer": "Penampil", "save": "Simpan", - "save_to_gallery": "Save to gallery", "saved_api_key": "Kunci API Tersimpan", "saved_profile": "Profil disimpan", "saved_settings": "Pengaturan disimpan", @@ -1498,33 +1478,18 @@ "search_city": "Cari kota...", "search_country": "Cari negara...", "search_filter_apply": "Terapkan filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", "search_filter_display_option_not_in_album": "Tidak dalam album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", "search_for": "Cari", "search_for_existing_person": "Cari orang yang sudah ada", - "search_no_more_result": "No more results", "search_no_people": "Tidak ada orang", "search_no_people_named": "Tidak ada orang bernama \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_options": "Pilihan pencarian", "search_page_categories": "Kategori", "search_page_motion_photos": "Foto Bergerak", "search_page_no_objects": "Tidak Ada Info Objek", "search_page_no_places": "Tidak Ada Info Lokasi", "search_page_screenshots": "Tangkapan Layar", - "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": "Swafoto", - "search_page_things": "Things", "search_page_view_all_button": "Lihat semua", "search_page_your_activity": "Aktivitasmu", "search_page_your_map": "Peta Anda", @@ -1534,7 +1499,7 @@ "search_result_page_new_search_hint": "Pencarian Baru", "search_settings": "Pengaturan pencarian", "search_state": "Cari negara bagian...", - "search_suggestion_list_smart_search_hint_1": "Penelusuran cerdas aktif secara bawaan. Untuk menelusuri metadata, gunakan sintaks", + "search_suggestion_list_smart_search_hint_1": "Penelusuran cerdas aktif secara bawaan. Untuk menelusuri metadata, gunakan sintaks ", "search_suggestion_list_smart_search_hint_2": "m:penelusuran-kamu", "search_tags": "Cari tag...", "search_timezone": "Cari zona waktu...", @@ -1554,6 +1519,7 @@ "select_keep_all": "Pilih simpan semua", "select_library_owner": "Pilih pemilik pustaka", "select_new_face": "Pilih wajah baru", + "select_person_to_tag": "Pilih orang untuk ditandai", "select_photos": "Pilih foto", "select_trash_all": "Pilih buang semua", "select_user_for_sharing_page_err_album": "Gagal membuat album", @@ -1561,7 +1527,6 @@ "selected_count": "{count, plural, other {# dipilih}}", "send_message": "Kirim pesan", "send_welcome_email": "Kirim surel selamat datang", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "Versi App", "server_info_box_server_url": "URL Server", "server_offline": "Server Luring", @@ -1575,39 +1540,33 @@ "set_date_of_birth": "Atur tanggal lahir", "set_profile_picture": "Tetapkan foto profil", "set_slideshow_to_fullscreen": "Atur Salindia ke layar penuh", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", "setting_image_viewer_original_subtitle": "Aktifkan untuk memuat gambar asli dengan resolusi penuh (berukuran besar!). Nonaktifkan untuk mengurangi penggunaan data (baik jaringan maupun cache perangkat).", "setting_image_viewer_original_title": "Muat gambar kualitas asli", "setting_image_viewer_preview_subtitle": "Aktifkan untuk memuat gambar dengan resolusi sedang. Nonaktifkan jika ingin langsung memuat gambar asli atau hanya ingin memuat thumbnail.", "setting_image_viewer_preview_title": "Muat gambar preview", "setting_image_viewer_title": "Foto", "setting_languages_apply": "Terapkan", - "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "Bahasa", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} jam", + "setting_notifications_notify_failures_grace_period": "Beritahu kegagalan cadangan latar belakang: {duration}", + "setting_notifications_notify_hours": "{count} jam", "setting_notifications_notify_immediately": "segera", - "setting_notifications_notify_minutes": "{} menit", + "setting_notifications_notify_minutes": "{count} menit", "setting_notifications_notify_never": "Jangan pernah", - "setting_notifications_notify_seconds": "{} detik", + "setting_notifications_notify_seconds": "{count} detik", "setting_notifications_single_progress_subtitle": "Rincian info proses unggah setiap asset", "setting_notifications_single_progress_title": "Tampilkan rincian proses cadangkan latar belakang", "setting_notifications_subtitle": "Atur setelan notifikasi", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", "setting_video_viewer_looping_title": "Ulangi", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Pengaturan", "settings_require_restart": "Harap mulai ulang Immich untuk menerapkan pengaturan ini", "settings_saved": "Pengaturan disimpan", + "setup_pin_code": "Pasang kode PIN", "share": "Bagikan", "share_add_photos": "Tambah foto", - "share_assets_selected": "{} terpilih", + "share_assets_selected": "{count} dipilih", "share_dialog_preparing": "Menyiapkan...", + "share_link": "Bagikan Link", "shared": "Dibagikan", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", "shared_album_activity_remove_title": "Hapus Aktivitas", "shared_album_section_people_action_error": "Gagal menghapus dari album", "shared_album_section_people_action_leave": "Hapus pengguna dari album", @@ -1617,40 +1576,37 @@ "shared_by_user": "Dibagikan oleh {user}", "shared_by_you": "Dibagikan oleh Anda", "shared_from_partner": "Foto dari {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Diunggah", "shared_link_app_bar_title": "Link Berbagi", "shared_link_clipboard_copied_massage": "Tersalin ke papan klip", - "shared_link_clipboard_text": "Link: {}\nSandi: {}", + "shared_link_clipboard_text": "Tautan: {link}\nKata Sandi: {password}", "shared_link_create_error": "Terjadi kesalahan saat membuat link berbagi", "shared_link_edit_description_hint": "Masukkan deskripsi link", "shared_link_edit_expire_after_option_day": "1 hari", - "shared_link_edit_expire_after_option_days": "{} hari", + "shared_link_edit_expire_after_option_days": "{count} hari", "shared_link_edit_expire_after_option_hour": "1 jam", - "shared_link_edit_expire_after_option_hours": "{} jam", + "shared_link_edit_expire_after_option_hours": "{count} jam", "shared_link_edit_expire_after_option_minute": "1 menit", - "shared_link_edit_expire_after_option_minutes": "{} menit", - "shared_link_edit_expire_after_option_months": "{} bulan", - "shared_link_edit_expire_after_option_year": "{} tahun", + "shared_link_edit_expire_after_option_minutes": "{count} menit", + "shared_link_edit_expire_after_option_months": "{count} bulan", + "shared_link_edit_expire_after_option_year": "{count} tahun", "shared_link_edit_password_hint": "Masukkan sandi link", "shared_link_edit_submit_button": "Perbarui link", "shared_link_error_server_url_fetch": "Tidak dapat memuat url server", - "shared_link_expires_day": "Kedaluwarsa dalam {} hari", - "shared_link_expires_days": "Kedaluwarsa dalam {} hari", - "shared_link_expires_hour": "Kedaluwarsa dalam {} jam", - "shared_link_expires_hours": "Kedaluwarsa dalam {} jam", - "shared_link_expires_minute": "Kedaluwarsa dalam {} menit", - "shared_link_expires_minutes": "Kedaluwarsa dalam {} menit", + "shared_link_expires_day": "Kedaluwarsa dalam {count} hari", + "shared_link_expires_days": "Kedaluwarsa dalam {count} hari", + "shared_link_expires_hour": "Kedaluwarsa dalam {count} jam", + "shared_link_expires_hours": "Kedaluwarsa dalam {count} jam", + "shared_link_expires_minute": "Kedaluwarsa dalam {count} menit", + "shared_link_expires_minutes": "Kedaluwarsa dalam {count} menit", "shared_link_expires_never": "Tidak akan kedaluwarsa", - "shared_link_expires_second": "Kedaluwarsa dalam {} detik", - "shared_link_expires_seconds": "Kedaluwarsa dalam {} detik", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", + "shared_link_expires_second": "Kedaluwarsa dalam {count} detik", + "shared_link_expires_seconds": "Kedaluwarsa dalam {count} detik", "shared_link_manage_links": "Atur link berbagi", "shared_link_options": "Pilihan tautan bersama", "shared_links": "Tautan terbagi", "shared_links_description": "Bagikan foto dan video dengan tautan", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video terbagi.}}", - "shared_with_me": "Shared with me", "shared_with_partner": "Dibagikan dengan {partner}", "sharing": "Pembagian", "sharing_enter_password": "Masukkan kata sandi untuk membuka tautan halaman ini.", @@ -1710,13 +1666,13 @@ "start": "Mulai", "start_date": "Tanggal mulai", "state": "Keadaan", - "status": "Status", "stop_motion_photo": "Hentikan Foto Gerak", "stop_photo_sharing": "Berhenti membagikan foto Anda?", "stop_photo_sharing_description": "{partner} tidak akan dapat mengakses foto Anda lagi.", "stop_sharing_photos_with_user": "Berhenti membagikan foto Anda dengan pengguna ini", "storage": "Ruang penyimpanan", "storage_label": "Label penyimpanan", + "storage_quota": "Kuota Penyimpanan", "storage_usage": "{used} dari {available} digunakan", "submit": "Kirim", "suggestions": "Saran", @@ -1726,10 +1682,6 @@ "support_third_party_description": "Pemasangan Immich Anda telah dipaketkan oleh pihak ketiga. Masalah yang Anda alami dapat disebabkan oleh paket tersebut, jadi silakan ajukan isu dengan masalah tersebut menggunakan tautan di bawah.", "swap_merge_direction": "Ganti arah penggabungan", "sync": "Sinkronisasikan", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "tag": "Tag", "tag_assets": "Tag aset", "tag_created": "Tag yang di buat: {tag}", "tag_feature_description": "Menjelajahi foto dan video yang dikelompokkan berdasarkan topik tag logis", @@ -1743,18 +1695,11 @@ "theme_selection": "Pemilihan tema", "theme_selection_description": "Tetapkan tema ke terang atau gelap secara otomatis berdasarkan preferensi sistem peramban Anda", "theme_setting_asset_list_storage_indicator_title": "Tampilkan sisa penyimpanan", - "theme_setting_asset_list_tiles_per_row_title": "Jumlah aset per baris", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Jumlah aset per baris ({count})", "theme_setting_image_viewer_quality_subtitle": "Atur kualitas dari penampil gambar", "theme_setting_image_viewer_quality_title": "Kualitas penampil gambar", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "Otomatis (Ikuti pengaturan sistem)", "theme_setting_theme_subtitle": "Pilih setelan tema aplikasi", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", "they_will_be_merged_together": "Mereka akan digabungkan bersama", "third_party_resources": "Sumber Daya Pihak Ketiga", "time_based_memories": "Kenangan berbasis waktu", @@ -1774,17 +1719,18 @@ "trash_all": "Buang Semua", "trash_count": "Sampah {count, number}", "trash_delete_asset": "Hapus Aset", - "trash_emptied": "Emptied trash", "trash_no_results_message": "Foto dan video di sampah akan muncul di sini.", "trash_page_delete_all": "Hapus Semua", "trash_page_empty_trash_dialog_content": "Apakah kamu ingin menghapus semua aset di sampah? Item tersebut akan dihapus secara permanen dari Immich", - "trash_page_info": "Item yang dipindahkan ke sampah akan terhapus secara permanen setelah {} hari", + "trash_page_info": "Item yang dipindahkan ke sampah akan terhapus secara permanen setelah {days} hari", "trash_page_no_assets": "Tidak ada aset di sampah", "trash_page_restore_all": "Pulihkan Semua", "trash_page_select_assets_btn": "Pilih aset", - "trash_page_title": "Sampah ({})", + "trash_page_title": "Sampah ({count})", "trashed_items_will_be_permanently_deleted_after": "Item yang dibuang akan dihapus secara permanen setelah {days, plural, one {# hari} other {# hari}}.", "type": "Jenis", + "unable_to_change_pin_code": "Tidak dapat mengubah kode PIN", + "unable_to_setup_pin_code": "Tidak dapat memasang kode PIN", "unarchive": "Keluarkan dari arsip", "unarchived_count": "{count, plural, other {# dipindahkan dari arsip}}", "unfavorite": "Hapus favorit", @@ -1808,6 +1754,7 @@ "untracked_files": "Berkas tidak dilacak", "untracked_files_decription": "Berkas ini tidak dilacak oleh aplikasi. Mereka dapat diakibatkan oleh pemindahan gagal, pengunggahan terganggu, atau tertinggal karena oleh kutu", "up_next": "Berikutnya", + "updated_at": "Diperbarui", "updated_password": "Kata sandi diperbarui", "upload": "Unggah", "upload_concurrency": "Konkurensi pengunggahan", @@ -1820,15 +1767,16 @@ "upload_status_errors": "Eror", "upload_status_uploaded": "Diunggah", "upload_success": "Pengunggahan berhasil, muat ulang laman untuk melihat aset terunggah yang baru.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "Unggah ke Immich ({count})", "usage": "Penggunaan", - "use_current_connection": "use current connection", + "use_biometric": "Gunakan biometrik", "use_custom_date_range": "Gunakan jangka tanggal khusus saja", "user": "Pengguna", + "user_has_been_deleted": "Pengguna ini telah dihapus.", "user_id": "ID Pengguna", "user_liked": "{user} menyukai {type, select, photo {foto ini} video {tayangan ini} asset {aset ini} other {ini}}", + "user_pin_code_settings": "Kode PIN", + "user_pin_code_settings_description": "Atur kode PIN Anda", "user_purchase_settings": "Pembelian", "user_purchase_settings_description": "Atur pembelian kamu", "user_role_set": "Tetapkan {user} sebagai {role}", @@ -1839,19 +1787,13 @@ "users": "Pengguna", "utilities": "Peralatan", "validate": "Validasi", - "validate_endpoint_error": "Please enter a valid URL", "variables": "Variabel", "version": "Versi", "version_announcement_closing": "Temanmu, Alex", "version_announcement_message": "Hai! Versi baru Immich telah tersedia. Harap luangkan waktu untuk membaca catatan rilis untuk memastikan pengaturan Anda terkini untuk mencegah kesalahan konfigurasi, terutama jika Anda menggunakan WatchTower atau mekanisme apa pun yang menangani pembaruan server Immich secara otomatis.", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", "version_announcement_overlay_title": "Server Versi Baru Tersedia 🎉", "version_history": "Riwayat Versi", "version_history_item": "Terpasang {version} pada {date}", - "video": "Video", "video_hover_setting": "Putar gambar kecil video saat kursor di atas", "video_hover_setting_description": "Putar gambar kecil video ketika tetikus berada di atas item. Bahkan saat dinonaktifkan, pemutaran dapat dimulai dengan mengambang di atas ikon putar.", "videos": "Video", @@ -1866,21 +1808,22 @@ "view_name": "Tampilkan", "view_next_asset": "Tampilkan aset berikutnya", "view_previous_asset": "Tampilkan aset sebelumnya", + "view_qr_code": "Tampilkan kode QR", "view_stack": "Tampilkan Tumpukan", "viewer_remove_from_stack": "Keluarkan dari Tumpukan", "viewer_stack_use_as_main_asset": "Gunakan sebagai aset utama", - "viewer_unstack": "Un-Stack", "visibility_changed": "Keterlihatan diubah untuk {count, plural, one {# orang} other {# orang}}", "waiting": "Menunggu", "warning": "Peringatan", "week": "Pekan", "welcome": "Selamat datang", "welcome_to_immich": "Selamat datang di Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nama Wi-Fi", + "wrong_pin_code": "Kode PIN salah", "year": "Tahun", "years_ago": "{years, plural, one {# tahun} other {# tahun}} yang lalu", "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak memiliki tautan terbagi", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Nama Wi-Fi Anda", "zoom_image": "Perbesar Gambar" } diff --git a/i18n/it.json b/i18n/it.json index 01aeb93721..8721cefc61 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Sei sicuro di voler disabilitare tutte le modalità di accesso? Il login verrà disabilitato completamente.", "authentication_settings_reenable": "Per ri-abilitare, utilizza un Comando Server.", "background_task_job": "Attività in Background", - "backup_database": "Database di Backup", + "backup_database": "Crea Dump Database", "backup_database_enable_description": "Abilita i backup del database", - "backup_keep_last_amount": "Quantità di backup precedenti da mantenere", - "backup_settings": "Impostazioni di backup", - "backup_settings_description": "Gestisci le impostazioni dei backup", + "backup_keep_last_amount": "Numero di backup da mantenere", + "backup_settings": "Impostazioni Dump database", + "backup_settings_description": "Gestisci le impostazioni dei backup. Nota: Questi jobs non sono monitorati e non riceverai notifiche in caso di errore.", "check_all": "Controlla Tutto", "cleanup": "Pulisci", "cleared_jobs": "Cancellati i processi per: {job}", @@ -53,6 +53,7 @@ "confirm_email_below": "Per confermare, scrivi \"{email}\" qui sotto", "confirm_reprocess_all_faces": "Sei sicuro di voler riprocessare tutti i volti? Questo cancellerà tutte le persone nominate.", "confirm_user_password_reset": "Sei sicuro di voler resettare la password di {user}?", + "confirm_user_pin_code_reset": "Sicuro di voler resettare il codice PIN di {user}?", "create_job": "Crea Processo", "cron_expression": "Espressione Cron", "cron_expression_description": "Imposta il tempo di scansione utilizzando il formato Cron. Per ulteriori informazioni fare riferimento a Crontab Guru", @@ -192,26 +193,22 @@ "oauth_auto_register": "Registrazione automatica", "oauth_auto_register_description": "Automaticamente registra nuovi utenti dopo il login OAuth", "oauth_button_text": "Testo pulsante", - "oauth_client_id": "ID Cliente", - "oauth_client_secret": "Chiave segreta client", + "oauth_client_secret_description": "Richiesto se PKCE (Proof Key for Code Exchange) non è supportato dal provider OAuth", "oauth_enable_description": "Login con OAuth", - "oauth_issuer_url": "URL emittente", "oauth_mobile_redirect_uri": "URI reindirizzamento mobile", "oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare", "oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmo firma profilo", - "oauth_profile_signing_algorithm_description": "L'algoritmo usato per firmare il profilo utente.", - "oauth_scope": "Ambito di autorizzazione", "oauth_settings": "OAuth", "oauth_settings_description": "Gestisci impostazioni di login OAuth", "oauth_settings_more_details": "Per piÚ dettagli riguardo a questa funzionalità, consulta la documentazione.", - "oauth_signing_algorithm": "Algoritmo di firma", "oauth_storage_label_claim": "Dichiarazione di ambito(claim) etichetta archiviazione", "oauth_storage_label_claim_description": "Imposta automaticamente l'etichetta dell'archiviazione dell'utente al valore di questa dichiarazione di ambito(claim).", "oauth_storage_quota_claim": "Dichiarazione di ambito(claim) limite archiviazione", "oauth_storage_quota_claim_description": "Imposta automaticamente il limite di archiviazione dell'utente in base al valore di questa dichiarazione di ambito(claim).", "oauth_storage_quota_default": "Limite predefinito di archiviazione (GiB)", "oauth_storage_quota_default_description": "Limite in GiB da usare quanto nessuna dichiarazione di ambito(claim) è stata fornita (Inserisci 0 per archiviazione illimitata).", + "oauth_timeout": "Timeout Richiesta", + "oauth_timeout_description": "Timeout per le richieste, espresso in millisecondi", "offline_paths": "Percorsi offline", "offline_paths_description": "Questi risultati potrebbero essere dovuti all'eliminazione manuale di file che non fanno parte di una libreria esterna.", "password_enable_description": "Login con email e password", @@ -352,6 +349,7 @@ "user_delete_delay_settings_description": "Numero di giorni dopo l'eliminazione per cancellare in modo definitivo l'account e gli asset di un utente. Il processo di cancellazione dell'utente viene eseguito a mezzanotte per verificare se esistono utenti pronti a essere eliminati. Le modifiche a questa impostazioni saranno prese in considerazione dalla prossima esecuzione.", "user_delete_immediately": "L'account e tutti gli asset dell'utente {user} verranno messi in coda per la cancellazione permanente immediata.", "user_delete_immediately_checkbox": "utente", + "user_details": "Dettagli Utente", "user_management": "Gestione Utenti", "user_password_has_been_reset": "La password dell'utente è stata reimpostata:", "user_password_reset_description": "Per favore inserisci una password temporanea per l'utente e informalo che dovrà cambiare la password al prossimo login.", @@ -371,13 +369,16 @@ "admin_password": "Password Amministratore", "administration": "Amministrazione", "advanced": "Avanzate", - "advanced_settings_log_level_title": "Livello log: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Usa questa opzione per filtrare i contenuti multimediali durante la sincronizzazione in base a criteri alternativi. Prova questa opzione solo se riscontri problemi con il rilevamento di tutti gli album da parte dell'app.", + "advanced_settings_enable_alternate_media_filter_title": "[SPERIMENTALE] Usa un filtro alternativo per la sincronizzazione degli album del dispositivo", + "advanced_settings_log_level_title": "Livello log: {level}", "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini dal dispositivo. Attivare questa impostazione per caricare invece le immagini remote.", - "advanced_settings_prefer_remote_title": "Preferisci immagini remote.", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_prefer_remote_title": "Preferisci immagini remote", + "advanced_settings_proxy_headers_subtitle": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", "advanced_settings_self_signed_ssl_subtitle": "Salta la verifica dei certificati SSL del server. Richiesto con l'uso di certificati self-signed.", "advanced_settings_self_signed_ssl_title": "Consenti certificati SSL self-signed", + "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo se l'azione è stata fatta via web", + "advanced_settings_sync_remote_deletions_title": "Sincronizza le cancellazioni remote [SPERIMENTALE]", "advanced_settings_tile_subtitle": "Impostazioni aggiuntive utenti", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", @@ -399,19 +400,19 @@ "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", - "album_thumbnail_card_item": "1 elemento ", - "album_thumbnail_card_items": "{} elementi", - "album_thumbnail_card_shared": "Condiviso", - "album_thumbnail_shared_by": "Condiviso da {}", + "album_thumbnail_card_item": "1 elemento", + "album_thumbnail_card_items": "{count} elementi", + "album_thumbnail_card_shared": " ¡ Condiviso", + "album_thumbnail_shared_by": "Condiviso da {user}", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", "album_user_left": "{album} abbandonato", "album_user_removed": "Utente {user} rimosso", "album_viewer_appbar_delete_confirm": "Sei sicuro di voler rimuovere questo album dal tuo account?", - "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album ", - "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album ", - "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album ", - "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album ", + "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album", + "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album", "album_viewer_appbar_share_leave": "Lascia album", "album_viewer_appbar_share_to": "Condividi a", "album_viewer_page_share_add_users": "Aggiungi utenti", @@ -440,7 +441,7 @@ "archive": "Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", "archive_page_no_archived_assets": "Nessuna oggetto archiviato", - "archive_page_title": "Archivia ({})", + "archive_page_title": "Archivio ({count})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", "archived": "Archiviati", @@ -460,7 +461,6 @@ "asset_list_layout_settings_group_automatically": "Automatico", "asset_list_layout_settings_group_by": "Raggruppa le risorse per", "asset_list_layout_settings_group_by_month_day": "Mese + giorno", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Impostazion del layout della griglia delle foto", "asset_list_settings_title": "Griglia foto", "asset_offline": "Risorsa Offline", @@ -477,18 +477,18 @@ "assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album", "assets_added_to_name_count": "Aggiunti {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, other {# asset}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} elementi cancellati definitivamente", + "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", "assets_permanently_deleted_count": "{count, plural, one {# asset cancellato} other {# asset cancellati}} definitivamente", "assets_removed_count": "{count, plural, one {# asset rimosso} other {# asset rimossi}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{count} elementi cancellati definitivamente dal tuo dispositivo", "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli asset cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", "assets_restored_count": "{count, plural, one {# asset ripristinato} other {# asset ripristinati}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{count} elementi ripristinati", + "assets_trashed": "{count} elementi cestinati", "assets_trashed_count": "{count, plural, one {Spostato # asset} other {Spostati # assets}} nel cestino", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{count} elementi cestinati dal server Immich", "assets_were_part_of_album_count": "{count, plural, one {L'asset era} other {Gli asset erano}} già parte dell'album", "authorized_devices": "Dispositivi autorizzati", "automatic_endpoint_switching_subtitle": "Connetti localmente quando la rete Wi-Fi specificata è disponibile e usa le connessioni alternative negli altri casi", @@ -497,8 +497,8 @@ "back_close_deselect": "Indietro, chiudi o deseleziona", "background_location_permission": "Permesso di localizzazione in background", "background_location_permission_content": "Per fare in modo che sia possibile cambiare rete quando è in esecuzione in background, Immich deve *sempre* avere accesso alla tua posizione precisa in modo da poter leggere il nome della rete Wi-Fi", - "backup_album_selection_page_albums_device": "Album sul dispositivo ({})", - "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere.", + "backup_album_selection_page_albums_device": "Album sul dispositivo ({count})", + "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere", "backup_album_selection_page_assets_scatter": "Visto che le risorse possono trovarsi in piÚ album, questi possono essere inclusi o esclusi dal backup.", "backup_album_selection_page_select_albums": "Seleziona gli album", "backup_album_selection_page_selection_info": "Informazioni sulla selezione", @@ -506,55 +506,52 @@ "backup_all": "Tutti", "backup_background_service_backup_failed_message": "Impossibile caricare i contenuti. Riprovoâ€Ļ", "backup_background_service_connection_failed_message": "Impossibile connettersi al server. Riprovoâ€Ļ", - "backup_background_service_current_upload_notification": "Caricamento {}", + "backup_background_service_current_upload_notification": "Caricamento di {filename} in corso", "backup_background_service_default_notification": "Ricerca di nuovi contenutiâ€Ļ", "backup_background_service_error_title": "Errore di backup", "backup_background_service_in_progress_notification": "Backup dei tuoi contenutiâ€Ļ", - "backup_background_service_upload_failure_notification": "Impossibile caricare {}", + "backup_background_service_upload_failure_notification": "Impossibile caricare {filename}", "backup_controller_page_albums": "Backup Album", "backup_controller_page_background_app_refresh_disabled_content": "Attiva l'aggiornamento dell'app in background in Impostazioni > Generale > Aggiorna app in background per utilizzare backup in background.", "backup_controller_page_background_app_refresh_disabled_title": "Backup in background è disattivo", "backup_controller_page_background_app_refresh_enable_button_text": "Vai alle impostazioni", "backup_controller_page_background_battery_info_link": "Mostrami come", "backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Ottimizzazioni batteria", "backup_controller_page_background_charging": "Solo durante la ricarica", "backup_controller_page_background_configure_error": "Impossibile configurare i servizi in background", - "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {}", + "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {duration}", "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di tutti i nuovi contenuti senza la necessità di aprire l'app", "backup_controller_page_background_is_off": "Backup automatico disattivato", "backup_controller_page_background_is_on": "Backup automatico attivo", "backup_controller_page_background_turn_off": "Disabilita servizi in background", "backup_controller_page_background_turn_on": "Abilita servizi in background", - "backup_controller_page_background_wifi": "Solo su WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selezionati:", + "backup_controller_page_background_wifi": "Solo Wi-Fi", + "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", - "backup_controller_page_created": "Creato il: {}", + "backup_controller_page_created": "Creato il: {date}", "backup_controller_page_desc_backup": "Attiva il backup per eseguire il caricamento automatico sul server all'apertura dell'applicazione.", - "backup_controller_page_excluded": "Esclusi:", - "backup_controller_page_failed": "Falliti: ({})", - "backup_controller_page_filename": "Nome file: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Esclusi: ", + "backup_controller_page_failed": "Falliti: ({count})", + "backup_controller_page_filename": "Nome file: {filename} [{size}]", "backup_controller_page_info": "Informazioni sul backup", "backup_controller_page_none_selected": "Nessuna selezione", "backup_controller_page_remainder": "Rimanenti", "backup_controller_page_remainder_sub": "Foto e video che devono essere ancora caricati", "backup_controller_page_server_storage": "Spazio sul server", "backup_controller_page_start_backup": "Avvia backup", - "backup_controller_page_status_off": "Backup è disattivato ", + "backup_controller_page_status_off": "Backup è disattivato", "backup_controller_page_status_on": "Backup è attivato", - "backup_controller_page_storage_format": "{} di {} usati", + "backup_controller_page_storage_format": "{used} di {total} usati", "backup_controller_page_to_backup": "Album da caricare", - "backup_controller_page_total_sub": "Tutte le foto e i video unici caricati dagli album selezionati ", + "backup_controller_page_total_sub": "Tutte le foto e i video unici caricati dagli album selezionati", "backup_controller_page_turn_off": "Disattiva backup", - "backup_controller_page_turn_on": "Attiva backup ", + "backup_controller_page_turn_on": "Attiva backup", "backup_controller_page_uploading_file_info": "Caricamento informazioni file", "backup_err_only_album": "Non è possibile rimuovere l'unico album", "backup_info_card_assets": "risorse", "backup_manual_cancelled": "Annullato", - "backup_manual_in_progress": "Caricamento già in corso. Riprova piÚ tardi.", + "backup_manual_in_progress": "Caricamento già in corso. Riprova piÚ tardi", "backup_manual_success": "Successo", "backup_manual_title": "Stato del caricamento", "backup_options_page_title": "Opzioni di Backup", @@ -570,21 +567,21 @@ "bulk_keep_duplicates_confirmation": "Sei sicuro di voler tenere {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione risolverà tutti i gruppi duplicati senza cancellare nulla.", "bulk_trash_duplicates_confirmation": "Sei davvero sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset piÚ pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati.", "buy": "Acquista Immich", - "cache_settings_album_thumbnails": "Anteprime pagine librerie ({} risorse)", + "cache_settings_album_thumbnails": "Anteprime pagine librerie ({count} elementi)", "cache_settings_clear_cache_button": "Pulisci cache", "cache_settings_clear_cache_button_title": "Pulisce la cache dell'app. Questo impatterà significativamente le prestazioni dell''app fino a quando la cache non sarà rigenerata.", "cache_settings_duplicated_assets_clear_button": "PULISCI", "cache_settings_duplicated_assets_subtitle": "Foto e video che sono nella black list dell'applicazione", - "cache_settings_duplicated_assets_title": "Elementi duplicati ({})", - "cache_settings_image_cache_size": "Dimensione cache delle immagini ({} risorse)", + "cache_settings_duplicated_assets_title": "Elementi duplicati ({count})", + "cache_settings_image_cache_size": "Dimensione cache delle immagini ({count} elementi)", "cache_settings_statistics_album": "Anteprime librerie", - "cache_settings_statistics_assets": "{} risorse ({})", + "cache_settings_statistics_assets": "{count} elementi ({size})", "cache_settings_statistics_full": "Immagini complete", "cache_settings_statistics_shared": "Anteprime album condivisi", "cache_settings_statistics_thumbnail": "Anteprime", "cache_settings_statistics_title": "Uso della cache", "cache_settings_subtitle": "Controlla il comportamento della cache dell'applicazione mobile immich", - "cache_settings_thumbnail_size": "Dimensione cache dei thumbnail ({} assets)", + "cache_settings_thumbnail_size": "Dimensione cache anteprime ({count} elementi)", "cache_settings_tile_subtitle": "Controlla il comportamento dello storage locale", "cache_settings_tile_title": "Archiviazione locale", "cache_settings_title": "Impostazioni della Cache", @@ -593,7 +590,7 @@ "camera_model": "Modello fotocamera", "cancel": "Annulla", "cancel_search": "Annulla ricerca", - "canceled": "Canceled", + "canceled": "Annullato", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -606,14 +603,15 @@ "change_password": "Modifica Password", "change_password_description": "È stato richiesto di cambiare la password (oppure è la prima volta che accedi). Inserisci la tua nuova password qui sotto.", "change_password_form_confirm_password": "Conferma Password", - "change_password_form_description": "Ciao {name},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto", + "change_password_form_description": "Ciao {name},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto.", "change_password_form_new_password": "Nuova Password", "change_password_form_password_mismatch": "Le password non coincidono", - "change_password_form_reenter_new_password": "Inserisci ancora la nuova password ", + "change_password_form_reenter_new_password": "Inserisci ancora la nuova password", + "change_pin_code": "Cambia il codice PIN", "change_your_password": "Modifica la tua password", "changed_visibility_successfully": "Visibilità modificata con successo", "check_all": "Controlla Tutti", - "check_corrupt_asset_backup": "Verifica la presenza di backup di asset corrotti ", + "check_corrupt_asset_backup": "Verifica la presenza di backup di asset corrotti", "check_corrupt_asset_backup_button": "Effettua controllo", "check_corrupt_asset_backup_description": "Effettua questo controllo solo sotto rete Wi-Fi e quando tutti gli asset sono stati sottoposti a backup. La procedura potrebbe impiegare qualche minuto.", "check_logs": "Controlla i log", @@ -624,14 +622,11 @@ "clear_all_recent_searches": "Rimuovi tutte le ricerche recenti", "clear_message": "Pulisci messaggio", "clear_value": "Pulisci valore", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "Certificato client importato", + "client_cert_invalid_msg": "File certificato invalido o password errata", + "client_cert_remove_msg": "Certificato client rimosso", + "client_cert_subtitle": "Supporta solo il formato PKCS12 (.p12, .pfx). L'importazione/rimozione del certificato è disponibile solo prima del login", + "client_cert_title": "Certificato Client SSL", "clockwise": "Senso orario", "close": "Chiudi", "collapse": "Restringi", @@ -643,24 +638,25 @@ "comments_and_likes": "Commenti & mi piace", "comments_are_disabled": "I commenti sono disabilitati", "common_create_new_album": "Crea nuovo Album", - "common_server_error": "Si prega di controllare la connessione network, che il server sia raggiungibile e che le versione del server e app sono gli stessi", - "completed": "Completed", + "common_server_error": "Si prega di controllare la connessione network, che il server sia raggiungibile e che le versione del server e app sono gli stessi.", + "completed": "Completato", "confirm": "Conferma", "confirm_admin_password": "Conferma password dell'amministratore", "confirm_delete_face": "Sei sicuro di voler cancellare il volto di {name} dall'asset?", "confirm_delete_shared_link": "Sei sicuro di voler eliminare questo link condiviso?", "confirm_keep_this_delete_others": "Tutti gli altri asset nello stack saranno eliminati, eccetto questo asset. Sei sicuro di voler continuare?", + "confirm_new_pin_code": "Conferma il nuovo codice PIN", "confirm_password": "Conferma password", "contain": "Adatta alla finestra", "context": "Contesto", "continue": "Continua", - "control_bottom_app_bar_album_info_shared": "{} elementi ¡ Condivisi", + "control_bottom_app_bar_album_info_shared": "{count} elementi ¡ Condivisi", "control_bottom_app_bar_create_new_album": "Crea nuovo album", "control_bottom_app_bar_delete_from_immich": "Elimina da Immich", "control_bottom_app_bar_delete_from_local": "Elimina dal dispositivo", "control_bottom_app_bar_edit_location": "Modifica posizione", "control_bottom_app_bar_edit_time": "Modifica data e ora", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Condividi Link", "control_bottom_app_bar_share_to": "Condividi a", "control_bottom_app_bar_trash_from_immich": "Sposta nel cestino", "copied_image_to_clipboard": "Immagine copiata negli appunti.", @@ -692,9 +688,11 @@ "create_tag_description": "Crea un nuovo tag. Per i tag annidati, si prega di inserire il percorso completo del tag tra cui barre oblique.", "create_user": "Crea utente", "created": "Creato", + "created_at": "Creato il", "crop": "Ritaglia", "curated_object_page_title": "Oggetti", "current_device": "Dispositivo attuale", + "current_pin_code": "Attuale codice PIN", "current_server_address": "Indirizzo del server in uso", "custom_locale": "Localizzazione personalizzata", "custom_locale_description": "Formatta data e numeri in base alla lingua e al paese", @@ -746,7 +744,6 @@ "direction": "Direzione", "disabled": "Disabilitato", "disallow_edits": "Blocca modifiche", - "discord": "Discord", "discover": "Scopri", "dismiss_all_errors": "Ignora tutti gli errori", "dismiss_error": "Ignora errore", @@ -763,7 +760,6 @@ "download_enqueue": "Download in coda", "download_error": "Errore durante il download", "download_failed": "Download fallito", - "download_filename": "file: {}", "download_finished": "Download terminato", "download_include_embedded_motion_videos": "Video incorporati", "download_include_embedded_motion_videos_description": "Includere i video incorporati nelle foto in movimento come file separato", @@ -772,12 +768,12 @@ "download_settings": "Scarica", "download_settings_description": "Gestisci le impostazioni relative al download delle risorse", "download_started": "Download avviato", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", + "download_sucess": "Download completato", + "download_sucess_android": "I contenuti multimediali sono stati scaricati in DCIM/Immich", "download_waiting_to_retry": "In attesa di riprovare", "downloading": "Scaricando", "downloading_asset_filename": "Scaricando la risorsa {filename}", - "downloading_media": "Downloading media", + "downloading_media": "Scaricamento file multimediali", "drop_files_to_upload": "Rilascia i file ovunque per caricarli", "duplicates": "Duplicati", "duplicates_description": "Risolvi ciascun gruppo indicando quali sono, se esistono, i duplicati", @@ -801,25 +797,24 @@ "edit_title": "Modifica Titolo", "edit_user": "Modifica utente", "edited": "Modificato", - "editor": "Editor", "editor_close_without_save_prompt": "Le modifiche non verranno salvate", "editor_close_without_save_title": "Vuoi chiudere l'editor?", "editor_crop_tool_h2_aspect_ratios": "Proporzioni", "editor_crop_tool_h2_rotation": "Rotazione", - "email": "Email", - "empty_folder": "This folder is empty", + "email_notifications": "Notifiche email", + "empty_folder": "La cartella è vuota", "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", "enabled": "Abilitato", "end_date": "Data Fine", - "enqueued": "Enqueued", + "enqueued": "Accodato", "enter_wifi_name": "Inserisci il nome della rete Wi-Fi", "error": "Errore", "error_change_sort_album": "Errore nel cambiare l'ordine di degli album", "error_delete_face": "Errore nel cancellare la faccia dalla foto", "error_loading_image": "Errore nel caricamento dell'immagine", - "error_saving_image": "Error: {}", + "error_saving_image": "Errore: {error}", "error_title": "Errore - Qualcosa è andato storto", "errors": { "cannot_navigate_next_asset": "Impossibile passare alla risorsa successiva", @@ -849,10 +844,12 @@ "failed_to_keep_this_delete_others": "Impossibile conservare questa risorsa ed eliminare le altre risorse", "failed_to_load_asset": "Errore durante il caricamento della risorsa", "failed_to_load_assets": "Errore durante il caricamento delle risorse", + "failed_to_load_notifications": "Errore nel caricamento delle notifiche", "failed_to_load_people": "Caricamento delle persone non riuscito", "failed_to_remove_product_key": "Rimozione del codice del prodotto fallita", "failed_to_stack_assets": "Errore durante il raggruppamento degli assets", "failed_to_unstack_assets": "Errore durante la separazione degli assets", + "failed_to_update_notification_status": "Aggiornamento stato notifiche fallito", "import_path_already_exists": "Questo percorso di importazione già esiste.", "incorrect_email_or_password": "Email o password non corretta", "paths_validation_failed": "{paths, plural, one {# percorso} other {# percorsi}} hanno fallito la validazione", @@ -920,6 +917,7 @@ "unable_to_remove_reaction": "Impossibile rimuovere reazione", "unable_to_repair_items": "Impossibile riparare elementi", "unable_to_reset_password": "Impossibile reimpostare la password", + "unable_to_reset_pin_code": "Impossibile resettare il codice PIN", "unable_to_resolve_duplicate": "Impossibile risolvere duplicato", "unable_to_restore_assets": "Impossibile ripristinare gli asset", "unable_to_restore_trash": "Impossibile ripristinare cestino", @@ -947,16 +945,15 @@ "unable_to_update_user": "Impossibile aggiornare l'utente", "unable_to_upload_file": "Impossibile caricare il file" }, - "exif": "Exif", "exif_bottom_sheet_description": "Aggiungi una descrizione...", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", "exif_bottom_sheet_person_add_person": "Aggiungi nome", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Età {age}", + "exif_bottom_sheet_person_age_months": "Età {months} mesi", + "exif_bottom_sheet_person_age_year_months": "Età 1 anno e {months} mesi", + "exif_bottom_sheet_person_age_years": "Età {years}", "exit_slideshow": "Esci dalla presentazione", "expand_all": "Espandi tutto", "experimental_settings_new_asset_list_subtitle": "Lavori in corso", @@ -976,9 +973,9 @@ "external_network": "Rete esterna", "external_network_sheet_info": "Quando non si è connessi alla rete Wi-Fi preferita, l'app si collegherà al server tramite il primo degli indirizzi della lista che riuscirà a raggiungere, dall'alto verso il basso", "face_unassigned": "Non assegnata", - "failed": "Failed", + "failed": "Fallito", "failed_to_load_assets": "Impossibile caricare gli asset", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", @@ -992,10 +989,11 @@ "filetype": "Tipo file", "filter": "Filtro", "filter_people": "Filtra persone", + "filter_places": "Filtra luoghi", "find_them_fast": "Trovale velocemente con la ricerca", "fix_incorrect_match": "Correggi corrispondenza errata", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Cartella", + "folder_not_found": "Cartella non trovata", "folders": "Cartelle", "folders_feature_description": "Navigare la visualizzazione a cartelle per le foto e i video sul file system", "forward": "Avanti", @@ -1016,12 +1014,12 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Aggiungi Header", + "header_settings_field_validator_msg": "Il valore non puÃ˛ essere vuoto", + "header_settings_header_name_input": "Nome header", + "header_settings_header_value_input": "Valore header", + "headers_settings_tile_subtitle": "Definisci gli header per i proxy che l'app deve inviare con ogni richiesta di rete", + "headers_settings_tile_title": "Header proxy personalizzati", "hi_user": "Ciao {name} ({email})", "hide_all_people": "Nascondi tutte le persone", "hide_gallery": "Nascondi galleria", @@ -1031,7 +1029,7 @@ "hide_unnamed_people": "Nascondi persone senza nome", "home_page_add_to_album_conflicts": "Aggiunti {added} elementi all'album {album}. {failed} elementi erano già presenti nell'album.", "home_page_add_to_album_err_local": "Non puoi aggiungere in album risorse non ancora caricate, azione ignorata", - "home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}", + "home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}.", "home_page_album_err_partner": "Non puoi aggiungere risorse del partner a un album, azione ignorata", "home_page_archive_err_local": "Non puoi archiviare immagini non ancora caricate, azione ignorata", "home_page_archive_err_partner": "Non puoi archiviare risorse del partner, azione ignorata", @@ -1040,10 +1038,9 @@ "home_page_delete_remote_err_local": "Risorse locali presenti nella selezione della eliminazione remota, azione ignorata", "home_page_favorite_err_local": "Non puoi aggiungere tra i preferiti delle risorse non ancora caricate, azione ignorata", "home_page_favorite_err_partner": "Non puoi mettere le risorse del partner nei preferiti, azione ignorata", - "home_page_first_time_notice": "Se è la prima volta che utilizzi l'app, assicurati di scegliere uno o piÚ album di backup, in modo che la timeline possa popolare le foto e i video presenti negli album.", + "home_page_first_time_notice": "Se è la prima volta che utilizzi l'app, assicurati di scegliere uno o piÚ album di backup, in modo che la timeline possa popolare le foto e i video presenti negli album", "home_page_share_err_local": "Non puoi condividere una risorsa locale tramite link, azione ignorata", "home_page_upload_err_limit": "Puoi caricare al massimo 30 file per volta, ignora quelli in eccesso", - "host": "Host", "hour": "Ora", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", @@ -1059,7 +1056,7 @@ "image_alt_text_date_place_3_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2}, e {person3} il giorno {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video girato} other {Foto scattata}} a {city}, {country} con {person1}, {person2} e {additionalCount, number} altre persone il {date}", "image_saved_successfully": "Immagine salvata", - "image_viewer_page_state_provider_download_started": "Download Started", + "image_viewer_page_state_provider_download_started": "Download iniziato", "image_viewer_page_state_provider_download_success": "Download con successo", "image_viewer_page_state_provider_share_error": "Errore di condivisione", "immich_logo": "Logo Immich", @@ -1073,15 +1070,14 @@ "include_shared_partner_assets": "Includi asset condivisi del compagno", "individual_share": "Condivisione individuale", "individual_shares": "Condivisioni individuali", - "info": "Info", "interval": { "day_at_onepm": "Ogni giorno alle 13", "hours": "Ogni {hours, plural, one {ora} other {{hours, number} ore}}", "night_at_midnight": "Ogni notte a mezzanotte", "night_at_twoam": "Ogni notte alle 2" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "Data invalida", + "invalid_date_format": "Formato data invalido", "invite_people": "Invita Persone", "invite_to_album": "Invita nell'album", "items_count": "{count, plural, one {# elemento} other {# elementi}}", @@ -1120,7 +1116,7 @@ "local_network": "Rete locale", "local_network_sheet_info": "L'app si collegherà al server tramite questo URL quando è in uso la rete Wi-Fi specificata", "location_permission": "Permesso di localizzazione", - "location_permission_content": "Per usare la funzione di cambio automatico, Immich necessita del permesso di localizzazione precisa cosÃŦ da poter leggere il nome della rete Wi-Fi in uso", + "location_permission_content": "Per usare la funzione di cambio automatico, Immich necessita del permesso di localizzazione cosÃŦ da poter leggere il nome della rete Wi-Fi in uso", "location_picker_choose_on_map": "Scegli una mappa", "location_picker_latitude_error": "Inserisci una latitudine valida", "location_picker_latitude_hint": "Inserisci la tua latitudine qui", @@ -1130,26 +1126,24 @@ "log_out_all_devices": "Disconnetti tutti i dispositivi", "logged_out_all_devices": "Disconnesso da tutti i dispositivi", "logged_out_device": "Disconnesso dal dispositivo", - "login": "Login", "login_disabled": "L'accesso è stato disattivato", - "login_form_api_exception": "API error, per favore ricontrolli URL del server e riprovi", + "login_form_api_exception": "API error, per favore ricontrolli URL del server e riprovi.", "login_form_back_button_text": "Indietro", "login_form_email_hint": "tuaemail@email.com", "login_form_endpoint_hint": "http://ip-del-tuo-server:port", - "login_form_endpoint_url": "Server Endpoint URL", + "login_form_endpoint_url": "URL dell'Endpoint del Server", "login_form_err_http": "Per favore specificare http:// o https://", "login_form_err_invalid_email": "Email non valida", "login_form_err_invalid_url": "URL invalido", - "login_form_err_leading_whitespace": "Whitespace all'inizio ", + "login_form_err_leading_whitespace": "Whitespace all'inizio", "login_form_err_trailing_whitespace": "Whitespace alla fine", "login_form_failed_get_oauth_server_config": "Errore di login usando OAuth, controlla l'URL del server", "login_form_failed_get_oauth_server_disable": "OAuth non è disponibile su questo server", "login_form_failed_login": "Errore nel login, controlla URL del server e le credenziali (email e password)", "login_form_handshake_exception": "Si è verificata un'eccezione di handshake con il server. Abilita il supporto del certificato self-signed nelle impostazioni se si utilizza questo tipo di certificato.", - "login_form_password_hint": "password ", - "login_form_save_login": "Rimani connesso ", - "login_form_server_empty": "Inserisci URL del server", - "login_form_server_error": "Non è possibile connettersi al server", + "login_form_save_login": "Rimani connesso", + "login_form_server_empty": "Inserisci URL del server.", + "login_form_server_error": "Non è possibile connettersi al server.", "login_has_been_disabled": "Il login è stato disabilitato.", "login_password_changed_error": "C'è stato un errore durante l'aggiornamento della password", "login_password_changed_success": "Password aggiornata con successo", @@ -1170,8 +1164,8 @@ "manage_your_devices": "Gestisci i tuoi dispositivi collegati", "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} foto", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} foto", "map_cannot_get_user_location": "Non è possibile ottenere la posizione dell'utente", "map_location_dialog_yes": "Si", "map_location_picker_page_use_location": "Usa questa posizione", @@ -1185,15 +1179,18 @@ "map_settings": "Impostazioni Mappa", "map_settings_dark_mode": "Modalità scura", "map_settings_date_range_option_day": "Ultime 24 ore", - "map_settings_date_range_option_days": "Ultimi {} giorni", + "map_settings_date_range_option_days": "Ultimi {days} giorni", "map_settings_date_range_option_year": "Ultimo anno", - "map_settings_date_range_option_years": "Ultimi {} anni", + "map_settings_date_range_option_years": "Ultimi {years} anni", "map_settings_dialog_title": "Impostazioni Mappa", "map_settings_include_show_archived": "Includi Archiviati", "map_settings_include_show_partners": "Includi Partner", "map_settings_only_show_favorites": "Mostra solo preferiti", "map_settings_theme_settings": "Tema della mappa", "map_zoom_to_see_photos": "Riduci lo zoom per vedere le foto", + "mark_all_as_read": "Segna tutto come letto", + "mark_as_read": "Segna come letto", + "marked_all_as_read": "Segnato tutto come letto", "matches": "Corrispondenze", "media_type": "Tipo Media", "memories": "Ricordi", @@ -1202,11 +1199,8 @@ "memories_setting_description": "Gestisci cosa vedi nei tuoi ricordi", "memories_start_over": "Ricomincia", "memories_swipe_to_close": "Scorri sopra per chiudere", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Memoria", "memory_lane_title": "Sentiero dei Ricordi {title}", - "menu": "Menu", "merge": "Unisci", "merge_people": "Unisci persone", "merge_people_limit": "Puoi unire al massimo 5 volti alla volta", @@ -1218,8 +1212,9 @@ "missing": "Mancanti", "model": "Modello", "month": "Mese", - "monthly_title_text_date_format": "MMMM y", "more": "Di piÚ", + "moved_to_archive": "Spostati {count, plural, one {# asset} other {# assets}} nell'archivio", + "moved_to_library": "Spostati {count, plural, one {# asset} other {# assets}} nella libreria", "moved_to_trash": "Spostato nel cestino", "multiselect_grid_edit_date_time_err_read_only": "Non puoi modificare la data di risorse in sola lettura, azione ignorata", "multiselect_grid_edit_gps_err_read_only": "Non puoi modificare la posizione di risorse in sola lettura, azione ignorata", @@ -1234,12 +1229,12 @@ "new_api_key": "Nuova Chiave di API", "new_password": "Nuova password", "new_person": "Nuova persona", + "new_pin_code": "Nuovo codice PIN", "new_user_created": "Nuovo utente creato", "new_version_available": "NUOVA VERSIONE DISPONIBILE", "newest_first": "Prima recenti", "next": "Prossimo", "next_memory": "Prossima memoria", - "no": "No", "no_albums_message": "Crea un album per organizzare le tue foto ed i tuoi video", "no_albums_with_name_yet": "Sembra che tu non abbia ancora nessun album con questo nome.", "no_albums_yet": "Sembra che tu non abbia ancora nessun album.", @@ -1252,27 +1247,26 @@ "no_favorites_message": "Aggiungi preferiti per trovare facilmente le tue migliori foto e video", "no_libraries_message": "Crea una libreria esterna per vedere le tue foto e i tuoi video", "no_name": "Nessun nome", + "no_notifications": "Nessuna notifica", + "no_people_found": "Nessuna persona trovata", "no_places": "Nessun posto", "no_results": "Nessun risultato", "no_results_description": "Prova ad usare un sinonimo oppure una parola chiave piÚ generica", "no_shared_albums_message": "Crea un album per condividere foto e video con le persone nella tua rete", "not_in_any_album": "In nessun album", - "not_selected": "Not selected", + "not_selected": "Non selezionato", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Per aggiungere l'etichetta dell'archiviazione agli asset caricati in precedenza, esegui", "notes": "Note", - "notification_permission_dialog_content": "Per attivare le notifiche, vai alle Impostazioni e seleziona concedi", - "notification_permission_list_tile_content": "Concedi i permessi per attivare le notifiche", + "notification_permission_dialog_content": "Per attivare le notifiche, vai alle Impostazioni e seleziona concedi.", + "notification_permission_list_tile_content": "Concedi i permessi per attivare le notifiche.", "notification_permission_list_tile_enable_button": "Attiva notifiche", "notification_permission_list_tile_title": "Permessi delle Notifiche", "notification_toggle_setting_description": "Attiva le notifiche via email", "notifications": "Notifiche", "notifications_setting_description": "Gestisci notifiche", - "oauth": "OAuth", "official_immich_resources": "Risorse Ufficiali Immich", - "offline": "Offline", "offline_paths": "Percorsi offline", "offline_paths_description": "Questi risultati potrebbero essere causati dall'eliminazione manuale di file che non fanno parte di una libreria esterna.", - "ok": "Ok", "oldest_first": "Prima vecchi", "on_this_device": "Su questo dispositivo", "onboarding": "Inserimento", @@ -1280,8 +1274,8 @@ "onboarding_theme_description": "Scegli un tema colore per la tua istanza. Potrai cambiarlo nelle impostazioni.", "onboarding_welcome_description": "Andiamo ad impostare la tua istanza con alcune impostazioni comuni.", "onboarding_welcome_user": "Benvenuto, {user}", - "online": "Online", "only_favorites": "Solo preferiti", + "open": "Apri", "open_in_map_view": "Apri nella visualizzazione mappa", "open_in_openstreetmap": "Apri su OpenStreetMap", "open_the_search_filters": "Apri filtri di ricerca", @@ -1294,21 +1288,19 @@ "other_variables": "Altre variabili", "owned": "Posseduti", "owner": "Proprietario", - "partner": "Partner", "partner_can_access": "{partner} puÃ˛ accedere", "partner_can_access_assets": "Tutte le tue foto e i tuoi video eccetto quelli Archiviati e Cancellati", "partner_can_access_location": "La posizione dove è stata scattata la foto", "partner_list_user_photos": "Foto di {user}", "partner_list_view_all": "Mostra tutto", "partner_page_empty_message": "Le tue foto non sono ancora condivise con alcun partner.", - "partner_page_no_more_users": "Nessun altro utente da aggiungere.", - "partner_page_partner_add_failed": "Aggiunta del partner non riuscita.", - "partner_page_select_partner": "Seleziona partner.", + "partner_page_no_more_users": "Nessun altro utente da aggiungere", + "partner_page_partner_add_failed": "Aggiunta del partner non riuscita", + "partner_page_select_partner": "Seleziona partner", "partner_page_shared_to_title": "Condividi con", - "partner_page_stop_sharing_content": "{} non sarà piÚ in grado di accedere alle tue foto.", + "partner_page_stop_sharing_content": "{partner} non sarà piÚ in grado di accedere alle tue foto.", "partner_sharing": "Condivisione Compagno", "partners": "Compagni", - "password": "Password", "password_does_not_match": "Le password non coincidono", "password_required": "Password Richiesta", "password_reset_success": "Ripristino password avvenuto con successo", @@ -1338,10 +1330,10 @@ "permission_onboarding_continue_anyway": "Continua lo stesso", "permission_onboarding_get_started": "Inizia", "permission_onboarding_go_to_settings": "Vai a Impostazioni", - "permission_onboarding_permission_denied": "Permessi negati. Per usare Immich concedi i permessi ai video e foto dalle impostazioni", - "permission_onboarding_permission_granted": "Concessi i permessi! Ora sei tutto apposto", + "permission_onboarding_permission_denied": "Permessi negati. Per usare Immich concedi i permessi ai video e foto dalle impostazioni.", + "permission_onboarding_permission_granted": "Concessi i permessi! Ora sei tutto apposto.", "permission_onboarding_permission_limited": "Permessi limitati. Per consentire a Immich di gestire e fare i backup di tutta la galleria, concedi i permessi Foto e Video dalle Impostazioni.", - "permission_onboarding_request": "Immich richiede i permessi per vedere le tue foto e video", + "permission_onboarding_request": "Immich richiede i permessi per vedere le tue foto e video.", "person": "Persona", "person_birthdate": "Nato il {date}", "person_hidden": "{name}{hidden, select, true { (nascosto)} other {}}", @@ -1351,6 +1343,9 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Foto}}", "photos_from_previous_years": "Foto degli anni scorsi", "pick_a_location": "Scegli una posizione", + "pin_code_changed_successfully": "Codice PIN cambiato", + "pin_code_reset_successfully": "Codice PIN resettato con successo", + "pin_code_setup_successfully": "Codice PIN cambiato con successo", "place": "Posizione", "places": "Luoghi", "places_count": "{count, plural, one {{count, number} Luogo} other {{count, number} Places}}", @@ -1367,12 +1362,11 @@ "previous_memory": "Ricordo precedente", "previous_or_next_photo": "Precedente o prossima foto", "primary": "Primario", - "privacy": "Privacy", - "profile_drawer_app_logs": "Logs", + "profile": "Profilo", + "profile_drawer_app_logs": "Registri", "profile_drawer_client_out_of_date_major": "L'applicazione non è aggiornata. Per favore aggiorna all'ultima versione principale.", "profile_drawer_client_out_of_date_minor": "L'applicazione non è aggiornata. Per favore aggiorna all'ultima versione minore.", "profile_drawer_client_server_up_to_date": "Client e server sono aggiornati", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Il server non è aggiornato. Per favore aggiorna all'ultima versione principale.", "profile_drawer_server_out_of_date_minor": "Il server non è aggiornato. Per favore aggiorna all'ultima versione minore.", "profile_image_of_user": "Immagine profilo di {user}", @@ -1381,7 +1375,7 @@ "public_share": "Condivisione Pubblica", "purchase_account_info": "Contributore", "purchase_activated_subtitle": "Grazie per supportare Immich e i software open source", - "purchase_activated_time": "Attivato il {date, date}", + "purchase_activated_time": "Attivato il {date}", "purchase_activated_title": "La tua chiave è stata attivata con successo", "purchase_button_activate": "Attiva", "purchase_button_buy": "Acquista", @@ -1401,7 +1395,6 @@ "purchase_panel_info_1": "Costruire Immich richiede molto tempo e impegno, e abbiamo ingegneri a tempo pieno che lavorano per renderlo il migliore possibile. La nostra missione è fare in modo che i software open source e le pratiche aziendali etiche diventino una fonte di reddito sostenibile per gli sviluppatori e creare un ecosistema che rispetti la privacy, offrendo vere alternative ai servizi cloud sfruttatori.", "purchase_panel_info_2": "PoichÊ siamo impegnati a non aggiungere barriere di pagamento, questo acquisto non ti offrirà funzionalità aggiuntive in Immich. Contiamo su utenti come te per sostenere lo sviluppo continuo di Immich.", "purchase_panel_title": "Contribuisci al progetto", - "purchase_per_server": "Per server", "purchase_per_user": "Per utente", "purchase_remove_product_key": "Rimuovi la Chiave del Prodotto", "purchase_remove_product_key_prompt": "Sei sicuro di voler rimuovere la chiave del prodotto?", @@ -1409,7 +1402,6 @@ "purchase_remove_server_product_key_prompt": "Sei sicuro di voler rimuovere la chiave del prodotto per Server?", "purchase_server_description_1": "Per l'intero server", "purchase_server_description_2": "Stato di Contributore", - "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", @@ -1426,6 +1418,8 @@ "recent_searches": "Ricerche recenti", "recently_added": "Aggiunti recentemente", "recently_added_page_title": "Aggiunti di recente", + "recently_taken": "Scattate di recente", + "recently_taken_page_title": "Scattate di Recente", "refresh": "Aggiorna", "refresh_encoded_videos": "Ricarica video codificati", "refresh_faces": "Aggiorna facce", @@ -1461,13 +1455,13 @@ "repair": "Ripara", "repair_no_results_message": "I file mancanti e non tracciati saranno mostrati qui", "replace_with_upload": "Rimpiazza con upload", - "repository": "Repository", "require_password": "Richiedi password", "require_user_to_change_password_on_first_login": "Richiedi all'utente di cambiare password al primo accesso", "rescan": "Scansiona nuovamente", "reset": "Ripristina", "reset_password": "Ripristina password", "reset_people_visibility": "Ripristina visibilità persone", + "reset_pin_code": "Resetta il codice PIN", "reset_to_default": "Ripristina i valori predefiniti", "resolve_duplicates": "Risolvi duplicati", "resolved_all_duplicates": "Tutti i duplicati sono stati risolti", @@ -1479,7 +1473,6 @@ "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", "role": "Ruolo", - "role_editor": "Editor", "role_viewer": "Visualizzatore", "save": "Salva", "save_to_gallery": "Salva in galleria", @@ -1487,7 +1480,7 @@ "saved_profile": "Profilo salvato", "saved_settings": "Impostazioni salvate", "say_something": "Dici qualcosa", - "scaffold_body_error_occurred": "Si è verificato un errore.", + "scaffold_body_error_occurred": "Si è verificato un errore", "scan_all_libraries": "Analizza tutte le librerie", "scan_library": "Scansione", "scan_settings": "Impostazioni Analisi", @@ -1504,24 +1497,21 @@ "search_city": "Cerca città...", "search_country": "Cerca paese...", "search_filter_apply": "Applica filtro", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", + "search_filter_camera_title": "Seleziona il tipo di camera", + "search_filter_date_title": "Scegli un range di date", "search_filter_display_option_not_in_album": "Non nell'album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", + "search_filter_display_options": "Opzioni di Visualizzazione", + "search_filter_filename": "Cerca per nome file", + "search_filter_location": "Posizione", + "search_filter_location_title": "Seleziona posizione", "search_filter_media_type_title": "Seleziona il tipo di media", "search_filter_people_title": "Seleziona persone", "search_for": "Cerca per", "search_for_existing_person": "Cerca per persona esistente", - "search_no_more_result": "No more results", + "search_no_more_result": "Non ci sono altri risultati", "search_no_people": "Nessuna persona", "search_no_people_named": "Nessuna persona chiamate \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "Nessun risultato trovato, prova con un termine o combinazione diversi", "search_options": "Opzioni Ricerca", "search_page_categories": "Categoria", "search_page_motion_photos": "Foto in movimento", @@ -1532,16 +1522,16 @@ "search_page_selfies": "Selfie", "search_page_things": "Oggetti", "search_page_view_all_button": "Guarda tutto", - "search_page_your_activity": "Tua attività ", + "search_page_your_activity": "Le tua attività", "search_page_your_map": "La tua mappa", "search_people": "Cerca persone", "search_places": "Cerca luoghi", "search_rating": "Cerca per valutazione...", - "search_result_page_new_search_hint": "Nuova ricerca ", + "search_result_page_new_search_hint": "Nuova ricerca", "search_settings": "Cerca Impostazioni", "search_state": "Cerca stato...", - "search_suggestion_list_smart_search_hint_1": "\nRicerca Smart è attiva di default, per usare la ricerca con i metadata usare la seguente sintassi", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", + "search_suggestion_list_smart_search_hint_1": "Ricerca Smart è attiva di default, per usare la ricerca con i metadata usare la seguente sintassi ", + "search_suggestion_list_smart_search_hint_2": "m:termine-di-ricerca", "search_tags": "Cerca tag...", "search_timezone": "Cerca fuso orario...", "search_type": "Cerca tipo", @@ -1560,18 +1550,16 @@ "select_keep_all": "Seleziona mantieni tutto", "select_library_owner": "Seleziona proprietario libreria", "select_new_face": "Seleziona nuovo volto", + "select_person_to_tag": "Seleziona una persona da taggare", "select_photos": "Seleziona foto", "select_trash_all": "Seleziona cestina tutto", - "select_user_for_sharing_page_err_album": "Impossibile nel creare l'album ", + "select_user_for_sharing_page_err_album": "Impossibile nel creare l'album", "selected": "Selezionato", "selected_count": "{count, plural, one {# selezionato} other {# selezionati}}", "send_message": "Manda messaggio", "send_welcome_email": "Invia email di benvenuto", "server_endpoint": "Server endpoint", "server_info_box_app_version": "Versione App", - "server_info_box_server_url": "Server URL", - "server_offline": "Server Offline", - "server_online": "Server Online", "server_stats": "Statistiche Server", "server_version": "Versione Server", "set": "Imposta", @@ -1584,32 +1572,32 @@ "setting_image_viewer_help": "Il visualizzatore dettagliato carica una piccola thumbnail per prima, per poi caricare un immagine di media grandezza (se abilitato). Ed infine carica l'originale (se abilitato).", "setting_image_viewer_original_subtitle": "Abilita per caricare l'immagine originale a risoluzione massima (grande!). Disabilita per ridurre l'utilizzo di banda (sia sul network che nella cache del dispositivo).", "setting_image_viewer_original_title": "Carica l'immagine originale", - "setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media.\nDisabilita per caricare direttamente l'immagine originale o usare la thumbnail.", + "setting_image_viewer_preview_subtitle": "Abilita per caricare un'immagine a risoluzione media. Disabilita per caricare direttamente l'immagine originale o usare la thumbnail.", "setting_image_viewer_preview_title": "Carica immagine di anteprima", - "setting_image_viewer_title": "Images", + "setting_image_viewer_title": "Immagini", "setting_languages_apply": "Applica", "setting_languages_subtitle": "Cambia la lingua dell'app", "setting_languages_title": "Lingue", - "setting_notifications_notify_failures_grace_period": "Notifica caricamenti falliti in background: {}", - "setting_notifications_notify_hours": "{} ore", + "setting_notifications_notify_failures_grace_period": "Notifica caricamenti falliti in background: {duration}", + "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "immediatamente", - "setting_notifications_notify_minutes": "{} minuti", + "setting_notifications_notify_minutes": "{count} minuti", "setting_notifications_notify_never": "mai", - "setting_notifications_notify_seconds": "{} secondi", + "setting_notifications_notify_seconds": "{count} secondi", "setting_notifications_single_progress_subtitle": "Informazioni dettagliate sul caricamento della risorsa", "setting_notifications_single_progress_title": "Mostra avanzamento dettagliato del backup in background", "setting_notifications_subtitle": "Cambia le impostazioni di notifica", "setting_notifications_total_progress_subtitle": "Progresso generale del caricamento (caricati / totali)", "setting_notifications_total_progress_title": "Mostra avanzamento del backup in background", - "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "Quando riproduci un video dal server, riproduci l'originale anche se è disponibile una versione transcodificata. Questo potrebbe portare a buffering. I video disponibili localmente sono sempre riprodotti a qualità originale indipendentemente da questa impostazione.", "setting_video_viewer_original_video_title": "Forza video originale", "settings": "Impostazioni", "settings_require_restart": "Si prega di riavviare Immich perchÊ vengano applicate le impostazioni", "settings_saved": "Impostazioni salvate", + "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", "share_add_photos": "Aggiungi foto", - "share_assets_selected": "{} selected", + "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparoâ€Ļ", "shared": "Condivisi", "shared_album_activities_input_disable": "I commenti sono disabilitati", @@ -1623,34 +1611,32 @@ "shared_by_user": "Condiviso da {user}", "shared_by_you": "Condiviso da te", "shared_from_partner": "Foto da {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} Caricati", "shared_link_app_bar_title": "Link condivisi", "shared_link_clipboard_copied_massage": "Copiato negli appunti", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", "shared_link_create_error": "Si è verificato un errore durante la creazione del link condiviso", "shared_link_edit_description_hint": "Inserisci la descrizione della condivisione", "shared_link_edit_expire_after_option_day": "1 giorno", - "shared_link_edit_expire_after_option_days": "{} giorni", + "shared_link_edit_expire_after_option_days": "{count} giorni", "shared_link_edit_expire_after_option_hour": "1 ora", - "shared_link_edit_expire_after_option_hours": "{} ore", + "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minuto", - "shared_link_edit_expire_after_option_minutes": "{} minuti", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", + "shared_link_edit_expire_after_option_minutes": "{count} minuti", + "shared_link_edit_expire_after_option_months": "{count} mesi", + "shared_link_edit_expire_after_option_year": "{count} anno", "shared_link_edit_password_hint": "Inserire la password di condivisione", "shared_link_edit_submit_button": "Aggiorna link", "shared_link_error_server_url_fetch": "Non è possibile trovare l'indirizzo del server", - "shared_link_expires_day": "Scade tra {} giorno", - "shared_link_expires_days": "Scade tra {} giorni", - "shared_link_expires_hour": "Scade tra {} ora", - "shared_link_expires_hours": "Scade tra {} ore", - "shared_link_expires_minute": "Scade tra {} minuto", - "shared_link_expires_minutes": "Scade tra {} minuti", + "shared_link_expires_day": "Scade tra {count} giorno", + "shared_link_expires_days": "Scade tra {count} giorni", + "shared_link_expires_hour": "Scade tra {count} ora", + "shared_link_expires_hours": "Scade tra {count} ore", + "shared_link_expires_minute": "Scade tra {count} minuto", + "shared_link_expires_minutes": "Scade tra {count} minuti", "shared_link_expires_never": "Scadenza ∞", - "shared_link_expires_second": "Scade tra {} secondo", - "shared_link_expires_seconds": "Scade tra {} secondi", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", + "shared_link_expires_second": "Scade tra {count} secondo", + "shared_link_expires_seconds": "Scade tra {count} secondi", + "shared_link_individual_shared": "Condiviso individualmente", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", "shared_links": "Link condivisi", @@ -1723,6 +1709,7 @@ "stop_sharing_photos_with_user": "Interrompi la condivisione delle tue foto con questo utente", "storage": "Spazio di archiviazione", "storage_label": "Etichetta archiviazione", + "storage_quota": "Limite Archiviazione", "storage_usage": "{used} di {available} utilizzati", "submit": "Invia", "suggestions": "Suggerimenti", @@ -1732,10 +1719,9 @@ "support_third_party_description": "La tua installazione di Immich è stata costruita da terze parti. I problemi che riscontri potrebbero essere causati da altri pacchetti, quindi ti preghiamo di sollevare il problema in prima istanza utilizzando i link sottostanti.", "swap_merge_direction": "Scambia direzione di unione", "sync": "Sincronizza", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "tag": "Tag", + "sync_albums": "Sincronizza album", + "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag_assets": "Tagga risorse", "tag_created": "Tag creata: {tag}", "tag_feature_description": "Navigazione foto e video raggruppati per argomenti tag logici", @@ -1749,12 +1735,12 @@ "theme_selection": "Selezione tema", "theme_selection_description": "Imposta automaticamente il tema chiaro o scuro in base all'impostazione del tuo browser", "theme_setting_asset_list_storage_indicator_title": "Mostra indicatore dello storage nei titoli dei contenuti", - "theme_setting_asset_list_tiles_per_row_title": "Numero di contenuti per riga ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Numero di elementi per riga ({count})", + "theme_setting_colorful_interface_subtitle": "Applica il colore primario alle superfici di sfondo.", + "theme_setting_colorful_interface_title": "Interfaccia colorata", "theme_setting_image_viewer_quality_subtitle": "Cambia la qualità del dettaglio dell'immagine", "theme_setting_image_viewer_quality_title": "Qualità immagine", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", + "theme_setting_primary_color_subtitle": "Scegli un colore per le azioni primarie e accentate.", "theme_setting_primary_color_title": "Colore primario", "theme_setting_system_primary_color_title": "Usa colori di sistema", "theme_setting_system_theme_switch": "Automatico (Segue le impostazioni di sistema)", @@ -1769,7 +1755,6 @@ "to_archive": "Archivio", "to_change_password": "Modifica password", "to_favorite": "Preferito", - "to_login": "Login", "to_parent": "Sali di un livello", "to_trash": "Cancella", "toggle_settings": "Attiva/disattiva impostazioni", @@ -1780,17 +1765,19 @@ "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", - "trash_emptied": "Emptied trash", + "trash_emptied": "Cestino svuotato", "trash_no_results_message": "Le foto cestinate saranno mostrate qui.", "trash_page_delete_all": "Elimina tutti", "trash_page_empty_trash_dialog_content": "Vuoi eliminare gli elementi nel cestino? Questi elementi saranno eliminati definitivamente da Immich", - "trash_page_info": "Gli elementi cestinati saranno eliminati definitivamente dopo {} giorni", + "trash_page_info": "Gli elementi cestinati saranno eliminati definitivamente dopo {days} giorni", "trash_page_no_assets": "Nessun elemento cestinato", "trash_page_restore_all": "Ripristina tutto", "trash_page_select_assets_btn": "Seleziona elemento", - "trash_page_title": "Cestino ({})", + "trash_page_title": "Cestino ({count})", "trashed_items_will_be_permanently_deleted_after": "Gli elementi cestinati saranno eliminati definitivamente dopo {days, plural, one {# giorno} other {# giorni}}.", "type": "Tipo", + "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", + "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", "unarchived_count": "{count, plural, other {Non archiviati #}}", "unfavorite": "Rimuovi preferito", @@ -1814,6 +1801,7 @@ "untracked_files": "File non tracciati", "untracked_files_decription": "Questi file non vengono tracciati dall'applicazione. Sono il risultato di spostamenti falliti, caricamenti interrotti, oppure sono stati abbandonati a causa di un bug", "up_next": "Prossimo", + "updated_at": "Aggiornato il", "updated_password": "Password aggiornata", "upload": "Carica", "upload_concurrency": "Caricamenti contemporanei", @@ -1826,15 +1814,17 @@ "upload_status_errors": "Errori", "upload_status_uploaded": "Caricato", "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "Carica su Immich ({count})", + "uploading": "Caricamento", "usage": "Utilizzo", "use_current_connection": "usa la connessione attuale", "use_custom_date_range": "Altrimenti utilizza un intervallo date personalizzato", "user": "Utente", + "user_has_been_deleted": "L'utente è stato rimosso.", "user_id": "ID utente", "user_liked": "A {user} piace {type, select, photo {questa foto} video {questo video} asset {questo asset} other {questo elemento}}", + "user_pin_code_settings": "Codice PIN", + "user_pin_code_settings_description": "Gestisci il tuo codice PIN", "user_purchase_settings": "Acquisto", "user_purchase_settings_description": "Gestisci il tuo acquisto", "user_role_set": "Imposta {user} come {role}", @@ -1853,11 +1843,10 @@ "version_announcement_overlay_release_notes": "note di rilascio", "version_announcement_overlay_text_1": "Ciao, c'è una nuova versione di", "version_announcement_overlay_text_2": "per favore prenditi il tuo tempo per visitare le ", - "version_announcement_overlay_text_3": " e verifica che il tuo docker-compose e il file .env siano aggiornati per impedire qualsiasi errore di configurazione, specialmente se utilizzate WatchTower o altri strumenti per l'aggiornamento automatico dell'applicativo", + "version_announcement_overlay_text_3": " e verifica che il tuo docker-compose e il file .env siano aggiornati per impedire qualsiasi errore di configurazione, specialmente se utilizzate WatchTower o altri strumenti per l'aggiornamento automatico dell'applicativo.", "version_announcement_overlay_title": "Nuova versione del server disponibile 🎉", "version_history": "Storico delle Versioni", "version_history_item": "Versione installata {version} il {date}", - "video": "Video", "video_hover_setting": "Riproduci l'anteprima del video al passaggio del mouse", "video_hover_setting_description": "Riproduci miniatura video quando il mouse passa sopra l'elemento. Anche se disabilitato, la riproduzione puÃ˛ essere avviata passando con il mouse sopra l'icona riproduci.", "videos": "Video", @@ -1883,7 +1872,7 @@ "week": "Settimana", "welcome": "Benvenuto", "welcome_to_immich": "Benvenuto in Immich", - "wifi_name": "Nome della rete Wi-Fi", + "wifi_name": "Nome rete Wi-Fi", "year": "Anno", "years_ago": "{years, plural, one {# anno} other {# anni}} fa", "yes": "Si", diff --git a/i18n/ja.json b/i18n/ja.json index 39496cc7f5..0bccd9f867 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -1,5 +1,5 @@ { - "about": "ã‚ĸプãƒĒãĢついãĻ", + "about": "Immich ãĢついãĻ", "account": "ã‚ĸã‚Ģã‚Ļãƒŗãƒˆ", "account_settings": "ã‚ĸã‚Ģã‚Ļãƒŗãƒˆč¨­åŽš", "acknowledge": "äē†č§Ŗ", @@ -14,7 +14,7 @@ "add_a_location": "場所をčŋŊ加", "add_a_name": "名前をčŋŊ加", "add_a_title": "ã‚ŋイトãƒĢをčŋŊ加", - "add_endpoint": "ã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆã‚’čŋŊ加しãĻください", + "add_endpoint": "ã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆã‚’čŋŊ加", "add_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’čŋŊ加", "add_import_path": "ã‚¤ãƒŗãƒãƒŧトパ゚をčŋŊ加", "add_location": "場所をčŋŊ加", @@ -26,11 +26,12 @@ "add_to_album": "ã‚ĸãƒĢバムãĢčŋŊ加", "add_to_album_bottom_sheet_added": "{album}ãĢčŋŊ加", "add_to_album_bottom_sheet_already_exists": "{album}ãĢčŋŊ加済ãŋ", + "add_to_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧãĢå…Ĩれる", "add_to_shared_album": "å…ąæœ‰ã‚ĸãƒĢバムãĢčŋŊ加", "add_url": "URLをčŋŊ加", - "added_to_archive": "ã‚ĸãƒŧã‚ĢイブãĢčŋŊ加済", + "added_to_archive": "ã‚ĸãƒŧã‚ĢイブãĢしぞした", "added_to_favorites": "お気ãĢå…ĨりãĢčŋŊ加済", - "added_to_favorites_count": "{count, number} 枚ぎį”ģ像をお気ãĢå…ĨりãĢčŋŊ加済", + "added_to_favorites_count": "{count, number} 枚ぎį”ģ像をお気ãĢå…ĨりãĢčŋŊ加しぞした", "admin": { "add_exclusion_pattern_description": "除外パã‚ŋãƒŧãƒŗã‚’čŋŊ加しぞす。ワイãƒĢドã‚Ģãƒŧド「*」「**」「?」をäŊŋį”¨ã§ããžã™ã€‚ã™ãšãĻãŽãƒ‡ã‚ŖãƒŦクトãƒĒで「Raw」と名前がäģ˜ã„ãŸãƒ•ã‚Ąã‚¤ãƒĢã‚’į„ĄčĻ–ã™ã‚‹ãĢは、「**/Raw/**」をäŊŋį”¨ã—ãžã™ã€‚ãžãŸã€ã€Œ.tif」でįĩ‚ã‚ã‚‹ãƒ•ã‚Ąã‚¤ãƒĢをすずãĻį„ĄčĻ–ã™ã‚‹ãĢは、「**/*.tif」をäŊŋį”¨ã—ãžã™ã€‚ã•ã‚‰ãĢ、įĩļå¯žãƒ‘ã‚šã‚’į„ĄčĻ–ã™ã‚‹ãĢは「/path/to/ignore/**」をäŊŋį”¨ã—ãžã™ã€‚", "asset_offline_description": "こぎ外部ナイブナãƒĒぎã‚ĸã‚ģãƒƒãƒˆã¯ãƒ‡ã‚Ŗã‚šã‚¯ä¸ŠãĢčĻ‹ã¤ã‹ã‚‰ãĒくãĒãŖãĻã‚´ãƒŸįŽąãĢį§ģå‹•ã•ã‚Œãžã—ãŸã€‚ãƒ•ã‚Ąã‚¤ãƒĢがナイブナãƒĒぎ中でį§ģ動された場合はã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗã§æ–°ã—ã„å¯žåŋœã™ã‚‹ã‚ĸã‚ģットをįĸēčĒã—ãĻください。こぎã‚ĸã‚ģットを垊元するãĢはäģĨä¸‹ãŽãƒ•ã‚Ąã‚¤ãƒĢパ゚がImmichからã‚ĸクã‚ģ゚できるかįĸēčĒã—ãĻナイブナãƒĒã‚’ã‚šã‚­ãƒŖãƒŗã—ãĻください。", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "æœŦåŊ“ãĢ全ãĻãŽãƒ­ã‚°ã‚¤ãƒŗæ–šæŗ•ã‚’į„ĄåŠšãĢしぞすか? ãƒ­ã‚°ã‚¤ãƒŗã¯åŽŒå…¨ãĢį„ĄåŠšãĢãĒりぞす。", "authentication_settings_reenable": "å†ãŗæœ‰åŠšãĢするãĢは、ã‚ĩãƒŧバãƒŧã‚ŗãƒžãƒŗãƒ‰ã‚’äŊŋį”¨ã—ãĻください。", "background_task_job": "バックグナã‚Ļãƒŗãƒ‰ã‚ŋ゚ク", - "backup_database": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップ", - "backup_database_enable_description": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップを有劚ãĢする", + "backup_database": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップをäŊœæˆ", + "backup_database_enable_description": "デãƒŧã‚ŋベãƒŧ゚バックã‚ĸップぎäŊœæˆã‚’有劚ãĢする", "backup_keep_last_amount": "過åŽģぎバックã‚ĸップぎäŋæŒæ•°", - "backup_settings": "バックã‚ĸãƒƒãƒ—č¨­åŽš", - "backup_settings_description": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸãƒƒãƒ—č¨­åŽšãŽįŽĄį†", + "backup_settings": "デãƒŧã‚ŋベãƒŧ゚バックã‚ĸップäŊœæˆãŽč¨­åޚ", + "backup_settings_description": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップäŊœæˆč¨­åŽšãŽįŽĄį† (こぎジョブはãƒĸニã‚ŋãƒĒãƒŗã‚°ã•ã‚Œãžã›ã‚“ã—ã€å¤ąæ•—ãŒį™ēį”Ÿã—ãĻもあãĒたãĢ通įŸĨãŒčĄŒãã“ã¨ã¯ã‚ã‚Šãžã›ã‚“)", "check_all": "すずãĻを選択", "cleanup": "クãƒĒãƒŧãƒŗã‚ĸップ", "cleared_jobs": "{job}ぎジョブをクãƒĒã‚ĸしぞした", @@ -53,6 +54,7 @@ "confirm_email_below": "įĸēčĒãŽãŸã‚ã€äģĨ下ãĢ \"{email}\" とå…Ĩ力しãĻください", "confirm_reprocess_all_faces": "æœŦåŊ“ãĢすずãĻãŽéĄ”ã‚’å†å‡Ļį†ã—ãžã™ã‹? これãĢより名前がäģ˜ã‘られたäēēį‰Šã‚‚æļˆåŽģされぞす。", "confirm_user_password_reset": "æœŦåŊ“ãĢ {user} ぎパ゚ワãƒŧドをãƒĒã‚ģットしぞすかīŧŸ", + "confirm_user_pin_code_reset": "{user}ぎPINã‚ŗãƒŧドをãƒĒã‚ģットしãĻよいですかīŧŸ", "create_job": "ジョブぎäŊœæˆ", "cron_expression": "Cronåŧ", "cron_expression_description": "cronぎフりãƒŧマットをäŊŋãŖãĻã‚šã‚­ãƒŖãƒŗé–“éš”ã‚’č¨­åŽšã—ãžã™ã€‚čŠŗã—ãã¯Crontab GuruãĒãŠã‚’å‚į…§ã—ãĻください", @@ -72,6 +74,7 @@ "image_format_description": "WebPはJPEGã‚ˆã‚Šã‚‚ãƒ•ã‚Ąã‚¤ãƒĢã‚ĩイã‚ēãŒå°ã•ã„ã§ã™ãŒã€ã‚¨ãƒŗã‚ŗãƒŧドãĢ時間がかかりぞす。", "image_fullsize_description": "į”ģåƒã‚’æ‹Ąå¤§ã™ã‚‹æ™‚ãĢäŊŋã‚ã‚Œã‚‹ãƒĄã‚ŋデãƒŧã‚ŋを取り除いた原寸大į”ģ像", "image_fullsize_enabled": "原寸大į”ģåƒį”Ÿæˆã‚’æœ‰åŠšãĢする", + "image_fullsize_enabled_description": "Webã§čĄ¨į¤ēãŒé›Ŗã—ã„ã¨ã•ã‚Œã‚‹į”ģ像フりãƒŧマットãĢ寞しãĻ原寸大į”ģåƒã‚’į”Ÿæˆã™ã‚‹ã€‚", "image_fullsize_quality_description": "1から100ぞで原寸大į”ģ像ぎčŗĒです。éĢ˜ã„ãģã†ãŒã„ã„ãŒãƒ•ã‚Ąã‚¤ãƒĢが大きくãĒりぞす。", "image_fullsize_title": "原寸大į”ģåƒč¨­åŽš", "image_prefer_embedded_preview": "埋めčžŧãŋプãƒŦビãƒĨãƒŧをå„Ē先", @@ -191,26 +194,22 @@ "oauth_auto_register": "č‡Ē動į™ģ錞", "oauth_auto_register_description": "OAuthでã‚ĩã‚¤ãƒŗã‚¤ãƒŗã—ãŸã‚ã¨ã€č‡Ēå‹•įš„ãĢ新čĻãƒĻãƒŧã‚ļãƒŧをį™ģéŒ˛ã™ã‚‹", "oauth_button_text": "ボã‚ŋãƒŗãƒ†ã‚­ã‚šãƒˆ", - "oauth_client_id": "クナイã‚ĸãƒŗãƒˆID", - "oauth_client_secret": "クナイã‚ĸãƒŗãƒˆ ã‚ˇãƒŧクãƒŦット", + "oauth_client_secret_description": "OAuthプロバイダãƒŧがPKCEをã‚ĩポãƒŧトしãĻいãĒい場合はåŋ…čρ", "oauth_enable_description": "OAuthã§ãƒ­ã‚°ã‚¤ãƒŗ", - "oauth_issuer_url": "į™ēčĄŒå…ƒURL", "oauth_mobile_redirect_uri": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURI", "oauth_mobile_redirect_uri_override": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURIīŧˆä¸Šæ›¸ãīŧ‰", "oauth_mobile_redirect_uri_override_description": "'{callback}'ãĒお、ãƒĸバイãƒĢURIがOAuthプロバイダãƒŧãĢã‚ˆãŖãĻč¨ąå¯ã•ã‚ŒãĻいãĒい場合ãĢ有劚ãĢしãĻください", - "oauth_profile_signing_algorithm": "ãƒ—ãƒ­ãƒ•ã‚Ąã‚¤ãƒĢぎįŊ˛åã‚ĸãƒĢゴãƒĒã‚ēム", - "oauth_profile_signing_algorithm_description": "ãƒĻãƒŧã‚ļãƒŧãƒ—ãƒ­ãƒ•ã‚Ąã‚¤ãƒĢをįŊ˛åã™ã‚‹ãŽãĢäŊŋį”¨ã™ã‚‹ã‚ĸãƒĢゴãƒĒã‚ēム。", - "oauth_scope": "ã‚šã‚ŗãƒŧプ", "oauth_settings": "OAuth", "oauth_settings_description": "OAuthãƒ­ã‚°ã‚¤ãƒŗč¨­åŽšã‚’įŽĄį†ã—ãžã™", "oauth_settings_more_details": "こぎ抟čƒŊãŽčŠŗį´°ãĢついãĻは、ドキãƒĨãƒĄãƒŗãƒˆã‚’å‚į…§ã—ãĻください。", - "oauth_signing_algorithm": "įŊ˛åã‚ĸãƒĢゴãƒĒã‚ēム", "oauth_storage_label_claim": "゚トãƒŦãƒŧジナベãƒĢ クãƒŦãƒŧム", "oauth_storage_label_claim_description": "ãƒĻãƒŧã‚ļãƒŧぎ゚トãƒŦãƒŧジナベãƒĢを、こぎクãƒŦãƒŧムぎ値ãĢč‡Ēå‹•įš„ãĢč¨­åŽšã—ãžã™ã€‚", "oauth_storage_quota_claim": "゚トãƒŦãƒŧジクりãƒŧã‚ŋ クãƒŦãƒŧム", "oauth_storage_quota_claim_description": "ãƒĻãƒŧã‚ļãƒŧぎ゚トãƒŦãƒŧジクりãƒŧã‚ŋをこぎクãƒŦãƒŧムぎ値ãĢč‡Ēå‹•įš„ãĢč¨­åŽšã—ãžã™ã€‚", "oauth_storage_quota_default": "デフりãƒĢトぎ゚トãƒŦãƒŧã‚¸å‰˛ã‚ŠåŊ“ãĻ(GiB)", "oauth_storage_quota_default_description": "クãƒŦãƒŧムが提䞛されãĻいãĒい場合ãĢäŊŋį”¨ã•ã‚Œã‚‹ã‚¯ã‚Šãƒŧã‚ŋをGiB単äŊã§č¨­åŽšã—ãžã™īŧˆį„Ąåˆļ限ãĢする場合は0をå…Ĩ力しãĻくださいīŧ‰ã€‚", + "oauth_timeout": "ãƒĒクエ゚トã‚ŋイムã‚ĸã‚Ļト", + "oauth_timeout_description": "ãƒĒクエ゚トぎã‚ŋイムã‚ĸã‚Ļトぞでぎ時間(ms)", "offline_paths": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽãƒ‘ã‚š", "offline_paths_description": "これらぎįĩæžœã¯ã€å¤–部ナイブナãƒĒãĢåąžã•ãĒã„ãƒ•ã‚Ąã‚¤ãƒĢを手動で削除したことãĢよる可čƒŊ性がありぞす。", "password_enable_description": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚とパ゚ワãƒŧãƒ‰ã§ãƒ­ã‚°ã‚¤ãƒŗ", @@ -351,6 +350,7 @@ "user_delete_delay_settings_description": "å‰Šé™¤åŽŸčĄŒåžŒã€ãƒĻãƒŧã‚ļãƒŧぎã‚ĸã‚Ģã‚Ļãƒŗãƒˆã¨ã‚ĸã‚ģットが厌全ãĢ削除されるぞでぎæ—Ĩ数。 ãƒĻãƒŧã‚ļãƒŧå‰Šé™¤ã‚¸ãƒ§ãƒ–ã¯æˇąå¤œãĢåŽŸčĄŒã•ã‚Œã€å‰Šé™¤ãŽæē–備ができãĻいるãƒĻãƒŧã‚ļãƒŧをįĸēčĒã—ãžã™ã€‚ ã“ãŽč¨­åŽšã¸ãŽå¤‰æ›´ã¯ã€æŦĄå›žãŽåŽŸčĄŒæ™‚ãĢ反映されぞす。", "user_delete_immediately": "{user} ぎã‚ĸã‚Ģã‚Ļãƒŗãƒˆã¨ã‚ĸã‚ģãƒƒãƒˆã¯ã€į›´ãĄãĢ厌全ãĢ削除するためãĢキãƒĨãƒŧãĢčŋŊ加されぞす。", "user_delete_immediately_checkbox": "ãƒĻãƒŧã‚ļãƒŧとã‚ĸã‚ģãƒƒãƒˆã‚’åŗæ™‚å‰Šé™¤ã™ã‚‹ã‚­ãƒĨãƒŧãĢå…Ĩれる", + "user_details": "ãƒĻãƒŧã‚ļãƒŧčŠŗį´°", "user_management": "ãƒĻãƒŧã‚ļãƒŧįŽĄį†", "user_password_has_been_reset": "ãƒĻãƒŧã‚ļãƒŧぎパ゚ワãƒŧドがãƒĒã‚ģットされぞした:", "user_password_reset_description": "ãƒĻãƒŧã‚ļãƒŧãĢこぎ一時パ゚ワãƒŧドを提䞛し、æŦĄå›žãƒ­ã‚°ã‚¤ãƒŗæ™‚ãĢパ゚ワãƒŧドを変更しãĒければãĒらãĒいことをäŧãˆãĻください。", @@ -370,13 +370,17 @@ "admin_password": "įŽĄį†č€…ãƒ‘ã‚šãƒ¯ãƒŧド", "administration": "įŽĄį†", "advanced": "čŠŗį´°č¨­åŽš", - "advanced_settings_log_level_title": "ログãƒŦベãƒĢ: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "åˆĨぎåŸēæē–ãĢåž“ãŖãĻãƒĄãƒ‡ã‚Ŗã‚ĸãƒ•ã‚Ąã‚¤ãƒĢãĢãƒ•ã‚ŖãƒĢã‚ŋãƒŧをかけãĻã€åŒæœŸã‚’čĄŒã„ãžã™ã€‚ã‚ĸプãƒĒがすずãĻぎã‚ĸãƒĢバムをčĒ­ãŋčžŧんでくれãĒい場合ãĢぎãŋ、こぎ抟čƒŊをčŠĻしãĻください。", + "advanced_settings_enable_alternate_media_filter_title": "[čŠĻ鍓運ᔍ] åˆĨぎデバイ゚ぎã‚ĸãƒĢãƒãƒ åŒæœŸãƒ•ã‚ŖãƒĢã‚ŋãƒŧをäŊŋį”¨ã™ã‚‹", + "advanced_settings_log_level_title": "ログãƒŦベãƒĢ: {level}", "advanced_settings_prefer_remote_subtitle": "デバイ゚ãĢã‚ˆãŖãĻは、デバイ゚上ãĢあるã‚ĩムネイãƒĢぎロãƒŧドãĢ非常ãĢ時間がかかることがありぞす。こぎã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’ãĢ有劚ãĢするäē‹ãĢより、ã‚ĩãƒŧバãƒŧã‹ã‚‰į›´æŽĨį”ģ像をロãƒŧドすることが可čƒŊです。", "advanced_settings_prefer_remote_title": "ãƒĒãƒĸãƒŧトをå„Ē先する", "advanced_settings_proxy_headers_subtitle": "ãƒ—ãƒ­ã‚­ã‚ˇãƒ˜ãƒƒãƒ€ã‚’č¨­åŽšã™ã‚‹", "advanced_settings_proxy_headers_title": "ãƒ—ãƒ­ã‚­ã‚ˇãƒ˜ãƒƒãƒ€", "advanced_settings_self_signed_ssl_subtitle": "SSLぎチェックを゚キップする。č‡ĒåˇąįŊ˛åč¨ŧ明書がåŋ…čĻã§ã™ã€‚", "advanced_settings_self_signed_ssl_title": "č‡ĒåˇąįŊ˛åč¨ŧæ˜Žæ›¸ã‚’č¨ąå¯ã™ã‚‹", + "advanced_settings_sync_remote_deletions_subtitle": "Webでこぎ操äŊœã‚’čĄŒãŖãŸéš›ãĢ、č‡Ēå‹•įš„ãĢこぎデバイ゚上からåŊ“čОã‚ĸã‚ģットを削除ぞたは垊元する", + "advanced_settings_sync_remote_deletions_title": "ãƒĒãƒĸãƒŧト削除ぎ同期 [čŠĻ鍓運ᔍ]", "advanced_settings_tile_subtitle": "čŋŊ加ãƒĻãƒŧã‚ļãƒŧč¨­åŽš", "advanced_settings_troubleshooting_subtitle": "トナブãƒĢã‚ˇãƒĨãƒŧãƒ†ã‚Ŗãƒŗã‚°į”¨ãŽčŠŗį´°č¨­åŽšã‚’ã‚ĒãƒŗãĢする", "advanced_settings_troubleshooting_title": "トナブãƒĢã‚ˇãƒĨãƒŧãƒ†ã‚Ŗãƒŗã‚°", @@ -399,16 +403,16 @@ "album_remove_user_confirmation": "æœŦåŊ“ãĢ{user}を削除しぞすか?", "album_share_no_users": "こぎã‚ĸãƒĢバムを全ãĻぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã—ãŸã‹ã€å…ąæœ‰ã™ã‚‹ãƒĻãƒŧã‚ļãƒŧがいãĒいようです。", "album_thumbnail_card_item": "1枚", - "album_thumbnail_card_items": "{}枚", - "album_thumbnail_card_shared": "å…ąæœ‰æ¸ˆãŋ", - "album_thumbnail_shared_by": "{}ãŒå…ąæœ‰ä¸­", + "album_thumbnail_card_items": "{count}é …į›Ž", + "album_thumbnail_card_shared": " ¡ å…ąæœ‰æ¸ˆãŋ", + "album_thumbnail_shared_by": "{user}ãŒå…ąæœ‰ä¸­", "album_updated": "ã‚ĸãƒĢバム更新", "album_updated_setting_description": "å…ąæœ‰ã‚ĸãƒĢバムãĢ新しいã‚ĸã‚ģットがčŋŊ加されたとき通įŸĨを受け取る", "album_user_left": "{album} をåŽģりぞした", "album_user_removed": "{user} を削除しぞした", "album_viewer_appbar_delete_confirm": "æœŦåŊ“ãĢこぎã‚ĸãƒĢバムを削除しぞすかīŧŸ", - "album_viewer_appbar_share_err_delete": "å‰Šé™¤å¤ąæ•—", - "album_viewer_appbar_share_err_leave": "退å‡ēå¤ąæ•—", + "album_viewer_appbar_share_err_delete": "ã‚ĸãƒĢバムぎ削除ãĢå¤ąæ•—ã—ãžã—ãŸ", + "album_viewer_appbar_share_err_leave": "退å‡ēãĢå¤ąæ•—ã—ãžã—ãŸ", "album_viewer_appbar_share_err_remove": "ã‚ĸãƒĢãƒãƒ ã‹ã‚‰å†™įœŸã‚’å‰Šé™¤ã™ã‚‹éš›ãĢエナãƒŧį™ēį”Ÿ", "album_viewer_appbar_share_err_title": "ã‚ŋイトãƒĢå¤‰æ›´ãŽå¤ąæ•—", "album_viewer_appbar_share_leave": "ã‚ĸãƒĢバムから退å‡ē", @@ -431,18 +435,18 @@ "api_key_description": "こぎ値は一回ぎãŋ襨į¤ēされぞす。 ã‚Ļã‚Ŗãƒŗãƒ‰ã‚Ļを閉じる前ãĢåŋ…ãšã‚ŗãƒ”ãƒŧしãĻください。", "api_key_empty": "APIキãƒŧ名はįŠēį™ŊãĢできぞせん", "api_keys": "APIキãƒŧ", - "app_bar_signout_dialog_content": " ã‚ĩã‚¤ãƒŗã‚ĸã‚ĻトしぞすかīŧŸ", + "app_bar_signout_dialog_content": "ã‚ĩã‚¤ãƒŗã‚ĸã‚ĻトしぞすかīŧŸ", "app_bar_signout_dialog_ok": "はい", - "app_bar_signout_dialog_title": " ã‚ĩã‚¤ãƒŗã‚ĸã‚Ļト", + "app_bar_signout_dialog_title": "ã‚ĩã‚¤ãƒŗã‚ĸã‚Ļト", "app_settings": "ã‚ĸプãƒĒč¨­åŽš", "appears_in": "これらãĢåĢぞれぞす", "archive": "ã‚ĸãƒŧã‚Ģイブ", "archive_or_unarchive_photo": "å†™įœŸã‚’ã‚ĸãƒŧã‚Ģイブぞたはã‚ĸãƒŧã‚Ģã‚¤ãƒ–č§Ŗé™¤", - "archive_page_no_archived_assets": "ã‚ĸãƒŧã‚Ģイブ済ãŋãŽå†™įœŸãžãŸã¯ãƒ“ãƒ‡ã‚Ēがありぞせん", - "archive_page_title": "ã‚ĸãƒŧã‚Ģイブ({})", + "archive_page_no_archived_assets": "ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã—ãŸå†™įœŸãžãŸã¯ãƒ“ãƒ‡ã‚Ēがありぞせん", + "archive_page_title": "ã‚ĸãƒŧã‚Ģイブ ({count})", "archive_size": "ã‚ĸãƒŧã‚Ģイブã‚ĩイã‚ē", "archive_size_description": "ダã‚Ļãƒŗãƒ­ãƒŧドぎã‚ĸãƒŧã‚Ģイブ ã‚ĩイã‚ēã‚’č¨­åŽš(GiB 単äŊ)", - "archived": "ã‚ĸãƒŧã‚Ģイブ済ãŋ", + "archived": "ã‚ĸãƒŧã‚Ģイブ", "archived_count": "ã‚ĸãƒŧã‚Ģイブされた{count, plural, other {#å€‹ãŽé …į›Ž}}", "are_these_the_same_person": "これらは同じäēēį‰Šã§ã™ã‹?", "are_you_sure_to_do_this": "æœŦåŊ“ãĢã“ã‚Œã‚’čĄŒã„ãžã™ã‹?", @@ -464,7 +468,7 @@ "asset_list_settings_title": "グãƒĒッド", "asset_offline": "ã‚ĸã‚ģットはã‚Ēãƒ•ãƒŠã‚¤ãƒŗã§ã™", "asset_offline_description": "こぎã‚ĸã‚ģットはã‚Ēãƒ•ãƒŠã‚¤ãƒŗã§ã™ã€‚ Immichã¯ãƒ•ã‚Ąã‚¤ãƒĢぎ場所ãĢã‚ĸクã‚ģ゚できぞせん。 ã‚ĸã‚ģãƒƒãƒˆãŒåˆŠį”¨å¯čƒŊであることをįĸēčĒã—ãƒŠã‚¤ãƒ–ãƒŠãƒĒã‚’å†ã‚šã‚­ãƒŖãƒŗã—ãĻください。", - "asset_restored_successfully": "{}é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", + "asset_restored_successfully": "垊元できぞした", "asset_skipped": "゚キップ済", "asset_skipped_in_trash": "ã‚´ãƒŸįŽąãŽä¸­", "asset_uploaded": "ã‚ĸップロãƒŧド済", @@ -476,18 +480,18 @@ "assets_added_to_album_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットをã‚ĸãƒĢバムãĢčŋŊ加しぞした", "assets_added_to_name_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを{hasName, select, true {{name}} other {新しいã‚ĸãƒĢバム}}ãĢčŋŊ加しぞした", "assets_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģット", - "assets_deleted_permanently": "{}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", - "assets_deleted_permanently_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", - "assets_moved_to_trash_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットをごãŋįŽąãĢį§ģ動しぞした", - "assets_permanently_deleted_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを厌全ãĢ削除しぞした", - "assets_removed_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを削除しぞした", - "assets_removed_permanently_from_device": "į̝æœĢから{}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "assets_deleted_permanently": "{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "assets_deleted_permanently_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "assets_moved_to_trash_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}ã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", + "assets_permanently_deleted_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}を厌全ãĢ削除しぞした", + "assets_removed_count": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}を削除しぞした", + "assets_removed_permanently_from_device": "デバイ゚から{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", "assets_restore_confirmation": "ごãŋįŽąãŽã‚ĸã‚ģットをすずãĻ垊元しãĻもよろしいですか? こぎ操äŊœã‚’å…ƒãĢæˆģすことはできぞせん! ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽã‚ĸã‚ģãƒƒãƒˆã¯ã“ãŽæ–šæŗ•ã§ã¯åžŠå…ƒã§ããžã›ã‚“ã€‚", - "assets_restored_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを垊元しぞした", - "assets_restored_successfully": "{}é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", - "assets_trashed": "{}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", + "assets_restored_count": "{count, plural, one {#} other {#}}é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", + "assets_restored_successfully": "{count}é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", + "assets_trashed": "{count}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "assets_trashed_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットをごãŋįŽąãĢį§ģ動しぞした", - "assets_trashed_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", + "assets_trashed_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{count}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "assets_were_part_of_album_count": "{count, plural, one {個} other {個}}ぎã‚ĸã‚ģットはæ—ĸãĢã‚ĸãƒĢバムぎ一部です", "authorized_devices": "čĒå¯æ¸ˆãŋデバイ゚", "automatic_endpoint_switching_subtitle": "指厚されたWi-FiãĢæŽĨį™‚ぎãŋロãƒŧã‚ĢãƒĢæŽĨįļšã‚’čĄŒã„ã€äģ–ぎネットワãƒŧク下では通常通りぎæŽĨįļšã‚’čĄŒã„ãžã™", @@ -496,7 +500,7 @@ "back_close_deselect": "æˆģã‚‹ã€é–‰ã˜ã‚‹ã€é¸æŠžč§Ŗé™¤", "background_location_permission": "バックグナã‚Ļãƒŗãƒ‰äŊįŊŽæƒ…å ąã‚ĸクã‚ģ゚", "background_location_permission_content": "æ­Ŗå¸¸ãĢWi-Fiぎ名前(SSID)ã‚’į˛åž—ã™ã‚‹ãĢはã‚ĸプãƒĒが常ãĢčŠŗį´°ãĒäŊįŊŽæƒ…å ąãĢã‚ĸクã‚ģ゚できるåŋ…čĻãŒã‚ã‚Šãžã™", - "backup_album_selection_page_albums_device": "į̝æœĢ上ぎã‚ĸãƒĢバム数: {} ", + "backup_album_selection_page_albums_device": "デバイ゚上ぎã‚ĸãƒĢバム({count})", "backup_album_selection_page_albums_tap": "ã‚ŋップで選択、ダブãƒĢã‚ŋップで除外", "backup_album_selection_page_assets_scatter": "ã‚ĸãƒĢバムを選択ãƒģ除外しãĻバックã‚ĸãƒƒãƒ—ã™ã‚‹å†™įœŸã‚’é¸ãļ (åŒã˜å†™įœŸãŒč¤‡æ•°ãŽã‚ĸãƒĢバムãĢį™ģéŒ˛ã•ã‚ŒãĻいることがあるため)", "backup_album_selection_page_select_albums": "ã‚ĸãƒĢバムを選択", @@ -505,11 +509,11 @@ "backup_all": "すずãĻ", "backup_background_service_backup_failed_message": "ã‚ĸップロãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸã€‚ãƒĒトナイ中", "backup_background_service_connection_failed_message": "ã‚ĩãƒŧバãƒŧãĢæŽĨįļšã§ããžã›ã‚“。ãƒĒトナイ中", - "backup_background_service_current_upload_notification": " {}をã‚ĸップロãƒŧド中", + "backup_background_service_current_upload_notification": "{filename}をã‚ĸップロãƒŧド中", "backup_background_service_default_notification": "æ–°ã—ã„å†™įœŸã‚’įĸēčĒä¸­", "backup_background_service_error_title": "バックã‚ĸップエナãƒŧ", "backup_background_service_in_progress_notification": "バックã‚ĸップ中", - "backup_background_service_upload_failure_notification": "{}ぎã‚ĸップロãƒŧドãĢå¤ąæ•—", + "backup_background_service_upload_failure_notification": "{filename}ぎã‚ĸップロãƒŧドãĢå¤ąæ•—", "backup_controller_page_albums": "ã‚ĸãƒĢバム", "backup_controller_page_background_app_refresh_disabled_content": "バックグナã‚Ļãƒŗãƒ‰ã§å†™įœŸãŽãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—ã‚’čĄŒã„ãŸã„å ´åˆã¯ã€ãƒãƒƒã‚¯ã‚°ãƒŠã‚Ļãƒŗãƒ‰æ›´æ–°ã‚’\nč¨­åŽš > 一čˆŦ > Appぎバックグナã‚Ļãƒŗãƒ‰æ›´æ–°\nからã‚ĒãƒŗãĢしãĻください。", "backup_controller_page_background_app_refresh_disabled_title": "バックグナã‚Ļãƒŗãƒ‰æ›´æ–°ã¯ã‚ĒフãĢãĒãŖãĻいぞす", @@ -520,7 +524,7 @@ "backup_controller_page_background_battery_info_title": "バッテãƒĒãƒŧぎ最遊化", "backup_controller_page_background_charging": "充é›ģ中ぎãŋ", "backup_controller_page_background_configure_error": "バックグナã‚Ļãƒŗãƒ‰ã‚ĩãƒŧãƒ“ã‚šãŽč¨­åŽšãĢå¤ąæ•—", - "backup_controller_page_background_delay": "æ–°ã—ã„å†™įœŸãŽãƒãƒƒã‚¯ã‚ĸップ遅åģļ: {}", + "backup_controller_page_background_delay": "æ–°ã—ã„é …į›ŽãŽãƒãƒƒã‚¯ã‚ĸップ開始ぞで垅つ時間: {duration}", "backup_controller_page_background_description": "ã‚ĸプãƒĒを開いãĻいãĒいときもバックã‚ĸãƒƒãƒ—ã‚’čĄŒã„ãžã™", "backup_controller_page_background_is_off": "ãƒãƒƒã‚¯ã‚°ãƒŠãƒŗãƒ‰ã‚ĩãƒŧビ゚がã‚ĒフãĢãĒãŖãĻいぞす", "backup_controller_page_background_is_on": "ãƒãƒƒã‚¯ã‚°ãƒŠãƒŗãƒ‰ã‚ĩãƒŧビ゚がã‚ĒãƒŗãĢãĒãŖãĻいぞす", @@ -530,12 +534,12 @@ "backup_controller_page_backup": "バックã‚ĸップ", "backup_controller_page_backup_selected": "選択中:", "backup_controller_page_backup_sub": "バックã‚ĸãƒƒãƒ—ã•ã‚ŒãŸå†™įœŸã¨å‹•į”ģぎ数", - "backup_controller_page_created": "{} äŊœæˆ", + "backup_controller_page_created": "äŊœæˆæ—Ĩ: {date}", "backup_controller_page_desc_backup": "ã‚ĸプãƒĒを開いãĻいるときãĢå†™įœŸã¨å‹•į”ģをバックã‚ĸップしぞす", "backup_controller_page_excluded": "除外中ぎã‚ĸãƒĢバム:", - "backup_controller_page_failed": "å¤ąæ•—: ({})", - "backup_controller_page_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名: {} [{}] ", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "å¤ąæ•—: ({count})", + "backup_controller_page_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "バックã‚ĸãƒƒãƒ—æƒ…å ą", "backup_controller_page_none_selected": "ãĒし", "backup_controller_page_remainder": "掋り", @@ -544,7 +548,7 @@ "backup_controller_page_start_backup": "バックã‚ĸップ開始", "backup_controller_page_status_off": "バックã‚ĸップがã‚ĒフãĢãĒãŖãĻいぞす", "backup_controller_page_status_on": "バックã‚ĸップがã‚ĒãƒŗãĢãĒãŖãĻいぞす", - "backup_controller_page_storage_format": "äŊŋį”¨æ¸ˆãŋ({}) - 全äŊ“({})", + "backup_controller_page_storage_format": "äŊŋᔍ䏭: {used} / {total}", "backup_controller_page_to_backup": "バックã‚ĸップされるã‚ĸãƒĢバム", "backup_controller_page_total_sub": "選択されたã‚ĸãƒĢãƒãƒ ãŽå†™įœŸã¨å‹•į”ģぎ数", "backup_controller_page_turn_off": "バックã‚ĸップをã‚ĒフãĢする", @@ -559,31 +563,35 @@ "backup_options_page_title": "バックã‚ĸップã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "backup_setting_subtitle": "ã‚ĸップロãƒŧドãĢé–ĸã™ã‚‹č¨­åŽš", "backward": "新しい斚へ", + "biometric_auth_enabled": "į”ŸäŊ“čĒč¨ŧを有劚化しぞした", + "biometric_locked_out": "į”ŸäŊ“čĒč¨ŧãĢより、ã‚ĸクã‚ģ゚できぞせん", + "biometric_no_options": "į”ŸäŊ“čĒč¨ŧã‚’åˆŠį”¨ã§ããžã›ã‚“", + "biometric_not_available": "ã“ãŽãƒ‡ãƒã‚¤ã‚šã§ã¯į”ŸäŊ“čĒč¨ŧã‚’ã”åˆŠį”¨ã„ãŸã ã‘ãžã›ã‚“", "birthdate_saved": "į”Ÿåš´æœˆæ—ĨãŒæ­Ŗå¸¸ãĢäŋå­˜ã•れぞした", "birthdate_set_description": "į”Ÿåš´æœˆæ—Ĩã¯ã€å†™įœŸæ’ŽåŊąæ™‚ぎこぎäēēį‰ŠãŽåš´éŊĸã‚’č¨ˆįŽ—ã™ã‚‹ãŸã‚ãĢäŊŋį”¨ã•ã‚Œãžã™ã€‚", "blurred_background": "ãŧã‚„ã‘ãŸčƒŒæ™¯", "bugs_and_feature_requests": "バグと抟čƒŊぎãƒĒクエ゚ト", "build": "ビãƒĢド", "build_image": "ビãƒĢãƒ‰ã‚¤ãƒĄãƒŧジ", - "bulk_delete_duplicates_confirmation": "æœŦåŊ“ãĢ {count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã—ãŸã‚ĸã‚ģットを一æ‹Ŧ削除しぞすか?これãĢã‚ˆã‚Šå„é‡č¤‡ä¸­ãŽæœ€å¤§ãŽã‚ĸã‚ģットがäŋæŒã•れ、äģ–ぎ全ãĻãŽé‡č¤‡ãŒå‰Šé™¤ã•ã‚Œãžã™ã€‚ã“ãŽæ“äŊœã‚’å…ƒãĢæˆģすことはできぞせん!", + "bulk_delete_duplicates_confirmation": "æœŦåŊ“ãĢ {count, plural, one {#} other {#}}ãŽé‡č¤‡ã—ãŸé …į›Žã‚’ä¸€æ‹Ŧ削除しぞすか?これãĢã‚ˆã‚Šã€é‡č¤‡ã—ãŸį”ģ像それぞれぎ中で最もã‚ĩイã‚ēぎ大きいもぎを掋し、äģ–ぎ全ãĻãŽé‡č¤‡ãŒå‰Šé™¤ã•ã‚Œãžã™ã€‚ã“ãŽæ“äŊœã‚’å…ƒãĢæˆģすことはできぞせん!", "bulk_keep_duplicates_confirmation": "æœŦåŊ“ãĢ{count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã‚ĸã‚ģットをäŋæŒã—ぞすか?これãĢよりäŊ•も削除されずãĢé‡č¤‡ã‚°ãƒĢãƒŧãƒ—ãŒč§Ŗæąēされぞす。", "bulk_trash_duplicates_confirmation": "æœŦåŊ“ãĢ{count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã—ãŸã‚ĸã‚ģットを一æ‹ŦでごãŋįŽąãĢį§ģ動しぞすか?これãĢã‚ˆã‚Šå„é‡č¤‡ä¸­ãŽæœ€å¤§ãŽã‚ĸã‚ģットがäŋæŒã•れ、äģ–ぎ全ãĻãŽé‡č¤‡ã¯ã”ãŋįŽąãĢį§ģ動されぞす。", "buy": "Immichをčŗŧå…Ĩ", - "cache_settings_album_thumbnails": "ナイブナãƒĒぎã‚ĩムネイãƒĢ ({}枚)", + "cache_settings_album_thumbnails": "ナイブナãƒĒぎã‚ĩムネイãƒĢ ({count}枚)", "cache_settings_clear_cache_button": "ã‚­ãƒŖãƒƒã‚ˇãƒĨをクãƒĒã‚ĸ", "cache_settings_clear_cache_button_title": "ã‚­ãƒŖãƒƒã‚ˇãƒĨを削除 (ã‚­ãƒŖãƒƒã‚ˇãƒĨãŒå†į”Ÿæˆã•ã‚Œã‚‹ãžã§ã€ã‚ĸプãƒĒぎパフりãƒŧãƒžãƒŗã‚šãŒč‘—ã—ãäŊŽä¸‹ã—ぞす)", "cache_settings_duplicated_assets_clear_button": "クãƒĒã‚ĸ", "cache_settings_duplicated_assets_subtitle": "ã‚ĩãƒŧバãƒŧãĢã‚ĸップロãƒŧド済ãŋとčĒč­˜ã•ã‚ŒãŸå†™įœŸã‚„å‹•į”ģぎ数", - "cache_settings_duplicated_assets_title": "{}é …į›ŽãŽé‡č¤‡", - "cache_settings_image_cache_size": "ã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({}枚) ", + "cache_settings_duplicated_assets_title": "é‡č¤‡ã—ãŸé …į›Žæ•°: ({count})", + "cache_settings_image_cache_size": "į”ģåƒã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({count}é …į›Ž)", "cache_settings_statistics_album": "ナイブナãƒĒぎã‚ĩムネイãƒĢ", - "cache_settings_statistics_assets": "{}枚 ({}枚中)", + "cache_settings_statistics_assets": "{count}é …į›Ž ({size})", "cache_settings_statistics_full": "フãƒĢį”ģ像", "cache_settings_statistics_shared": "å…ąæœ‰ã‚ĸãƒĢバムぎã‚ĩムネイãƒĢ", "cache_settings_statistics_thumbnail": "ã‚ĩムネイãƒĢ", "cache_settings_statistics_title": "ã‚­ãƒŖãƒƒã‚ˇãƒĨ", "cache_settings_subtitle": "ã‚­ãƒŖãƒƒã‚ˇãƒĨぎ動äŊœã‚’変更する", - "cache_settings_thumbnail_size": "ã‚ĩムネイãƒĢãŽã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({}枚)", + "cache_settings_thumbnail_size": "ã‚ĩムネイãƒĢãŽã‚­ãƒŖãƒƒã‚ˇãƒĨぎã‚ĩイã‚ē ({count}é …į›Ž)", "cache_settings_tile_subtitle": "ロãƒŧã‚ĢãƒĢ゚トãƒŦãƒŧジぎ挙動をįĸēčĒã™ã‚‹", "cache_settings_tile_title": "ロãƒŧã‚ĢãƒĢ゚トãƒŦãƒŧジ", "cache_settings_title": "ã‚­ãƒŖãƒƒã‚ˇãƒĨãŽč¨­åŽš", @@ -592,12 +600,14 @@ "camera_model": "ã‚ĢãƒĄãƒŠãƒĸデãƒĢ", "cancel": "ã‚­ãƒŖãƒŗã‚ģãƒĢ", "cancel_search": "検į´ĸã‚’ã‚­ãƒŖãƒŗã‚ģãƒĢ", - "canceled": "Canceled", + "canceled": "ã‚­ãƒŖãƒŗã‚ģãƒĢされぞした", "cannot_merge_people": "äēēį‰Šã‚’įĩąåˆã§ããžã›ã‚“", "cannot_undo_this_action": "こぎ操äŊœã¯å…ƒãĢæˆģせぞせんīŧ", "cannot_update_the_description": "čĒŦ明を更新できぞせん", + "cast": "ã‚­ãƒŖã‚šãƒˆ", "change_date": "æ—Ĩ時を変更", - "change_display_order": "Change display order", + "change_description": "čĒŦ明文を変更", + "change_display_order": "襨į¤ē順を変更", "change_expiration_time": "有劚期限を変更", "change_location": "場所を変更", "change_name": "名前を変更", @@ -609,6 +619,7 @@ "change_password_form_new_password": "新しいパ゚ワãƒŧド", "change_password_form_password_mismatch": "パ゚ワãƒŧãƒ‰ãŒä¸€č‡´ã—ãžã›ã‚“", "change_password_form_reenter_new_password": "再åēĻパ゚ワãƒŧドをå…Ĩ力しãĻください", + "change_pin_code": "PINã‚ŗãƒŧドを変更", "change_your_password": "パ゚ワãƒŧドを変更しぞす", "changed_visibility_successfully": "非表į¤ēč¨­åŽšã‚’æ­Ŗå¸¸ãĢ変更しぞした", "check_all": "全ãĻ選択", @@ -643,23 +654,25 @@ "comments_are_disabled": "ã‚ŗãƒĄãƒŗãƒˆã¯į„ĄåŠšåŒ–ã•ã‚ŒãĻいぞす", "common_create_new_album": "ã‚ĸãƒĢバムをäŊœæˆ", "common_server_error": "ネットワãƒŧクæŽĨįļšã‚’įĸēčĒã—ã€ã‚ĩãƒŧバãƒŧがæŽĨįļšã§ãã‚‹įŠļ態ãĢあるかįĸēčĒã—ãĻください。ã‚ĸプãƒĒとã‚ĩãƒŧバãƒŧぎバãƒŧã‚¸ãƒ§ãƒŗãŒä¸€č‡´ã—ãĻいるかもįĸēčĒã—ãĻください。", - "completed": "Completed", + "completed": "厌äē†", "confirm": "įĸēčĒ", "confirm_admin_password": "įŽĄį†č€…ãƒ‘ã‚šãƒ¯ãƒŧドをįĸēčĒ", - "confirm_delete_face": "æœŦåŊ“ãĢ『{name}ã€ãŽéĄ”ã‚’ã‚ĸã‚ģットから削除しぞすか?", + "confirm_delete_face": "æœŦåŊ“ãĢ『{name}ã€ãŽéĄ”ã‚’é …į›Žã‹ã‚‰å‰Šé™¤ã—ãžã™ã‹?", "confirm_delete_shared_link": "æœŦåŊ“ãĢã“ãŽå…ąæœ‰ãƒĒãƒŗã‚¯ã‚’å‰Šé™¤ã—ãžã™ã‹?", - "confirm_keep_this_delete_others": "こぎã‚ĸã‚ģットäģĨ外ぎã‚ĸã‚ģットが゚ã‚ŋックから削除されぞす。æœŦåŊ“ãĢ削除しぞすか?", + "confirm_keep_this_delete_others": "ã“ãŽé …į›ŽäģĨå¤–ãŽé …į›ŽãŒã‚šã‚ŋックから削除されぞす。æœŦåŊ“ãĢ削除しぞすか?", + "confirm_new_pin_code": "こぎPINã‚ŗãƒŧドをäŊŋう", "confirm_password": "įĸēčĒ", + "connected_to": "æŽĨįļš:", "contain": "収める", "context": "įŠļæŗ", "continue": "įļšã‘ã‚‹", - "control_bottom_app_bar_album_info_shared": "{}枚 ¡ å…ąæœ‰æ¸ˆ", + "control_bottom_app_bar_album_info_shared": "{count}é …į›Ž ¡ å…ąæœ‰ä¸­", "control_bottom_app_bar_create_new_album": "ã‚ĸãƒĢバムをäŊœæˆ", - "control_bottom_app_bar_delete_from_immich": "Immichから削除", - "control_bottom_app_bar_delete_from_local": "į̝æœĢ上から削除", + "control_bottom_app_bar_delete_from_immich": "ã‚ĩãƒŧバãƒŧから削除", + "control_bottom_app_bar_delete_from_local": "デバイ゚上から削除", "control_bottom_app_bar_edit_location": "äŊįŊŽæƒ…å ąã‚’įˇ¨é›†", "control_bottom_app_bar_edit_time": "æ—Ĩ時を変更", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "å…ąæœ‰ãƒĒãƒŗã‚¯", "control_bottom_app_bar_share_to": "æŦĄãŽãƒĻãƒŧã‚ļãƒŧãĢå…ąæœ‰: ", "control_bottom_app_bar_trash_from_immich": "ã‚´ãƒŸįŽąãĢå…Ĩれる", "copied_image_to_clipboard": "į”ģ像をクãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧしぞした。", @@ -676,7 +689,7 @@ "covers": "ã‚Ģバãƒŧ", "create": "äŊœæˆ", "create_album": "ã‚ĸãƒĢバムをäŊœæˆ", - "create_album_page_untitled": "ã‚ŋイトãƒĢãĒし", + "create_album_page_untitled": "į„ĄéĄŒãŽã‚ŋイトãƒĢ", "create_library": "ナイブナãƒĒをäŊœæˆ", "create_link": "ãƒĒãƒŗã‚¯ã‚’äŊœã‚‹", "create_link_to_share": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’äŊœã‚‹", @@ -691,9 +704,11 @@ "create_tag_description": "ã‚ŋグをäŊœæˆã—ぞす。å…Ĩれ子構造ぎã‚ŋã‚°ã¯ã€ã¯ã˜ã‚ãŽã‚šãƒŠãƒƒã‚ˇãƒĨをåĢめた、ã‚ŋグぎ厌全ãĒパ゚をå…Ĩ力しãĻください。", "create_user": "ãƒĻãƒŧã‚ļãƒŧをäŊœæˆ", "created": "äŊœæˆ", + "created_at": "äŊœæˆ:", "crop": "クロップ", "curated_object_page_title": "čĸĢ写äŊ“", "current_device": "įžåœ¨ãŽãƒ‡ãƒã‚¤ã‚š", + "current_pin_code": "įžåœ¨ãŽPINã‚ŗãƒŧド", "current_server_address": "įžåœ¨ãŽã‚ĩãƒŧバãƒŧURL", "custom_locale": "ã‚Ģ゚ã‚ŋãƒ ãƒ­ã‚ąãƒŧãƒĢ", "custom_locale_description": "言čĒžã¨åœ°åŸŸãĢåŸēãĨいãĻæ—Ĩäģ˜ã¨æ•°å€¤ã‚’フりãƒŧマットしぞす", @@ -714,15 +729,15 @@ "deduplication_info_description": "ã‚ĸã‚ģットをč‡Ēå‹•įš„ãĢ選択しãĻé‡č¤‡ã‚’ä¸€æ‹Ŧで削除するãĢはæŦĄãŽã‚ˆã†ãĢしぞす:", "default_locale": "デフりãƒĢãƒˆãŽãƒ­ã‚ąãƒŧãƒĢ", "default_locale_description": "ブナã‚Ļã‚ļãŽãƒ­ã‚ąãƒŧãƒĢãĢåŸēãĨいãĻæ—Ĩäģ˜ã¨æ•°å€¤ã‚’フりãƒŧマットしぞす", - "delete": "削除", + "delete": "デバイ゚とã‚ĩãƒŧバãƒŧから削除", "delete_album": "ã‚ĸãƒĢバムを削除", "delete_api_key_prompt": "æœŦåŊ“ãĢこぎAPI キãƒŧを削除しぞすか?", - "delete_dialog_alert": "ã‚ĩãƒŧバãƒŧã¨ãƒ‡ãƒã‚¤ã‚šãŽä¸Ąæ–šã‹ã‚‰æ°¸äš…įš„ãĢ削除されぞすīŧ", - "delete_dialog_alert_local": "é¸æŠžã•ã‚ŒãŸé …į›Žã¯ãƒ‡ãƒã‚¤ã‚šã‹ã‚‰å‰Šé™¤ã•ã‚Œãžã™ãŒã€ImmichãĢは掋りぞす", - "delete_dialog_alert_local_non_backed_up": "é¸æŠžã•ã‚ŒãŸé …į›ŽãŽã†ãĄã€ImmichãĢバックã‚ĸップされãĻいãĒã„į‰ŠãŒåĢぞれãĻいぞす。デバイ゚からも厌全ãĢ削除されぞす。", - "delete_dialog_alert_remote": "é¸æŠžã•ã‚ŒãŸé …į›Žã¯Immichから永䚅ãĢ削除されぞす", + "delete_dialog_alert": "ã‚ĩãƒŧバãƒŧã¨ãƒ‡ãƒã‚¤ã‚šãŽä¸Ąæ–šã‹ã‚‰åŽŒå…¨ãĢ削除されぞす", + "delete_dialog_alert_local": "é¸æŠžã•ã‚ŒãŸé …į›Žã¯ãƒ‡ãƒã‚¤ã‚šã‹ã‚‰å‰Šé™¤ã•ã‚Œãžã™ãŒã€ã‚ĩãƒŧバãƒŧãĢは掋りぞす", + "delete_dialog_alert_local_non_backed_up": "é¸æŠžã•ã‚ŒãŸé …į›ŽãŽä¸­ãĢ、ã‚ĩãƒŧバãƒŧãĢバックã‚ĸップされãĻいãĒã„į‰ŠãŒåĢぞれãĻいぞす。そぎため、デバイ゚から厌全ãĢ削除されぞす。", + "delete_dialog_alert_remote": "é¸æŠžã•ã‚ŒãŸé …į›Žã¯ã‚ĩãƒŧバãƒŧから厌全ãĢ削除されぞす", "delete_dialog_ok_force": "削除しぞす", - "delete_dialog_title": "æ°¸äš…įš„ãĢ削除", + "delete_dialog_title": "厌全ãĢ削除", "delete_duplicates_confirmation": "æœŦåŊ“ãĢã“ã‚Œã‚‰ãŽé‡č¤‡ã‚’åŽŒå…¨ãĢ削除しぞすか?", "delete_face": "éĄ”ãŽå‰Šé™¤", "delete_key": "キãƒŧを削除", @@ -745,7 +760,6 @@ "direction": "斚向", "disabled": "į„ĄåŠš", "disallow_edits": "įˇ¨é›†ã‚’č¨ąå¯ã—ãĒい", - "discord": "Discord", "discover": "æŽĸį´ĸ", "dismiss_all_errors": "全ãĻぎエナãƒŧã‚’į„ĄčĻ–", "dismiss_error": "エナãƒŧã‚’į„ĄčĻ–", @@ -762,7 +776,7 @@ "download_enqueue": "ダã‚Ļãƒŗãƒ­ãƒŧド垅抟中", "download_error": "ダã‚Ļãƒŗãƒ­ãƒŧドエナãƒŧ", "download_failed": "ダã‚Ļãƒŗãƒ­ãƒŧãƒ‰å¤ąæ•—", - "download_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名: {}", + "download_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名: {filename}", "download_finished": "ダã‚Ļãƒŗãƒ­ãƒŧドįĩ‚äē†", "download_include_embedded_motion_videos": "埋めčžŧぞれた動į”ģ", "download_include_embedded_motion_videos_description": "åˆĨãƒ•ã‚Ąã‚¤ãƒĢとしãĻ、ãƒĸãƒŧã‚ˇãƒ§ãƒŗãƒ•ã‚ŠãƒˆãĢ埋めčžŧぞれた動į”ģをåĢめる", @@ -786,6 +800,8 @@ "edit_avatar": "ã‚ĸバã‚ŋãƒŧã‚’įˇ¨é›†", "edit_date": "æ—Ĩäģ˜ã‚’ᎍ集", "edit_date_and_time": "æ—Ĩæ™‚ã‚’įˇ¨é›†", + "edit_description": "čĒŦæ˜Žæ–‡ã‚’įˇ¨é›†", + "edit_description_prompt": "新しいčĒŦ明文を選んでください:", "edit_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’įˇ¨é›†", "edit_faces": "éĄ”ã‚’įˇ¨é›†", "edit_import_path": "ã‚¤ãƒŗãƒãƒŧãƒˆãƒ‘ã‚šã‚’įˇ¨é›†", @@ -806,26 +822,30 @@ "editor_crop_tool_h2_aspect_ratios": "ã‚ĸ゚ペクト比", "editor_crop_tool_h2_rotation": "回čģĸ", "email": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚", - "empty_folder": "This folder is empty", + "email_notifications": "EãƒĄãƒŧãƒĢ通įŸĨ", + "empty_folder": "こぎフりãƒĢダãƒŧはįŠēです", "empty_trash": "ã‚ŗãƒŸįŽąã‚’įŠēãĢする", "empty_trash_confirmation": "æœŦåŊ“ãĢã‚´ãƒŸįŽąã‚’įŠēãĢしぞすか? これãĢã‚ˆã‚Šã€ã‚´ãƒŸįŽąå†…ãŽã™ãšãĻぎã‚ĸã‚ģットが Immich から永䚅ãĢ削除されぞす。\nこぎ操äŊœã‚’å…ƒãĢæˆģすことはできぞせん!", "enable": "有劚化", + "enable_biometric_auth_description": "į”ŸäŊ“čĒč¨ŧを有劚化するためãĢ、PINã‚ŗãƒŧドをå…Ĩ力しãĻください", "enabled": "有劚", "end_date": "įĩ‚ä熿—Ĩ", - "enqueued": "Enqueued", + "enqueued": "順į•Ēåž…ãĄä¸­", "enter_wifi_name": "Wi-Fiぎ名前(SSID)をå…Ĩ力", + "enter_your_pin_code": "PINã‚ŗãƒŧドをå…Ĩ力しãĻください", + "enter_your_pin_code_subtitle": "éĩäģ˜ããƒ•りãƒĢダãƒŧį”¨ãŽPINã‚ŗãƒŧドをå…Ĩ力しãĻください", "error": "エナãƒŧ", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "ã‚ĸãƒĢãƒãƒ ãŽčĄ¨į¤ē順ぎ変更ãĢå¤ąæ•—ã—ãžã—ãŸ", "error_delete_face": "ã‚ĸã‚ģãƒƒãƒˆã‹ã‚‰éĄ”ãŽå‰Šé™¤ãŒã§ããžã›ã‚“ã§ã—ãŸ", "error_loading_image": "į”ģ像ぎčĒ­ãŋčžŧãŋエナãƒŧ", - "error_saving_image": "エナãƒŧ: {}", + "error_saving_image": "エナãƒŧ: {error}", "error_title": "エナãƒŧ - å•éĄŒãŒį™ēį”Ÿã—ãžã—ãŸ", "errors": { "cannot_navigate_next_asset": "æŦĄãŽã‚ĸã‚ģットãĢį§ģ動できぞせん", "cannot_navigate_previous_asset": "前ぎã‚ĸã‚ģットãĢį§ģ動できぞせん", "cant_apply_changes": "å¤‰æ›´ã‚’éŠį”¨ã§ããžã›ã‚“", "cant_change_activity": "ã‚ĸã‚¯ãƒ†ã‚Ŗãƒ“ãƒ†ã‚Ŗã‚’{enabled, select, true {į„ĄåŠšåŒ–} other {有劚化}}できぞせん", - "cant_change_asset_favorite": "ã‚ĸã‚ģットぎお気ãĢå…Ĩりを変更できぞせん", + "cant_change_asset_favorite": "é …į›ŽãŽãŠæ°—ãĢå…Ĩりを変更できぞせん", "cant_change_metadata_assets_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģãƒƒãƒˆãŽãƒĄã‚ŋデãƒŧã‚ŋを変更できぞせん", "cant_get_faces": "éĄ”ã‚’å–åž—ã§ããžã›ã‚“", "cant_get_number_of_comments": "ã‚ŗãƒĄãƒŗãƒˆæ•°ã‚’å–åž—ã§ããžã›ã‚“", @@ -845,13 +865,15 @@ "failed_to_create_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’äŊœæˆã§ããžã›ã‚“でした", "failed_to_edit_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’įˇ¨é›†ã§ããžã›ã‚“ã§ã—ãŸ", "failed_to_get_people": "äēēį‰Šã‚’å–åž—ã§ããžã›ã‚“ã§ã—ãŸ", - "failed_to_keep_this_delete_others": "ãģかぎã‚ĸã‚ģットを削除できぞせんでした", + "failed_to_keep_this_delete_others": "ã“ãŽé …į›ŽäģĨå¤–ãŽé …į›ŽãŽå‰Šé™¤ãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_load_asset": "ã‚ĸã‚ģットをčĒ­ãŋčžŧめぞせんでした", "failed_to_load_assets": "ã‚ĸã‚ģットをčĒ­ãŋčžŧめぞせんでした", + "failed_to_load_notifications": "通įŸĨぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_load_people": "äēēį‰Šã‚’čĒ­ãŋčžŧめぞせんでした", "failed_to_remove_product_key": "プロダクトキãƒŧを削除できぞせんでした", "failed_to_stack_assets": "ã‚ĸã‚ģットを゚ã‚ŋックできぞせんでした", "failed_to_unstack_assets": "ã‚ĸã‚ģットを゚ã‚ŋãƒƒã‚¯ã‹ã‚‰č§Ŗé™¤ã™ã‚‹ã“ã¨ãŒã§ããžã›ã‚“ã§ã—ãŸ", + "failed_to_update_notification_status": "通įŸĨ゚テãƒŧã‚ŋ゚ぎ更新ãĢå¤ąæ•—ã—ãžã—ãŸ", "import_path_already_exists": "ã“ãŽã‚¤ãƒŗãƒãƒŧトパ゚はæ—ĸãĢ存在しぞす。", "incorrect_email_or_password": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚ぞたはパ゚ワãƒŧãƒ‰ãŒé–“é•ãŖãĻいぞす", "paths_validation_failed": "{paths, plural, one {#個} other {#個}}ぎパ゚ぎ検č¨ŧãĢå¤ąæ•—ã—ãžã—ãŸ", @@ -865,11 +887,12 @@ "unable_to_add_import_path": "ã‚¤ãƒŗãƒãƒŧトパ゚をčŋŊ加できぞせん", "unable_to_add_partners": "パãƒŧトナãƒŧをčŋŊ加できぞせん", "unable_to_add_remove_archive": "ã‚ĸãƒŧã‚Ģイブ{archived, select, true {からã‚ĸã‚ģットを削除} other {ãĢã‚ĸã‚ģットをčŋŊ加}}できぞせん", - "unable_to_add_remove_favorites": "お気ãĢå…Ĩりを{favorite, select, true {ã‚ĸã‚ģットãĢčŋŊ加} other {ã‚ĸã‚ģットから削除}}できぞせん", + "unable_to_add_remove_favorites": "é …į›Žã‚’ãŠæ°—ãĢå…Ĩり{favorite, select, true {ãĢčŋŊ加} other {ãŽč§Ŗé™¤}}できぞせんでした", "unable_to_archive_unarchive": "{archived, select, true {ã‚ĸãƒŧã‚Ģイブ} other {ã‚ĸãƒŧã‚Ģã‚¤ãƒ–č§Ŗé™¤}}できぞせん", "unable_to_change_album_user_role": "ã‚ĸãƒĢバムぎãƒĻãƒŧã‚ļãƒŧロãƒŧãƒĢを変更できぞせん", "unable_to_change_date": "æ—Ĩäģ˜ã‚’変更できぞせん", - "unable_to_change_favorite": "ã‚ĸã‚ģットぎお気ãĢå…Ĩりを変更できぞせん", + "unable_to_change_description": "čĒŦ明文ぎ変更ãĢå¤ąæ•—ã—ãžã—ãŸ", + "unable_to_change_favorite": "お気ãĢå…Ĩりを変更できぞせんでした", "unable_to_change_location": "場所を変更できぞせん", "unable_to_change_password": "パ゚ワãƒŧドを変更できぞせん", "unable_to_change_visibility": "{count, plural, one {#äēē} other {#äēē}}ぎäēēį‰ŠãŽéžčĄ¨į¤ēč¨­åŽšã‚’å¤‰æ›´ã§ããžã›ã‚“", @@ -882,8 +905,8 @@ "unable_to_create_library": "ナイブナãƒĒをäŊœæˆã§ããžã›ã‚“", "unable_to_create_user": "ãƒĻãƒŧã‚ļãƒŧをäŊœæˆã§ããžã›ã‚“", "unable_to_delete_album": "ã‚ĸãƒĢバムを削除できぞせん", - "unable_to_delete_asset": "ã‚ĸã‚ģットを削除できぞせん", - "unable_to_delete_assets": "ã‚ĸã‚ģットを削除中ぎエナãƒŧ", + "unable_to_delete_asset": "é …į›Žã‚’å‰Šé™¤ã§ããžã›ã‚“", + "unable_to_delete_assets": "é …į›Žã‚’å‰Šé™¤ä¸­ãŽã‚¨ãƒŠãƒŧ", "unable_to_delete_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’å‰Šé™¤ã§ããžã›ã‚“", "unable_to_delete_import_path": "ã‚¤ãƒŗãƒãƒŧトパ゚を削除できぞせん", "unable_to_delete_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’å‰Šé™¤ã§ããžã›ã‚“", @@ -901,11 +924,12 @@ "unable_to_link_oauth_account": "OAuth ã‚ĸã‚Ģã‚Ļãƒŗãƒˆã‚’ãƒĒãƒŗã‚¯ã§ããžã›ã‚“", "unable_to_load_album": "ã‚ĸãƒĢバムをčĒ­ãŋčžŧめぞせん", "unable_to_load_asset_activity": "ã‚ĸã‚ģットぎã‚ĸã‚¯ãƒ†ã‚Ŗãƒ“ãƒ†ã‚Ŗã‚’čĒ­ãŋčžŧめぞせん", - "unable_to_load_items": "ã‚ĸイテムをčĒ­ãŋčžŧめぞせん", + "unable_to_load_items": "é …į›Žã‚’čĒ­ãŋčžŧめぞせん", "unable_to_load_liked_status": "いいねぎ゚テãƒŧã‚ŋ゚をčĒ­ãŋčžŧめぞせん", "unable_to_log_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļトできぞせん", "unable_to_log_out_device": "デバイ゚からログã‚ĸã‚Ļトできぞせん", "unable_to_login_with_oauth": "OAuth ã§ãƒ­ã‚°ã‚¤ãƒŗã§ããžã›ã‚“", + "unable_to_move_to_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧへぎį§ģ動ãĢå¤ąæ•—ã—ãžã—ãŸ", "unable_to_play_video": "動į”ģã‚’å†į”Ÿã§ããžã›ã‚“", "unable_to_reassign_assets_existing_person": "ã‚ĸã‚ģットを{name, select, null {æ—ĸ存ぎäēēį‰Š} other {{name}}}ãĢå†å‰˛ã‚ŠåŊ“ãĻできぞせん", "unable_to_reassign_assets_new_person": "ã‚ĸã‚ģットを新しいäēēį‰ŠãĢå†å‰˛ã‚ŠåŊ“ãĻできぞせん", @@ -919,6 +943,7 @@ "unable_to_remove_reaction": "ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗã‚’å‰Šé™¤ã§ããžã›ã‚“", "unable_to_repair_items": "ã‚ĸイテムをäŋŽåžŠã§ããžã›ã‚“", "unable_to_reset_password": "パ゚ワãƒŧドをãƒĒã‚ģットできぞせん", + "unable_to_reset_pin_code": "PINã‚ŗãƒŧドをãƒĒã‚ģットできぞせんでした", "unable_to_resolve_duplicate": "é‡č¤‡ã‚’č§Ŗæąēできぞせん", "unable_to_restore_assets": "ã‚ĸã‚ģットを垊元できぞせん", "unable_to_restore_trash": "ã‚´ãƒŸįŽąã‚’åžŠå…ƒã§ããžã›ã‚“", @@ -946,16 +971,15 @@ "unable_to_update_user": "ãƒĻãƒŧã‚ļãƒŧを更新できぞせん", "unable_to_upload_file": "ãƒ•ã‚Ąã‚¤ãƒĢをã‚ĸップロãƒŧドできぞせん" }, - "exif": "Exif", "exif_bottom_sheet_description": "čĒŦ明をčŋŊ加", "exif_bottom_sheet_details": "čŠŗį´°", "exif_bottom_sheet_location": "æ’ŽåŊąå ´æ‰€", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "名前をčŋŊ加", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "{age}æ­ŗ", + "exif_bottom_sheet_person_age_months": "į”ŸåžŒ{months}ãƒļ月", + "exif_bottom_sheet_person_age_year_months": "1æ­ŗ{months}ãƒļ月", + "exif_bottom_sheet_person_age_years": "{years}æ­ŗ", "exit_slideshow": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧをįĩ‚わる", "expand_all": "全ãĻåą•é–‹", "experimental_settings_new_asset_list_subtitle": "čŖŊäŊœé€”中 (WIP)", @@ -975,13 +999,14 @@ "external_network": "外部ぎネットワãƒŧク", "external_network_sheet_info": "指厚されたWi-FiãĢįš‹ãŒãŖãĻいãĒい時ã‚ĸプãƒĒはã‚ĩãƒŧバãƒŧへぎæŽĨįļšã‚’指厚されたURLã§čĄŒã„ãžã™ã€‚å„Ē先順äŊã¯ä¸Šã‹ã‚‰ä¸‹ã§ã™", "face_unassigned": "æœĒå‰˛ã‚ŠåŊ“ãĻ", - "failed": "Failed", + "failed": "å¤ąæ•—", + "failed_to_authenticate": "čĒč¨ŧãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_load_assets": "ã‚ĸã‚ģットぎロãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸ", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "フりãƒĢダãƒŧぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—", "favorite": "お気ãĢå…Ĩり", - "favorite_or_unfavorite_photo": "å†™įœŸã‚’ãŠæ°—ãĢå…Ĩりぞたはお気ãĢå…Ĩã‚Šč§Ŗé™¤", + "favorite_or_unfavorite_photo": "å†™įœŸã‚’ãŠæ°—ãĢいりãĢį™ģéŒ˛ãžãŸã¯č§Ŗé™¤", "favorites": "お気ãĢå…Ĩり", - "favorites_page_no_favorites": "お気ãĢå…Ĩりį™ģéŒ˛ã•ã‚ŒãŸå†™įœŸãžãŸã¯ãƒ“ãƒ‡ã‚Ēがありぞせん", + "favorites_page_no_favorites": "お気ãĢå…Ĩりį™ģéŒ˛ã•ã‚ŒãŸé …į›ŽãŒã‚ã‚Šãžã›ã‚“", "feature_photo_updated": "äēēį‰Šį”ģ像が更新されぞした", "features": "抟čƒŊ", "features_setting_description": "ã‚ĸプãƒĒぎ抟čƒŊã‚’įŽĄį†ã™ã‚‹", @@ -991,10 +1016,11 @@ "filetype": "ãƒ•ã‚Ąã‚¤ãƒĢã‚ŋイプ", "filter": "ãƒ•ã‚ŖãƒĢã‚ŋãƒŧ", "filter_people": "äēēį‰Šã‚’įĩžã‚Ščžŧãŋ", + "filter_places": "å ´æ‰€ã‚’ãƒ•ã‚ŖãƒĢã‚ŋãƒŧ", "find_them_fast": "名前で検į´ĸしãĻį´ æ—Šãį™ēčĻ‹", "fix_incorrect_match": "é–“é•ãŖãŸä¸€č‡´ã‚’äŋŽæ­Ŗ", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "フりãƒĢダãƒŧ", + "folder_not_found": "フりãƒĢダãƒŧがčĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸ", "folders": "フりãƒĢダ", "folders_feature_description": "ãƒ•ã‚Ąã‚¤ãƒĢã‚ˇã‚šãƒ†ãƒ ä¸ŠãŽå†™įœŸã¨å‹•į”ģぎフりãƒĢダビãƒĨãƒŧã‚’é–˛čĻ§ã™ã‚‹", "forward": "前へ", @@ -1036,14 +1062,17 @@ "home_page_archive_err_partner": "パãƒŧトナãƒŧãŽå†™įœŸã¯ã‚ĸãƒŧã‚Ģイブできぞせん。゚キップしぞす", "home_page_building_timeline": "ã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗæ§‹į¯‰ä¸­", "home_page_delete_err_partner": "パãƒŧトナãƒŧãŽå†™įœŸã¯å‰Šé™¤ã§ããžã›ã‚“ã€‚ã‚šã‚­ãƒƒãƒ—ã—ãžã™", - "home_page_delete_remote_err_local": "ã‚ĩãƒŧバãƒŧ上ぎã‚ĸイテムぎ削除ぎ選択ãĢį̝æœĢ上ぎã‚ĸイテムがåĢぞれãĻいるぎで゚キップしぞす", + "home_page_delete_remote_err_local": "ã‚ĩãƒŧバãƒŧ上ぎã‚ĸイテムぎ削除ぎ選択ãĢデバイ゚上ぎã‚ĸイテムがåĢぞれãĻいるぎで゚キップしぞす", "home_page_favorite_err_local": "ぞだã‚ĸップロãƒŧドされãĻãĒã„é …į›Žã¯ãŠæ°—ãĢå…Ĩりį™ģéŒ˛ã§ããžã›ã‚“", - "home_page_favorite_err_partner": "ぞだパãƒŧトナãƒŧãŽå†™įœŸã‚’ãŠæ°—ãĢå…Ĩりį™ģéŒ˛ã§ããžã›ã‚“ã€‚ã‚šã‚­ãƒƒãƒ—ã—ãžã™ (ã‚ĸップデãƒŧãƒˆã‚’ãŠåž…ãĄãã ã•ã„)", + "home_page_favorite_err_partner": "パãƒŧトナãƒŧãŽå†™įœŸã¯ãŠæ°—ãĢå…Ĩりį™ģéŒ˛ã§ããžã›ã‚“ã€‚ã‚šã‚­ãƒƒãƒ—ã—ãžã™", "home_page_first_time_notice": "はじめãĻã‚ĸプãƒĒをäŊŋう場合、ã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗãĢå†™įœŸã‚’čĄ¨į¤ēするためãĢã‚ĸãƒĢバムを選択しãĻください", + "home_page_locked_error_local": "デバイ゚上ãĢしかãĒã„é …į›Žã‚’éĩäģ˜ããƒ•りãƒĢダãƒŧãĢį§ģ動することはできぞせん。゚キップしぞす", + "home_page_locked_error_partner": "パãƒŧトナãƒŧãŽé …į›Žã‚’éĩäģ˜ããƒ•りãƒĢダãƒŧãĢį§ģ動することはできぞせん。゚キップしぞす", "home_page_share_err_local": "ロãƒŧã‚ĢãƒĢぎãŋãŽé …į›Žã‚’ãƒĒãƒŗã‚¯ã§å…ąæœ‰ã¯ã§ããžã›ã‚“ã€‚ã‚šã‚­ãƒƒãƒ—ã—ãžã™", "home_page_upload_err_limit": "1回でã‚ĸップロãƒŧãƒ‰ã§ãã‚‹å†™įœŸãŽæ•°ã¯30枚です。゚キップしぞす", "host": "ポト", "hour": "時間", + "id": "ID", "ignore_icloud_photos": "iCloudä¸ŠãŽå†™įœŸã‚’ã‚šã‚­ãƒƒãƒ—", "ignore_icloud_photos_description": "iCloudãĢäŋå­˜æ¸ˆãŋãŽé …į›Žã‚’Immichã‚ĩãƒŧバãƒŧ上ãĢã‚ĸップロãƒŧドしぞせん", "image": "å†™įœŸ", @@ -1071,7 +1100,7 @@ "include_shared_albums": "å…ąæœ‰ã‚ĸãƒĢバムをåĢめる", "include_shared_partner_assets": "パãƒŧトナãƒŧãŒã‚ˇã‚§ã‚ĸしたã‚ĸã‚ģットをåĢめる", "individual_share": "1æžšãŽå…ąæœ‰", - "individual_shares": "個äēēãŽå…ąæœ‰", + "individual_shares": "1æžšãšã¤ãŽå…ąæœ‰", "info": "æƒ…å ą", "interval": { "day_at_onepm": "毎æ—Ĩ午垌1時", @@ -1095,7 +1124,7 @@ "last_seen": "最新ぎæ´ģ動", "latest_version": "最新バãƒŧã‚¸ãƒ§ãƒŗ", "latitude": "᎝åēĻ", - "leave": "標é̘", + "leave": "退å‡ē", "lens_model": "ãƒŦãƒŗã‚ēãƒĸデãƒĢ", "let_others_respond": "äģ–ぎãƒĻãƒŧã‚ļãƒŧぎčŋ”äŋĄã‚’č¨ąå¯ã™ã‚‹", "level": "ãƒŦベãƒĢ", @@ -1125,6 +1154,8 @@ "location_picker_latitude_hint": "᎝åēĻをå…Ĩ力", "location_picker_longitude_error": "有劚ãĒįĩŒåēĻをå…Ĩ力しãĻください", "location_picker_longitude_hint": "įĩŒåēĻをå…Ĩ力", + "lock": "ロック", + "locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧ", "log_out": "ログã‚ĸã‚Ļト", "log_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļト", "logged_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļトしぞした", @@ -1134,7 +1165,6 @@ "login_form_api_exception": "APIエナãƒŧがį™ēį”Ÿã—ãžã—ãŸã€‚URLをチェックしãĻもう一åēĻおčŠĻしください。", "login_form_back_button_text": "æˆģる", "login_form_email_hint": "hoge@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "ã‚ĩãƒŧバãƒŧãŽã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆURL", "login_form_err_http": "http://かhttps://かを指厚しãĻください", "login_form_err_invalid_email": "ãƒĄãƒŧãƒĢã‚ĸドãƒŦã‚šãŒį„ĄåŠšã§ã™", @@ -1169,8 +1199,8 @@ "manage_your_devices": "ãƒ­ã‚°ã‚¤ãƒŗãƒ‡ãƒã‚¤ã‚šã‚’įŽĄį†ã—ãžã™", "manage_your_oauth_connection": "OAuthæŽĨįļšã‚’įŽĄį†ã—ãžã™", "map": "åœ°å›ŗ", - "map_assets_in_bound": "{}é …į›Ž", - "map_assets_in_bounds": "{}é …į›Ž", + "map_assets_in_bound": "{count}枚", + "map_assets_in_bounds": "{count}枚", "map_cannot_get_user_location": "äŊįŊŽæƒ…å ąãŒã‚˛ãƒƒãƒˆã§ããžã›ã‚“", "map_location_dialog_yes": "はい", "map_location_picker_page_use_location": "こぎäŊįŊŽæƒ…å ąã‚’äŊŋう", @@ -1184,25 +1214,26 @@ "map_settings": "ãƒžãƒƒãƒ—ãŽč¨­åŽš", "map_settings_dark_mode": "ダãƒŧクãƒĸãƒŧド", "map_settings_date_range_option_day": "過åŽģ24時間", - "map_settings_date_range_option_days": "過åŽģ{}æ—Ĩ間", + "map_settings_date_range_option_days": "過åŽģ{days}æ—Ĩ間", "map_settings_date_range_option_year": "過åŽģ1åš´é–“", - "map_settings_date_range_option_years": "過åŽģ{}åš´é–“", + "map_settings_date_range_option_years": "過åŽģ{years}åš´é–“", "map_settings_dialog_title": "ãƒžãƒƒãƒ—ãŽč¨­åŽš", "map_settings_include_show_archived": "ã‚ĸãƒŧã‚Ģイブ済ãŋをåĢめる", "map_settings_include_show_partners": "パãƒŧトナãƒŧをåĢめる", "map_settings_only_show_favorites": "お気ãĢå…Ĩりぎãŋã‚’čĄ¨į¤ē", "map_settings_theme_settings": "åœ°å›ŗãŽčĻ‹ãŸį›Ž", "map_zoom_to_see_photos": "å†™įœŸã‚’čĻ‹ã‚‹ãĢはã‚ēãƒŧムã‚ĸã‚Ļト", + "mark_all_as_read": "すずãĻæ—ĸčĒ­ãĢする", + "mark_as_read": "æ—ĸčĒ­ãĢする", + "marked_all_as_read": "すずãĻæ—ĸčĒ­ãĢしぞした", "matches": "マッチ", "media_type": "ãƒĄãƒ‡ã‚Ŗã‚ĸã‚ŋイプ", "memories": "ãƒĄãƒĸãƒĒãƒŧ", - "memories_all_caught_up": "すずãĻįĸēčĒæ¸ˆãŋ", - "memories_check_back_tomorrow": "明æ—Ĩもう一åēĻįĸēčĒã—ãĻください", + "memories_all_caught_up": "これで全部です", + "memories_check_back_tomorrow": "ぞた明æ—Ĩ、čĻ‹ãĢæĨãĻくださいね", "memories_setting_description": "ãƒĄãƒĸãƒĒãƒŧãŽå†…åŽšã‚’įŽĄį†ã—ãžã™", - "memories_start_over": "始める", + "memories_start_over": "もう一åēĻčĻ‹ã‚‹", "memories_swipe_to_close": "上ãĢ゚ワイプしãĻ閉じる", - "memories_year_ago": "一嚴前", - "memories_years_ago": "{}嚴前", "memory": "ãƒĄãƒĸãƒĒãƒŧ", "memory_lane_title": "思いå‡ē {title}", "menu": "ãƒĄãƒ‹ãƒĨãƒŧ", @@ -1219,6 +1250,12 @@ "month": "月", "monthly_title_text_date_format": "yyyy MM", "more": "ã‚‚ãŖã¨čĄ¨į¤ē", + "move": "į§ģ動", + "move_off_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧからå‡ēす", + "move_to_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧへį§ģ動", + "move_to_locked_folder_confirmation": "ã“ã‚Œã‚‰ãŽå†™įœŸã‚„å‹•į”ģはすずãĻぎã‚ĸãƒĢバムから外され、éĩäģ˜ããƒ•りãƒĢダãƒŧ内でぎãŋ閲čĻ§å¯čƒŊãĢãĒりぞす", + "moved_to_archive": "{count, plural, one {#} other {#}}é …į›Žã‚’ã‚ĸãƒŧã‚Ģイブしぞした", + "moved_to_library": "{count, plural, one {#} other {#}}é …į›Žã‚’ãƒŠã‚¤ãƒ–ãƒŠãƒĒãĢį§ģ動しぞした", "moved_to_trash": "ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "multiselect_grid_edit_date_time_err_read_only": "čĒ­ãŋå–ã‚Šå°‚į”¨ãŽé …į›ŽãŽæ—Ĩäģ˜ã‚’変更できぞせん", "multiselect_grid_edit_gps_err_read_only": "čĒ­ãŋå–ã‚Šå°‚į”¨ãŽé …į›ŽãŽäŊįŊŽæƒ…å ąã‚’å¤‰æ›´ã§ããžã›ã‚“", @@ -1233,6 +1270,8 @@ "new_api_key": "新しいAPI キãƒŧ", "new_password": "新しいパ゚ワãƒŧド", "new_person": "新しいäēēį‰Š", + "new_pin_code": "新しいPINã‚ŗãƒŧド", + "new_pin_code_subtitle": "éĩäģ˜ããƒ•りãƒĢダãƒŧã‚’åˆŠį”¨ã™ã‚‹ãŽãŒåˆã‚ãĻぎようです。PINã‚ŗãƒŧドをäŊœæˆã—ãĻください", "new_user_created": "新しいãƒĻãƒŧã‚ļãƒŧがäŊœæˆã•れぞした", "new_version_available": "新しいバãƒŧã‚¸ãƒ§ãƒŗãŒåˆŠį”¨å¯čƒŊ", "newest_first": "最新順", @@ -1248,17 +1287,21 @@ "no_duplicates_found": "é‡č¤‡ã¯čĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸã€‚", "no_exif_info_available": "exifæƒ…å ąãŒåˆŠį”¨ã§ããžã›ã‚“", "no_explore_results_message": "ã‚ŗãƒŦã‚¯ã‚ˇãƒ§ãƒŗã‚’æŽĸį´ĸするãĢはさらãĢå†™įœŸã‚’ã‚ĸップロãƒŧドしãĻください。", - "no_favorites_message": "お気ãĢå…ĨりãĢčŋŊ加すると最éĢ˜ãŽå†™įœŸã‚„å‹•į”ģをすぐãĢčĻ‹ã¤ã‘ã‚‰ã‚Œãžã™", + "no_favorites_message": "お気ãĢå…Ĩりį™ģéŒ˛ã™ã‚‹ã¨åĨŊきãĒå†™įœŸã‚„å‹•į”ģをすぐãĢčĻ‹ã¤ã‘ã‚‰ã‚Œãžã™", "no_libraries_message": "あãĒãŸãŽå†™įœŸã‚„å‹•į”ģã‚’čĄ¨į¤ēするためぎ外部ナイブナãƒĒをäŊœæˆã—ぞしょう", + "no_locked_photos_message": "éĩäģ˜ããƒ•りãƒĢダãƒŧå†…ãŽå†™įœŸã‚„å‹•į”ģは通常ぎナイブナãƒĒから隠されぞす。", "no_name": "名前ãĒし", + "no_notifications": "通įŸĨãĒし", + "no_people_found": "ä¸€č‡´ã™ã‚‹äēēį‰ŠãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", "no_places": "場所ãĒし", "no_results": "įĩæžœãŒã‚りぞせん", "no_results_description": "åŒįžŠčĒžã‚„ã‚ˆã‚Šä¸€čˆŦįš„ãĒキãƒŧワãƒŧドをčŠĻしãĻください", "no_shared_albums_message": "ã‚ĸãƒĢバムをäŊœæˆã—ãĻå†™įœŸã‚„å‹•į”ģã‚’å…ąæœ‰ã—ãžã—ã‚‡ã†", "not_in_any_album": "おぎã‚ĸãƒĢバムãĢもå…ĨãŖãĻいãĒい", - "not_selected": "Not selected", + "not_selected": "選択ãĒし", "note_apply_storage_label_to_previously_uploaded assets": "æŗ¨æ„: äģĨ前ãĢã‚ĸップロãƒŧドしたã‚ĸã‚ģットãĢ゚トãƒŦãƒŧジナベãƒĢã‚’éŠį”¨ã™ã‚‹ãĢはäģĨä¸‹ã‚’åŽŸčĄŒã—ãĻください", "notes": "æŗ¨æ„", + "nothing_here_yet": "ぞだäŊ•ã‚‚į„Ąã„ã‚ˆã†ã§ã™", "notification_permission_dialog_content": "通įŸĨã‚’č¨ąå¯ã™ã‚‹ãĢã¯č¨­åŽšã‚’é–‹ã„ãĻã‚ĒãƒŗãĢしãĻください", "notification_permission_list_tile_content": "通įŸĨãŽč¨ąå¯ をã‚ĒãƒŗãĢしãĻください", "notification_permission_list_tile_enable_button": "通įŸĨをã‚ĒãƒŗãĢする", @@ -1266,7 +1309,6 @@ "notification_toggle_setting_description": "ãƒĄãƒŧãƒĢ通įŸĨを有劚化", "notifications": "通įŸĨ", "notifications_setting_description": "通įŸĨã‚’įŽĄį†ã—ãžã™", - "oauth": "OAuth", "official_immich_resources": "å…ŦåŧImmichãƒĒã‚Ŋãƒŧ゚", "offline": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗ", "offline_paths": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽãƒ‘ã‚š", @@ -1281,6 +1323,7 @@ "onboarding_welcome_user": "ようこそ、{user} さん", "online": "ã‚ĒãƒŗãƒŠã‚¤ãƒŗ", "only_favorites": "お気ãĢå…Ĩりぎãŋ", + "open": "開く", "open_in_map_view": "åœ°å›ŗčĄ¨į¤ēでčĻ‹ã‚‹", "open_in_openstreetmap": "OpenStreetMapで開く", "open_the_search_filters": "検į´ĸãƒ•ã‚ŖãƒĢã‚ŋを開く", @@ -1304,7 +1347,7 @@ "partner_page_partner_add_failed": "パãƒŧトナãƒŧぎčŋŊ加ãĢå¤ąæ•—", "partner_page_select_partner": "パãƒŧトナãƒŧを選択", "partner_page_shared_to_title": "æŦĄãŽãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã—ãžã™: ", - "partner_page_stop_sharing_content": "{}ã¯å†™įœŸã¸ã‚ĸクã‚ģ゚できãĒくãĒりぞす", + "partner_page_stop_sharing_content": "{partner}はäģŠåžŒã‚ãĒãŸãŽå†™įœŸã¸ã‚ĸクã‚ģ゚できãĒくãĒりぞす", "partner_sharing": "パãƒŧãƒˆãƒŠã¨ãŽå…ąæœ‰", "partners": "パãƒŧトナãƒŧ", "password": "パ゚ワãƒŧド", @@ -1329,10 +1372,10 @@ "permanent_deletion_warning": "永䚅削除ぎč­Ļ告", "permanent_deletion_warning_setting_description": "ã‚ĸã‚ģットを厌全ãĢ削除するときãĢč­Ļå‘Šã‚’čĄ¨į¤ēする", "permanently_delete": "厌全ãĢ削除", - "permanently_delete_assets_count": "{count, plural, one {ã‚ĸã‚ģット} other {ã‚ĸã‚ģット}}を厌全ãĢ削除", + "permanently_delete_assets_count": "{count, plural, one {#} other {#}}é …į›Žã‚’åŽŒå…¨ãĢ削除", "permanently_delete_assets_prompt": "æœŦåŊ“ãĢ{count, plural, one {こぎã‚ĸã‚ģット} other {これらぎ#個ぎã‚ĸã‚ģット}}を厌全ãĢ削除しぞすか? これãĢより {count, plural, one {こぎã‚ĸã‚ģット} other {これらぎã‚ĸã‚ģット}}はã‚ĸãƒĢバムからも削除されぞす。", - "permanently_deleted_asset": "ã‚ĸã‚ģットを厌全ãĢ削除しぞした", - "permanently_deleted_assets_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを厌全ãĢ削除しぞした", + "permanently_deleted_asset": "é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", + "permanently_deleted_assets_count": "{count, plural, one {#個} other {#個}}ãŽé …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", "permission_onboarding_back": "æˆģる", "permission_onboarding_continue_anyway": "į„ĄčĻ–ã—ãĻįļščĄŒ", "permission_onboarding_get_started": "はじめる", @@ -1350,6 +1393,10 @@ "photos_count": "{count, plural, one {{count, number}æžšãŽå†™įœŸ} other {{count, number}æžšãŽå†™įœŸ}}", "photos_from_previous_years": "äģĨå‰ãŽåš´ãŽå†™įœŸ", "pick_a_location": "場所を選択", + "pin_code_changed_successfully": "PINã‚ŗãƒŧドを変更しぞした", + "pin_code_reset_successfully": "PINã‚ŗãƒŧドをãƒĒã‚ģットしぞした", + "pin_code_setup_successfully": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップしぞした", + "pin_verification": "PINã‚ŗãƒŧドčĒč¨ŧ", "place": "場所", "places": "æ’ŽåŊąå ´æ‰€", "places_count": "{count, plural, other {{count, number}įŽ‡æ‰€}}", @@ -1357,6 +1404,7 @@ "play_memories": "ãƒĄãƒĸãƒĒãƒŧã‚’å†į”Ÿ", "play_motion_photo": "ãƒĸãƒŧã‚ˇãƒ§ãƒŗãƒ“ãƒ‡ã‚Ēã‚’å†į”Ÿ", "play_or_pause_video": "動į”ģã‚’å†į”ŸãžãŸã¯ä¸€æ™‚åœæ­ĸ", + "please_auth_to_access": "ã‚ĸクã‚ģ゚するãĢはčĒč¨ŧがåŋ…čĻã§ã™", "port": "ポãƒŧトãƒŦãƒŧト", "preferences_settings_subtitle": "ã‚ĸプãƒĒãĢé–ĸã™ã‚‹č¨­åŽš", "preferences_settings_title": "č¨­åŽš", @@ -1367,11 +1415,11 @@ "previous_or_next_photo": "前ぞたはæŦĄãŽå†™įœŸ", "primary": "最å„Ē先", "privacy": "ãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ", + "profile": "ãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢ", "profile_drawer_app_logs": "ログ", "profile_drawer_client_out_of_date_major": "ã‚ĸプãƒĒが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_drawer_client_out_of_date_minor": "ã‚ĸプãƒĒが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_drawer_client_server_up_to_date": "すずãĻæœ€æ–°į‰ˆã§ã™", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "ã‚ĩãƒŧバãƒŧが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_drawer_server_out_of_date_minor": "ã‚ĩãƒŧバãƒŧが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_image_of_user": "{user} ãŽãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢį”ģ像", @@ -1380,7 +1428,7 @@ "public_share": "å…Ŧé–‹å…ąæœ‰", "purchase_account_info": "ã‚ĩポãƒŧã‚ŋãƒŧ", "purchase_activated_subtitle": "Immich とã‚Ēãƒŧãƒ—ãƒŗã‚Ŋãƒŧ゚ ã‚Ŋフトã‚Ļェã‚ĸを支援しãĻいただきありがとうございぞす", - "purchase_activated_time": "{date, date}ãĢã‚ĸã‚¯ãƒ†ã‚Ŗãƒ™ãƒŧト", + "purchase_activated_time": "{date}ãĢã‚ĸã‚¯ãƒ†ã‚Ŗãƒ™ãƒŧト", "purchase_activated_title": "キãƒŧã¯æ­Ŗå¸¸ãĢã‚ĸã‚¯ãƒ†ã‚Ŗãƒ™ãƒŧトされぞした", "purchase_button_activate": "ã‚ĸã‚¯ãƒ†ã‚Ŗãƒ™ãƒŧト", "purchase_button_buy": "čŗŧå…Ĩ", @@ -1425,6 +1473,8 @@ "recent_searches": "最čŋ‘ぎ検į´ĸ", "recently_added": "最čŋ‘čŋŊåŠ ã•ã‚ŒãŸé …į›Ž", "recently_added_page_title": "最čŋ‘", + "recently_taken": "最čŋ‘撎られたもぎ", + "recently_taken_page_title": "最čŋ‘ぎ撎åŊą", "refresh": "更新", "refresh_encoded_videos": "ã‚¨ãƒŗã‚ŗãƒŧドされた動į”ģを更新", "refresh_faces": "顔čĒč­˜ã‚’æ›´æ–°", @@ -1443,19 +1493,21 @@ "remove_custom_date_range": "ã‚Ģ゚ã‚ŋムæ—Ĩäģ˜į¯„å›˛ã‚’å‰Šé™¤", "remove_deleted_assets": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽãƒ•ã‚Ąã‚¤ãƒĢを削除", "remove_from_album": "ã‚ĸãƒĢバムから削除", - "remove_from_favorites": "お気ãĢå…Ĩりから削除", + "remove_from_favorites": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", + "remove_from_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧから取り除く", + "remove_from_locked_folder_confirmation": "é¸æŠžã—ãŸå†™įœŸãƒģ動į”ģをéĩäģ˜ããƒ•りãƒĢダãƒŧぎ外ãĢå‡ēしãĻよろしいですかīŧŸãƒŠã‚¤ãƒ–ナãƒĒãĢå†ãŗčĄ¨į¤ēされるようãĢãĒりぞす", "remove_from_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‹ã‚‰å‰Šé™¤", "remove_memory": "ãƒĄãƒĸãƒĒãƒŧぎ削除", "remove_photo_from_memory": "ãƒĄãƒĸãƒĒãƒŧã‹ã‚‰å†™įœŸã‚’å‰Šé™¤", "remove_url": "URLぎ削除", "remove_user": "ãƒĻãƒŧã‚ļãƒŧを削除", "removed_api_key": "削除されたAPI キãƒŧ: {name}", - "removed_from_archive": "ã‚ĸãƒŧã‚Ģイブから削除されぞした", - "removed_from_favorites": "お気ãĢå…Ĩりから削除しぞした", - "removed_from_favorites_count": "{count, plural, other {#é …į›Ž}}お気ãĢå…Ĩりから削除しぞした", + "removed_from_archive": "ã‚ĸãƒŧã‚Ģイブから外しぞした", + "removed_from_favorites": "お気ãĢå…Ĩã‚Šã‚’č§Ŗé™¤ã—ãžã—ãŸ", + "removed_from_favorites_count": "{count, plural, other {#é …į›Ž}}をお気ãĢå…Ĩりから外しぞした", "removed_memory": "å‰Šé™¤ã•ã‚ŒãŸãƒĄãƒĸãƒĒãƒŧ", "removed_photo_from_memory": "ãƒĄãƒĸãƒĒãƒŧã‹ã‚‰å‰Šé™¤ã•ã‚ŒãŸå†™įœŸ", - "removed_tagged_assets": "{count, plural, one {#個ぎã‚ĸã‚ģット} other {#個ぎã‚ĸã‚ģット}}からã‚ŋグを削除しぞした", + "removed_tagged_assets": "{count, plural, one {#é …į›Ž} other {#é …į›Ž}}からã‚ŋグを外しぞした", "rename": "ãƒĒネãƒŧム", "repair": "äŋŽåžŠ", "repair_no_results_message": "čŋŊčˇĄã•ã‚ŒãĻいãĒã„ãƒ•ã‚Ąã‚¤ãƒĢや存在しãĒã„ãƒ•ã‚Ąã‚¤ãƒĢがここãĢ襨į¤ēされぞす", @@ -1467,13 +1519,14 @@ "reset": "ãƒĒã‚ģット", "reset_password": "パ゚ワãƒŧドをãƒĒã‚ģット", "reset_people_visibility": "äēēį‰ŠãŽéžčĄ¨į¤ēč¨­åŽšã‚’ãƒĒã‚ģット", + "reset_pin_code": "PINã‚ŗãƒŧドをãƒĒã‚ģット", "reset_to_default": "デフりãƒĢトãĢãƒĒã‚ģット", "resolve_duplicates": "é‡č¤‡ã‚’č§Ŗæąēする", "resolved_all_duplicates": "全ãĻãŽé‡č¤‡ã‚’č§Ŗæąēしぞした", "restore": "垊元", "restore_all": "全ãĻ垊元", "restore_user": "ãƒĻãƒŧã‚ļãƒŧを垊元", - "restored_asset": "ã‚ĸã‚ģットを垊元しぞした", + "restored_asset": "é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", "resume": "再開", "retry_upload": "ã‚ĸップロãƒŧドを再čŠĻ行", "review_duplicates": "é‡č¤‡ã‚’čĒŋæŸģ", @@ -1509,7 +1562,7 @@ "search_filter_date_title": "æ’ŽåŊ࿜Ÿé–“を選択", "search_filter_display_option_not_in_album": "ã‚ĸãƒĢバムãĢありぞせん", "search_filter_display_options": "襨į¤ēã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", - "search_filter_filename": "Search by file name", + "search_filter_filename": "ãƒ•ã‚Ąã‚¤ãƒĢ名で検į´ĸ", "search_filter_location": "場所", "search_filter_location_title": "場所を選択", "search_filter_media_type": "ãƒĄãƒ‡ã‚Ŗã‚ĸãŽį¨ŽéĄž", @@ -1517,17 +1570,17 @@ "search_filter_people_title": "äēēį‰Šã‚’é¸æŠž", "search_for": "検į´ĸ", "search_for_existing_person": "æ—ĸ存ぎäēēį‰Šã‚’æ¤œį´ĸ", - "search_no_more_result": "No more results", + "search_no_more_result": "検į´ĸįĩæžœäģĨ上", "search_no_people": "äēēį‰ŠãŒã„ãžã›ã‚“", "search_no_people_named": "「{name}」という名前ぎäēēį‰ŠãŒã„ãžã›ã‚“", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "検į´ĸįĩæžœãĒし", "search_options": "検į´ĸã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "search_page_categories": "ã‚ĢテゴãƒĒ", "search_page_motion_photos": "ãƒĸãƒŧã‚ˇãƒ§ãƒŗãƒ•ã‚Šãƒˆ", "search_page_no_objects": "čĸĢ写äŊ“ãĢé–ĸするデãƒŧã‚ŋがãĒし", "search_page_no_places": "場所ãĢé–ĸするデãƒŧã‚ŋãĒし", "search_page_screenshots": "゚クãƒĒãƒŧãƒŗã‚ˇãƒ§ãƒƒãƒˆ", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "å†™įœŸã‚„å‹•į”ģを検į´ĸ", "search_page_selfies": "č‡Ē撎り", "search_page_things": "čĸĢ写äŊ“", "search_page_view_all_button": "すずãĻ襨į¤ē", @@ -1559,6 +1612,7 @@ "select_keep_all": "全ãĻäŋæŒ", "select_library_owner": "ナイブナãƒĒæ‰€æœ‰č€…ã‚’é¸æŠž", "select_new_face": "æ–°ã—ã„éĄ”ã‚’é¸æŠž", + "select_person_to_tag": "ã‚ŋグをäģ˜ã‘ã‚‹äēēį‰Šã‚’é¸ã‚“ã§ãã ã•ã„", "select_photos": "å†™įœŸã‚’é¸æŠž", "select_trash_all": "全ãĻ削除", "select_user_for_sharing_page_err_album": "ã‚ĸãƒĢバムäŊœæˆãĢå¤ąæ•—", @@ -1589,67 +1643,68 @@ "setting_languages_apply": "éŠį”¨ã™ã‚‹", "setting_languages_subtitle": "ã‚ĸプãƒĒãŽč¨€čĒžã‚’å¤‰æ›´ã™ã‚‹", "setting_languages_title": "言čĒž", - "setting_notifications_notify_failures_grace_period": "バックã‚ĸãƒƒãƒ—å¤ąæ•—ãŽé€šįŸĨ: {}", - "setting_notifications_notify_hours": "{}時間垌", + "setting_notifications_notify_failures_grace_period": "バックグナã‚Ļãƒŗãƒ‰ãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—å¤ąæ•—ãŽé€šįŸĨ: {duration}", + "setting_notifications_notify_hours": "{count}時間垌", "setting_notifications_notify_immediately": "すぐãĢčĄŒã†", - "setting_notifications_notify_minutes": "{}分垌", + "setting_notifications_notify_minutes": "{count}分垌", "setting_notifications_notify_never": "čĄŒã‚ãĒい", - "setting_notifications_notify_seconds": "{}į§’åžŒ", + "setting_notifications_notify_seconds": "{count}į§’åžŒ", "setting_notifications_single_progress_subtitle": "ã‚ĸップロãƒŧãƒ‰ä¸­ãŽå†™įœŸãŽčŠŗį´°", "setting_notifications_single_progress_title": "バックã‚ĸãƒƒãƒ—ãŽčŠŗį´°ãĒ進行įŠļæŗã‚’čĄ¨į¤ē", "setting_notifications_subtitle": "通įŸĨč¨­åŽšã‚’å¤‰æ›´ã™ã‚‹", "setting_notifications_total_progress_subtitle": "ã‚ĸップロãƒŧãƒ‰ãŽé€˛čĄŒįŠļæŗ (厌ä熿¸ˆãŋ/全äŊ“æžšæ•°)", "setting_notifications_total_progress_title": "全äŊ“ぎバックã‚ĸãƒƒãƒ—ãŽé€˛čĄŒįŠļæŗã‚’čĄ¨į¤ē", - "setting_video_viewer_looping_title": "ãƒĢãƒŧプ中", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_looping_title": "動į”ģをãƒĢãƒŧプする", + "setting_video_viewer_original_video_subtitle": "動į”ģを゚トãƒĒãƒŧãƒŸãƒŗã‚°ã™ã‚‹éš›ãĢã€ãƒˆãƒŠãƒŗã‚šã‚ŗãƒŧドされた動į”ģが存在しãĻいãĻも、あえãĻã‚ĒãƒĒジナãƒĢį”ģčŗĒぎ動į”ģã‚’å†į”Ÿã—ãžã™ã€‚ã‚šãƒˆãƒĒãƒŧãƒŸãƒŗã‚°ãĢåž…ãĄæ™‚é–“ãŒį”Ÿã˜ã‚‹ã‹ã‚‚ã—ã‚Œãžã›ã‚“ã€‚ãĒお、デバイ゚上ãĢäŋå­˜ã•れãĻいる動į”ģã¯ã“ãŽč¨­åŽšãŽæœ‰į„ĄãĢé–ĸわらず、ã‚ĒãƒĒジナãƒĢį”ģčŗĒぎ動į”ģã‚’å†į”Ÿã—ãžã™ã€‚", + "setting_video_viewer_original_video_title": "常ãĢã‚ĒãƒĒジナãƒĢį”ģčŗĒぎ動į”ģã‚’å†į”Ÿã™ã‚‹", "settings": "č¨­åŽš", "settings_require_restart": "Immichを再čĩˇå‹•しãĻč¨­åŽšã‚’éŠį”¨ã—ãĻください", "settings_saved": "č¨­åŽšãŒäŋå­˜ã•れぞした", + "setup_pin_code": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップ", "share": "å…ąæœ‰", "share_add_photos": "å†™įœŸã‚’čŋŊ加", - "share_assets_selected": "{}選択されぞした", + "share_assets_selected": "{count}選択中", "share_dialog_preparing": "æē–備中", + "share_link": "å…ąæœ‰ãƒĒãƒŗã‚¯", "shared": "å…ąæœ‰æ¸ˆãŋ", "shared_album_activities_input_disable": "ã‚ŗãƒĄãƒŗãƒˆã¯ã‚ĒフãĢãĒãŖãĻぞす", - "shared_album_activity_remove_content": "こぎã‚ĸã‚¯ãƒ†ã‚Ŗãƒ“ãƒ†ã‚Ŗã‚’å‰Šé™¤ã—ãžã™ã‹", + "shared_album_activity_remove_content": "こぎã‚ĸã‚¯ãƒ†ã‚Ŗãƒ“ãƒ†ã‚Ŗã‚’å‰Šé™¤ã—ãžã™ã‹īŧŸ", "shared_album_activity_remove_title": "ã‚ĸã‚¯ãƒ†ã‚Ŗãƒ“ãƒ†ã‚Ŗã‚’å‰Šé™¤ã—ãžã™", "shared_album_section_people_action_error": "退å‡ēãĢå¤ąæ•—", - "shared_album_section_people_action_leave": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムから退å‡ē", - "shared_album_section_people_action_remove_user": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムから退å‡ē", + "shared_album_section_people_action_leave": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムから退å‡ēさせる", + "shared_album_section_people_action_remove_user": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムから退å‡ēさせる", "shared_album_section_people_title": "äēēį‰Š", "shared_by": "ãĢã‚ˆã‚Šå…ąæœ‰", "shared_by_user": "{user} ãĢã‚ˆã‚Šå…ąæœ‰", "shared_by_you": "あãĒたãĢã‚ˆã‚Šå…ąæœ‰", "shared_from_partner": "{partner} ãĢã‚ˆã‚‹å†™įœŸ", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} ã‚ĸップロãƒŧド厌äē†", "shared_link_app_bar_title": "å…ąæœ‰ãƒĒãƒŗã‚¯", "shared_link_clipboard_copied_massage": "クãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧしぞした", - "shared_link_clipboard_text": "ãƒĒãƒŗã‚¯: {}\nパ゚ワãƒŧド: {}", + "shared_link_clipboard_text": "ãƒĒãƒŗã‚¯: {link}\nパ゚ワãƒŧド: {password}", "shared_link_create_error": "å…ąæœ‰į”¨ãŽãƒĒãƒŗã‚¯äŊœæˆæ™‚ãĢエナãƒŧがį™ēį”Ÿã—ãžã—ãŸ", "shared_link_edit_description_hint": "æĻ‚čĻã‚’čŋŊ加", "shared_link_edit_expire_after_option_day": "1æ—Ĩ", - "shared_link_edit_expire_after_option_days": "{}æ—Ĩ", + "shared_link_edit_expire_after_option_days": "{count}æ—Ĩ", "shared_link_edit_expire_after_option_hour": "1時間", - "shared_link_edit_expire_after_option_hours": "{}時間", + "shared_link_edit_expire_after_option_hours": "{count}時間", "shared_link_edit_expire_after_option_minute": "1分", - "shared_link_edit_expire_after_option_minutes": "{}分", - "shared_link_edit_expire_after_option_months": "{}ãƒļ月", - "shared_link_edit_expire_after_option_year": "{}åš´", + "shared_link_edit_expire_after_option_minutes": "{count}分", + "shared_link_edit_expire_after_option_months": "{count}ãƒļ月", + "shared_link_edit_expire_after_option_year": "{count}åš´", "shared_link_edit_password_hint": "å…ąæœ‰ãƒ‘ã‚šãƒ¯ãƒŧドをå…Ĩ力する", "shared_link_edit_submit_button": "ãƒĒãƒŗã‚¯ã‚’ã‚ĸップデãƒŧトする", "shared_link_error_server_url_fetch": "ã‚ĩãƒŧバãƒŧぎURLを取垗できぞせん", - "shared_link_expires_day": "{}æ—Ĩで切れぞす", - "shared_link_expires_days": "{}æ—Ĩで切れぞす", - "shared_link_expires_hour": "{}時間で切れぞす", - "shared_link_expires_hours": "{}時間で切れぞす", - "shared_link_expires_minute": "{}分で切れぞす", - "shared_link_expires_minutes": "{}分で切れぞす", + "shared_link_expires_day": "{count}æ—Ĩ垌ãĢ有劚期限切れ", + "shared_link_expires_days": "{count}æ—Ĩ垌ãĢ有劚期限切れ", + "shared_link_expires_hour": "{count}時間垌ãĢ有劚期限切れ", + "shared_link_expires_hours": "{count}時間垌ãĢ有劚期限切れ", + "shared_link_expires_minute": "{count}分垌ãĢ有劚期限切れ", + "shared_link_expires_minutes": "{count}分垌ãĢ有劚期限切れ", "shared_link_expires_never": "有劚期限はありぞせん", - "shared_link_expires_second": "{}į§’ã§åˆ‡ã‚Œãžã™", - "shared_link_expires_seconds": "{}į§’ã§åˆ‡ã‚Œãžã™", - "shared_link_individual_shared": "個äēēå…ąæœ‰", - "shared_link_info_chip_metadata": "EXIF", + "shared_link_expires_second": "{count}į§’åžŒãĢ有劚期限切れ", + "shared_link_expires_seconds": "{count}į§’åžŒãĢ有劚期限切れ", + "shared_link_individual_shared": "1æžšãšã¤å…ąæœ‰ã•ã‚ŒãĻいぞす", "shared_link_manage_links": "å…ąæœ‰æ¸ˆãŋぎãƒĒãƒŗã‚¯ã‚’įŽĄį†", "shared_link_options": "å…ąæœ‰ãƒĒãƒŗã‚¯ãŽã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "shared_links": "å…ąæœ‰ãƒĒãƒŗã‚¯", @@ -1699,7 +1754,7 @@ "slideshow_settings": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧč¨­åŽš", "sort_albums_by": "こぎ順åēã§ã‚ĸãƒĢバムをã‚Ŋãƒŧトâ€Ļ", "sort_created": "äŊœæˆæ—Ĩ", - "sort_items": "ã‚ĸイテムぎ数", + "sort_items": "é …į›ŽãŽæ•°", "sort_modified": "変更æ—Ĩ", "sort_oldest": "å¤ã„å†™įœŸ", "sort_people_by_similarity": "äŧŧãĻいる順ãĢäēēį‰Šã‚’ä¸Ļãŗæ›ŋえる", @@ -1722,6 +1777,7 @@ "stop_sharing_photos_with_user": "こぎãƒĻãƒŧã‚ļãƒŧã¨ãŽå†™įœŸãŽå…ąæœ‰ã‚’ã‚„ã‚ã‚‹", "storage": "゚トãƒŦãƒŧジäŊŋį”¨é‡", "storage_label": "゚トãƒŦãƒŧジナベãƒĢ", + "storage_quota": "゚トãƒŦãƒŧジ厚量", "storage_usage": "{available} 中 {used} äŊŋᔍ䏭", "submit": "送äŋĄ", "suggestions": "ãƒĻãƒŧã‚ļãƒŧãƒĒ゚ト", @@ -1748,7 +1804,7 @@ "theme_selection": "テãƒŧマ選択", "theme_selection_description": "ブナã‚Ļã‚ļãŽã‚ˇã‚šãƒ†ãƒ č¨­åŽšãĢåŸēãĨいãĻテãƒŧãƒžã‚’æ˜Žč‰˛ãžãŸã¯æš—č‰˛ãĢč‡Ēå‹•įš„ãĢč¨­åŽšã—ãžã™", "theme_setting_asset_list_storage_indicator_title": "゚トãƒŦãƒŧジãĢé–ĸã™ã‚‹æƒ…å ąã‚’čĄ¨į¤ē", - "theme_setting_asset_list_tiles_per_row_title": "ä¸€åˆ—ã”ã¨ãŽčĄ¨į¤ē枚数: {}", + "theme_setting_asset_list_tiles_per_row_title": "ä¸€čĄŒã”ã¨ãŽčĄ¨į¤ē枚数: {count}", "theme_setting_colorful_interface_subtitle": "ã‚ĸクã‚ģãƒŗãƒˆã‚Ģナãƒŧã‚’čƒŒæ™¯ãĢもäŊŋį”¨ã™ã‚‹", "theme_setting_colorful_interface_title": "ã‚ĢナフãƒĢãĒUI", "theme_setting_image_viewer_quality_subtitle": "į”ģ像ビãƒĨãƒŧぎį”ģčŗĒãŽč¨­åŽš", @@ -1783,16 +1839,18 @@ "trash_no_results_message": "ã‚´ãƒŸįŽąãĢį§ģå‹•ã—ãŸå†™įœŸã‚„å‹•į”ģがここãĢ襨į¤ēされぞす。", "trash_page_delete_all": "すずãĻ削除", "trash_page_empty_trash_dialog_content": "ã‚´ãƒŸįŽąã‚’įŠēãĢしぞすかīŧŸé¸æŠžã•ã‚ŒãŸé …į›Žã¯åŽŒå…¨ãĢ削除されぞす。こぎ操äŊœã¯å–りæļˆã›ãžã›ã‚“。", - "trash_page_info": "ã‚´ãƒŸįŽąãĢį§ģ動したã‚ĸイテムは{}æ—Ĩ垌ãĢ削除されぞす", + "trash_page_info": "ã‚´ãƒŸįŽąãĢį§ģ動したã‚ĸイテムは{days}æ—Ĩ垌ãĢ削除されぞす", "trash_page_no_assets": "ã‚´ãƒŸįŽąã¯įŠēです", "trash_page_restore_all": "すずãĻ垊元", "trash_page_select_assets_btn": "é …į›Žã‚’é¸æŠž", - "trash_page_title": "削除({})", + "trash_page_title": "ã‚´ãƒŸįŽą ({count})", "trashed_items_will_be_permanently_deleted_after": "ã‚´ãƒŸįŽąãĢå…Ĩれられたã‚ĸイテムは{days, plural, one {#æ—Ĩ} other {#æ—Ĩ}}垌ãĢ厌全ãĢ削除されぞす。", "type": "ã‚ŋイプ", + "unable_to_change_pin_code": "PINã‚ŗãƒŧドを変更できぞせんでした", + "unable_to_setup_pin_code": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップできぞせんでした", "unarchive": "ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤", - "unarchived_count": "{count, plural, other {#枚ã‚ĸãƒŧã‚Ģイブしぞした}}", - "unfavorite": "お気ãĢå…Ĩりから外す", + "unarchived_count": "{count, plural, other {#枚ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤ã—ãžã—ãŸ}}", + "unfavorite": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", "unhide_person": "äēēį‰ŠãŽéžčĄ¨į¤ēã‚’č§Ŗé™¤", "unknown": "不明", "unknown_country": "不明ãĒå›Ŋ", @@ -1813,6 +1871,7 @@ "untracked_files": "æœĒčŋŊčˇĄãƒ•ã‚Ąã‚¤ãƒĢ", "untracked_files_decription": "ã“ã‚Œã‚‰ãŽãƒ•ã‚Ąã‚¤ãƒĢはã‚ĸプãƒĒã‚ąãƒŧã‚ˇãƒ§ãƒŗãĢã‚ˆãŖãĻčŋŊčˇĄã•ã‚ŒãĻいぞせん。これらはį§ģå‹•ãŽå¤ąæ•—ã€ã‚ĸップロãƒŧドぎ中断、ぞたはバグãĢより取り掋されたもぎである可čƒŊ性がありぞす", "up_next": "æŦĄã¸", + "updated_at": "更新", "updated_password": "パ゚ワãƒŧドを更新しぞした", "upload": "ã‚ĸップロãƒŧド", "upload_concurrency": "ã‚ĸップロãƒŧãƒ‰ãŽåŒæ™‚åŽŸčĄŒæ•°", @@ -1825,15 +1884,18 @@ "upload_status_errors": "エナãƒŧ", "upload_status_uploaded": "ã‚ĸップロãƒŧド済", "upload_success": "ã‚ĸップロãƒŧド成功、新しくã‚ĸップロãƒŧドされたã‚ĸã‚ģットをčĻ‹ã‚‹ãĢはペãƒŧジを更新しãĻください。", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "ImmichãĢã‚ĸップロãƒŧド ({count})", + "uploading": "ã‚ĸップロãƒŧド中", "usage": "äŊŋį”¨åŽšé‡", + "use_biometric": "į”ŸäŊ“čĒč¨ŧã‚’ã”åˆŠį”¨ãã ã•ã„", "use_current_connection": "įžåœ¨ãŽæŽĨįƒ…å ąã‚’äŊŋᔍ", "use_custom_date_range": "äģŖã‚ã‚ŠãĢã‚Ģ゚ã‚ŋムæ—Ĩäģ˜į¯„å›˛ã‚’äŊŋᔍ", "user": "ãƒĻãƒŧã‚ļãƒŧ", + "user_has_been_deleted": "こぎãƒĻãƒŧã‚ļãƒŧは削除されぞした", "user_id": "ãƒĻãƒŧã‚ļãƒŧID", "user_liked": "{user} が{type, select, photo {ã“ãŽå†™įœŸã‚’} video {こぎ動į”ģを} asset {こぎã‚ĸã‚ģットを} other {}}いいねしぞした", + "user_pin_code_settings": "PINã‚ŗãƒŧド", + "user_pin_code_settings_description": "PINã‚ŗãƒŧãƒ‰ã‚’įŽĄį†", "user_purchase_settings": "čŗŧå…Ĩ", "user_purchase_settings_description": "čŗŧå…Ĩã‚’įŽĄį†", "user_role_set": "{user} を{role}ãĢč¨­åŽšã—ãžã—ãŸ", @@ -1883,6 +1945,7 @@ "welcome": "ようこそ", "welcome_to_immich": "ImmichãĢようこそ", "wifi_name": "Wi-Fiぎ名前(SSID)", + "wrong_pin_code": "PINã‚ŗãƒŧãƒ‰ãŒé–“é•ãŖãĻいぞす", "year": "åš´", "years_ago": "{years, plural, one {#åš´} other {#åš´}}前", "yes": "はい", diff --git a/i18n/ka.json b/i18n/ka.json index a521539d11..e0421d6225 100644 --- a/i18n/ka.json +++ b/i18n/ka.json @@ -4,47 +4,62 @@ "account_settings": "ანგარიშის პარამეáƒĸრები", "acknowledge": "მიáƒĻება", "action": "áƒĨმედება", + "action_common_update": "განაახლე", "actions": "áƒĨმედებები", "active": "აáƒĨáƒĸáƒ˜áƒŖáƒ áƒ˜", "activity": "აáƒĨáƒĸივობა", - "add": "დამაáƒĸება", + "activity_changed": "აáƒĨáƒĸივობა {enabled, select, true {áƒŠáƒáƒ áƒ—áƒŖáƒšáƒ˜} other {áƒ’áƒáƒ›áƒáƒ áƒ—áƒŖáƒšáƒ˜}}", + "add": "დაამაáƒĸე", "add_a_description": "დაამაáƒĸე აáƒĻáƒŦერა", "add_a_location": "დაამაáƒĸე ადგილი", "add_a_name": "დაამაáƒĸე სახელი", "add_a_title": "áƒ“áƒáƒáƒĄáƒáƒ—áƒáƒŖáƒ áƒ”", + "add_exclusion_pattern": "დაამაáƒĸე გამონაკლისი áƒœáƒ˜áƒ›áƒŖáƒ¨áƒ˜", "add_import_path": "დაამაáƒĸე საიმპორáƒĸო მისამართი", "add_location": "დაამაáƒĸე ადგილი", "add_more_users": "დაამაáƒĸე მომხმარებლები", "add_partner": "დაამაáƒĸე პარáƒĸნიორი", "add_path": "დაამაáƒĸე მისამართი", "add_photos": "დაამაáƒĸე ფოáƒĸოები", + "add_to": "დაამაáƒĸე ...ში", "add_to_album": "დაამაáƒĸე ალბომში", + "add_to_album_bottom_sheet_added": "დამაáƒĸáƒ”áƒ‘áƒŖáƒšáƒ˜áƒ {album}-ში", + "add_to_album_bottom_sheet_already_exists": "{album}-ში áƒŖáƒ™áƒ•áƒ” არსებობს", "add_to_shared_album": "დაამაáƒĸე საზიარო ალბომში", "add_url": "დაამაáƒĸე URL", "added_to_archive": "დაარáƒĨივდა", "added_to_favorites": "დაამაáƒĸე áƒ áƒŠáƒ”áƒŖáƒšáƒ”áƒ‘áƒ¨áƒ˜", "added_to_favorites_count": "{count, number} დაემაáƒĸა áƒ áƒŠáƒ”áƒŖáƒšáƒ”áƒ‘áƒ¨áƒ˜", "admin": { + "asset_offline_description": "ეს საგარეო ბიბლიოთეკის აáƒĨáƒĸივი დისკზე ვერ მოიáƒĢებნა და სანაგვეში იáƒĨნა áƒ›áƒáƒ—áƒáƒ•áƒĄáƒ”áƒ‘áƒŖáƒšáƒ˜. áƒ—áƒŖ ფაილი ბიბლიოთეკის შიგნით მდებარეობს, შეამოáƒŦმეთ შესაბამისი აáƒĨáƒĸივი áƒĸაიმლაინზე. ამ აáƒĨáƒĸივის აáƒĻსადგენად, დარáƒŦáƒ›áƒŖáƒœáƒ“áƒ˜áƒ— რომ áƒĨვემოთ მოáƒĒáƒ”áƒ›áƒŖáƒšáƒ˜ ფაილის მისამართი Immich-იქ მიერ áƒŦვდომადია და დაასკანერეთ ბიბლიოთეკა.", "authentication_settings": "ავთენáƒĸიკაáƒĒიიქ პარამეáƒĸრები", "authentication_settings_description": "პაროლის, OAuth-იქ და სხვა ავáƒĸენთიფიკაáƒĒიიქ პარამეáƒĸრების მართვა", "authentication_settings_disable_all": "ნამდვილად გინდა ავáƒĸორიზაáƒĒიიქ ყველა მეთოდის გამორთვა? ავáƒĸორიზაáƒĒიაქ ვეáƒĻარანაირად შეáƒĢლებ.", "authentication_settings_reenable": "რეაáƒĨáƒĸივაáƒĒიისთვის, გამოიყენე სერვერის ბრáƒĢანება.", "background_task_job": "áƒ¤áƒáƒœáƒŖáƒ áƒ˜ დავალებები", - "backup_database": "შეáƒĨმენი სარეზერვო ასლი", - "backup_database_enable_description": "ჩართე სარეზერვო ასლების áƒ¤áƒŖáƒœáƒĨáƒĒია", - "backup_keep_last_amount": "შესანახი სარეზერვო ასლების რაოდენობა", - "backup_settings": "სარეზერვო ასლების პარამეáƒĸრები", - "backup_settings_description": "მონაáƒĒემთა ბაზის სარეზერვო ასლების პარამეáƒĸრების მართვა", + "backup_database": "ბაზის დამპის შეáƒĨმნა", + "backup_database_enable_description": "ბაზის დამპების ჩართვა", + "backup_keep_last_amount": "áƒŦინა დამპების áƒ¨áƒ”áƒĄáƒáƒœáƒáƒ áƒŠáƒŖáƒœáƒ”áƒ‘áƒ”áƒšáƒ˜ რაოდენობა", + "backup_settings": "მონაáƒĒემთა ბაზის დამპის მორგება", + "backup_settings_description": "მონაáƒĒემთა ბაზის პარამეáƒĸრების ამრთვა. შენიშვნა: ამ დავალებების მონიáƒĸორინგი არ ხდება და თáƒĨვენ არ მოგივათ შეáƒĸყობინება, áƒ—áƒŖ იქ ჩავარდება.", "check_all": "შეამოáƒŦმე ყველა", "cleanup": "áƒ’áƒáƒĄáƒŖáƒ¤áƒ—áƒáƒ•áƒ”áƒ‘áƒ", + "cleared_jobs": "დავალებები {job}-ისათვის გაáƒŦმენდილია", + "config_set_by_file": "მიმდინარე áƒ™áƒáƒœáƒ¤áƒ˜áƒ’áƒŖáƒ áƒáƒĒია ფაილის მიერ არიქ áƒ“áƒáƒ§áƒ”áƒœáƒ”áƒ‘áƒŖáƒšáƒ˜", "confirm_delete_library": "ნამდვილად გინდა {library} ბიბლიოთეკის áƒŦაშლა?", + "confirm_delete_library_assets": "მართლა áƒ’áƒĄáƒŖáƒ áƒ— ამ ბიბლიოთეკის áƒŦაშლა? ეს áƒĨმედება Immich-იდან áƒŦაშლის ყველა áƒ›áƒáƒœáƒ˜áƒ¨áƒœáƒŖáƒš აáƒĨáƒĸივს და áƒ¨áƒ”áƒŖáƒĨáƒĒევადია. ფაილები მყარ დისკზე áƒŽáƒ”áƒšáƒŖáƒŽáƒšáƒ”áƒ‘áƒ”áƒšáƒ˜ დარჩება.", "confirm_email_below": "დასადასáƒĸáƒŖáƒ áƒ”áƒ‘áƒšáƒáƒ“, áƒĨვემოთ აკრიფე \"{email}\"", + "confirm_reprocess_all_faces": "მართლა áƒ’áƒĄáƒŖáƒ áƒ— ყველა ქა჎იქ თავიდან áƒ“áƒáƒ›áƒŖáƒ¨áƒáƒ•áƒ”áƒ‘áƒ? ეს áƒĨმედება ხალხისათვის áƒ›áƒ˜áƒœáƒ˜áƒ­áƒ”áƒ‘áƒŖáƒš სახელებს გაáƒŦმენდს.", "confirm_user_password_reset": "ნამდვილად გინდა {user}-(ი)ქ პაროლის დარესეáƒĸება?", + "create_job": "შეáƒĨმენი დავალება", + "cron_expression": "Cron áƒ’áƒáƒ›áƒáƒĄáƒáƒŽáƒŖáƒšáƒ”áƒ‘áƒ", "disable_login": "გამორთე ავáƒĸორიზაáƒĒია", "external_library_management": "გარე ბიბლიოთეკების მართვა", "face_detection": "ქა჎იქ ამოáƒĒნობა", "image_format": "ფორმაáƒĸი", + "image_format_description": "WebP ფორმაáƒĸი JPEG-ზე პაáƒĸარა ფაილებს აáƒŦარმოებს, მაგრამ მის დამზადებას áƒŖáƒ¤áƒ áƒ მეáƒĸი დრო სჭირდება.", "image_fullsize_title": "áƒĄáƒ áƒŖáƒšáƒ˜ ზომის áƒ’áƒáƒ›áƒáƒĄáƒáƒŽáƒŖáƒšáƒ”áƒ‘áƒ˜áƒĄ პარამეáƒĸრები", + "image_prefer_wide_gamut": "áƒŖáƒžáƒ˜áƒ áƒáƒĸესობა მიენიჭოს ფერის ფართე დიაპაზონს", "image_quality": "჎არიქ჎ი", "image_resolution": "გაფართოება", "image_settings": "áƒ’áƒáƒ›áƒáƒĄáƒáƒŽáƒŖáƒšáƒ”áƒ‘áƒ˜áƒĄ პარამეáƒĸრები", @@ -59,7 +74,6 @@ "logging_settings": "áƒŸáƒŖáƒ áƒœáƒáƒšáƒ˜", "map_settings": "áƒ áƒŖáƒ™áƒ", "migration_job": "მიგრაáƒĒია", - "oauth_scope": "დიაპაზონი", "oauth_settings": "OAuth", "template_email_preview": "მინიაáƒĸáƒŖáƒ áƒ", "transcoding_acceleration_vaapi": "VAAPI", @@ -104,7 +118,6 @@ "details": "დეáƒĸალები", "direction": "áƒ›áƒ˜áƒ›áƒáƒ áƒ—áƒŖáƒšáƒ”áƒ‘áƒ", "disabled": "áƒ’áƒáƒ—áƒ˜áƒ¨áƒŖáƒšáƒ˜áƒ", - "discord": "Discord", "discover": "აáƒĻმოჩენა", "documentation": "áƒ“áƒáƒ™áƒŖáƒ›áƒ”áƒœáƒĸაáƒĒია", "done": "მზადაა", @@ -121,7 +134,6 @@ "enable": "ჩართვა", "enabled": "áƒŠáƒáƒ áƒ—áƒŖáƒšáƒ˜áƒ", "error": "შეáƒĒდომა", - "exif": "Exif", "expired": "ვადაამოáƒŦáƒŖáƒ áƒŖáƒšáƒ˜áƒ", "explore": "დათვალიერება", "explorer": "გამáƒĒილებელი", diff --git a/i18n/kk.json b/i18n/kk.json index 0967ef424b..e3a1f51c3e 100644 --- a/i18n/kk.json +++ b/i18n/kk.json @@ -1 +1,16 @@ -{} +{ + "add_photos": "ŅŅƒŅ€Đĩ҂҂ĐĩŅ€Đ´Ņ– Ō›ĐžŅŅƒ", + "add_to": "Ō›ĐžŅŅƒâ€Ļ", + "add_to_album": "аĐģŅŒĐąĐžĐŧŌ“Đ° Ō›ĐžŅŅƒ", + "add_to_album_bottom_sheet_added": "{album}'Ō“Đ° Ō›ĐžŅŅ‹ĐģŌ“Đ°ĐŊ", + "add_to_album_bottom_sheet_already_exists": "ОĐŊŅŅ‹Đˇ да {album} йОĐģŌ“Đ°ĐŊ", + "add_to_shared_album": "ĐąĶŠĐģҖҁĐēĐĩĐŊ аĐģŅŒĐąĐžĐŧŌ“Đ° Ō›ĐžŅŅƒ", + "add_url": "URL Ņ‚Đ°ŌŖĐ´Đ°Ņƒ", + "added_to_archive": "ĐŅ€Ņ…Đ¸Đ˛ĐēĐĩ ĐļŅ–ĐąĐĩҀҖĐģĐŗĐĩĐŊ", + "added_to_favorites": "Ņ‚Đ°ŌŖĐ´Đ°ŅƒĐģŅ‹ĐģĐ°Ņ€Ō“Đ° Ō›ĐžŅŅ‹ĐģŌ“Đ°ĐŊ", + "admin": { + "check_all": "Đ‘Ķ™Ņ€Ņ–ĐŊ Ņ‚ĐĩĐēҁĐĩҀҖĐŋ аĐģ҃", + "create_job": "Đ–ŌąĐŧҋҁ҂ҋ ĐąĐ°ŅŅ‚Đ°Ņƒ" + }, + "zoom_image": "ŅŅƒŅ€Đĩ҂҂Җ Ō¯ĐģĐēĐĩĐšŅ‚Ņƒ" +} diff --git a/i18n/kmr.json b/i18n/kmr.json index 1a7e7bff41..cf634e00da 100644 --- a/i18n/kmr.json +++ b/i18n/kmr.json @@ -2,873 +2,5 @@ "about": "Ø¯Û•ØąØ¨Ø§ØąÛ•", "account": "Ų‡Û•Ú˜Ų…Ø§Øą", "account_settings": "Ú•ÛŽÚŠØŽØŗØĒŲ†ÛŒ Ų‡Û•Ú˜Ų…Ø§Øą", - "acknowledge": "Ø¯Ø§Ų†ŲžÛŽØ¯Ø§Ų†Ø§Ų†", - "action": "", - "actions": "", - "active": "", - "activity": "", - "add": "", - "add_a_description": "", - "add_a_location": "", - "add_a_name": "", - "add_a_title": "", - "add_exclusion_pattern": "", - "add_import_path": "", - "add_location": "", - "add_more_users": "", - "add_partner": "", - "add_path": "", - "add_photos": "", - "add_to": "", - "add_to_album": "", - "add_to_shared_album": "", - "admin": { - "add_exclusion_pattern_description": "", - "authentication_settings": "", - "authentication_settings_description": "", - "background_task_job": "", - "check_all": "", - "config_set_by_file": "", - "confirm_delete_library": "", - "confirm_delete_library_assets": "", - "confirm_email_below": "", - "confirm_reprocess_all_faces": "", - "confirm_user_password_reset": "", - "disable_login": "", - "duplicate_detection_job_description": "", - "exclusion_pattern_description": "", - "external_library_created_at": "", - "external_library_management": "", - "face_detection": "", - "face_detection_description": "", - "facial_recognition_job_description": "", - "force_delete_user_warning": "", - "forcing_refresh_library_files": "", - "image_format_description": "", - "image_prefer_embedded_preview": "", - "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", - "image_prefer_wide_gamut_setting_description": "", - "image_quality": "", - "image_settings": "", - "image_settings_description": "", - "job_concurrency": "", - "job_not_concurrency_safe": "", - "job_settings": "", - "job_settings_description": "", - "job_status": "", - "jobs_delayed": "", - "jobs_failed": "", - "library_created": "", - "library_deleted": "", - "library_import_path_description": "", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", - "library_settings_description": "", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", - "machine_learning_clip_model": "", - "machine_learning_duplicate_detection": "", - "machine_learning_duplicate_detection_enabled": "", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled": "", - "machine_learning_enabled_description": "", - "machine_learning_facial_recognition": "", - "machine_learning_facial_recognition_description": "", - "machine_learning_facial_recognition_model": "", - "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting": "", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", - "machine_learning_settings": "", - "machine_learning_settings_description": "", - "machine_learning_smart_search": "", - "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled": "", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_concurrency": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", - "map_settings": "", - "map_settings_description": "", - "map_style_description": "", - "metadata_extraction_job": "", - "metadata_extraction_job_description": "", - "migration_job": "", - "migration_job_description": "", - "no_paths_added": "", - "no_pattern_added": "", - "note_apply_storage_label_previous_assets": "", - "note_cannot_be_changed_later": "", - "notification_email_from_address": "", - "notification_email_from_address_description": "", - "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", - "notification_email_ignore_certificate_errors_description": "", - "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", - "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", - "oauth_mobile_redirect_uri_override": "", - "oauth_mobile_redirect_uri_override_description": "", - "oauth_scope": "", - "oauth_settings": "", - "oauth_settings_description": "", - "oauth_signing_algorithm": "", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", - "oauth_storage_quota_default": "", - "oauth_storage_quota_default_description": "", - "offline_paths": "", - "offline_paths_description": "", - "password_enable_description": "", - "password_settings": "", - "password_settings_description": "", - "paths_validated_successfully": "", - "quota_size_gib": "", - "refreshing_all_libraries": "", - "repair_all": "", - "repair_matched_items": "", - "repaired_items": "", - "require_password_change_on_login": "", - "reset_settings_to_default": "", - "reset_settings_to_recent_saved": "", - "send_welcome_email": "", - "server_external_domain_settings": "", - "server_external_domain_settings_description": "", - "server_settings": "", - "server_settings_description": "", - "server_welcome_message": "", - "server_welcome_message_description": "", - "sidecar_job": "", - "sidecar_job_description": "", - "slideshow_duration_description": "", - "smart_search_job_description": "", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", - "storage_template_migration": "", - "storage_template_migration_job": "", - "storage_template_settings": "", - "storage_template_settings_description": "", - "system_settings": "", - "theme_custom_css_settings": "", - "theme_custom_css_settings_description": "", - "theme_settings": "", - "theme_settings_description": "", - "these_files_matched_by_checksum": "", - "thumbnail_generation_job": "", - "thumbnail_generation_job_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", - "transcoding_acceleration_qsv": "", - "transcoding_acceleration_rkmpp": "", - "transcoding_acceleration_vaapi": "", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", - "transcoding_audio_codec": "", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", - "transcoding_constant_quality_mode": "", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", - "transcoding_hardware_acceleration_description": "", - "transcoding_hardware_decoding": "", - "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_transcode_policy_description": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", - "transcoding_video_codec": "", - "transcoding_video_codec_description": "", - "trash_enabled_description": "", - "trash_number_of_days": "", - "trash_number_of_days_description": "", - "trash_settings": "", - "trash_settings_description": "", - "untracked_files": "", - "untracked_files_description": "", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", - "user_management": "", - "user_password_has_been_reset": "", - "user_password_reset_description": "", - "user_settings": "", - "user_settings_description": "", - "user_successfully_removed": "", - "version_check_enabled_description": "", - "version_check_settings": "", - "version_check_settings_description": "", - "video_conversion_job": "", - "video_conversion_job_description": "" - }, - "admin_email": "", - "admin_password": "", - "administration": "", - "advanced": "", - "album_added": "", - "album_added_notification_setting_description": "", - "album_cover_updated": "", - "album_info_updated": "", - "album_name": "", - "album_options": "", - "album_updated": "", - "album_updated_setting_description": "", - "albums": "", - "albums_count": "", - "all": "", - "all_people": "", - "allow_dark_mode": "", - "allow_edits": "", - "api_key": "", - "api_keys": "", - "app_settings": "", - "appears_in": "", - "archive": "", - "archive_or_unarchive_photo": "", - "archive_size": "", - "archive_size_description": "", - "asset_offline": "", - "assets": "", - "authorized_devices": "", - "back": "", - "backward": "", - "blurred_background": "", - "camera": "", - "camera_brand": "", - "camera_model": "", - "cancel": "", - "cancel_search": "", - "cannot_merge_people": "", - "cannot_update_the_description": "", - "change_date": "", - "change_expiration_time": "", - "change_location": "", - "change_name": "", - "change_name_successfully": "", - "change_password": "", - "change_your_password": "", - "changed_visibility_successfully": "", - "check_all": "", - "check_logs": "", - "choose_matching_people_to_merge": "", - "city": "", - "clear": "", - "clear_all": "", - "clear_message": "", - "clear_value": "", - "close": "", - "collapse_all": "", - "color_theme": "", - "comment_options": "", - "comments_are_disabled": "", - "confirm": "", - "confirm_admin_password": "", - "confirm_delete_shared_link": "", - "confirm_password": "", - "contain": "", - "context": "", - "continue": "", - "copied_image_to_clipboard": "", - "copied_to_clipboard": "", - "copy_error": "", - "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", - "country": "", - "cover": "", - "covers": "", - "create": "", - "create_album": "", - "create_library": "", - "create_link": "", - "create_link_to_share": "", - "create_new_person": "", - "create_new_user": "", - "create_user": "", - "created": "", - "current_device": "", - "custom_locale": "", - "custom_locale_description": "", - "dark": "", - "date_after": "", - "date_and_time": "", - "date_before": "", - "date_range": "", - "day": "", - "default_locale": "", - "default_locale_description": "", - "delete": "", - "delete_album": "", - "delete_api_key_prompt": "", - "delete_key": "", - "delete_library": "", - "delete_link": "", - "delete_shared_link": "", - "delete_user": "", - "deleted_shared_link": "", - "description": "", - "details": "", - "direction": "", - "disabled": "", - "disallow_edits": "", - "discover": "", - "dismiss_all_errors": "", - "dismiss_error": "", - "display_options": "", - "display_order": "", - "display_original_photos": "", - "display_original_photos_setting_description": "", - "done": "", - "download": "", - "download_settings": "", - "download_settings_description": "", - "downloading": "", - "duplicates": "", - "duration": "", - "edit_album": "", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", - "edit_key": "", - "edit_link": "", - "edit_location": "", - "edit_name": "", - "edit_people": "", - "edit_title": "", - "edit_user": "", - "edited": "", - "editor": "", - "email": "", - "empty_trash": "", - "end_date": "", - "error": "", - "error_loading_image": "", - "errors": { - "cleared_jobs": "", - "exclusion_pattern_already_exists": "", - "failed_job_command": "", - "import_path_already_exists": "", - "paths_validation_failed": "", - "quota_higher_than_disk_size": "", - "repair_unable_to_check_items": "", - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_exclusion_pattern": "", - "unable_to_add_import_path": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_change_password": "", - "unable_to_copy_to_clipboard": "", - "unable_to_create_api_key": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", - "unable_to_delete_exclusion_pattern": "", - "unable_to_delete_import_path": "", - "unable_to_delete_shared_link": "", - "unable_to_delete_user": "", - "unable_to_edit_exclusion_pattern": "", - "unable_to_edit_import_path": "", - "unable_to_empty_trash": "", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", - "unable_to_hide_person": "", - "unable_to_link_oauth_account": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_api_key": "", - "unable_to_remove_deleted_assets": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_api_key": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_timeline_display_status": "", - "unable_to_update_user": "" - }, - "exit_slideshow": "", - "expand_all": "", - "expire_after": "", - "expired": "", - "explore": "", - "export": "", - "export_as_json": "", - "extension": "", - "external": "", - "external_libraries": "", - "favorite": "", - "favorite_or_unfavorite_photo": "", - "favorites": "", - "feature_photo_updated": "", - "file_name": "", - "file_name_or_extension": "", - "filename": "", - "filetype": "", - "filter_people": "", - "find_them_fast": "", - "fix_incorrect_match": "", - "forward": "", - "general": "", - "get_help": "", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "group_albums_by": "", - "has_quota": "", - "hide_gallery": "", - "hide_password": "", - "hide_person": "", - "host": "", - "hour": "", - "image": "", - "immich_logo": "", - "immich_web_interface": "", - "import_from_json": "", - "import_path": "", - "in_archive": "", - "include_archived": "", - "include_shared_albums": "", - "include_shared_partner_assets": "", - "individual_share": "", - "info": "", - "interval": { - "day_at_onepm": "", - "hours": "", - "night_at_midnight": "", - "night_at_twoam": "" - }, - "invite_people": "", - "invite_to_album": "", - "jobs": "", - "keep": "", - "keyboard_shortcuts": "", - "language": "", - "language_setting_description": "", - "last_seen": "", - "leave": "", - "let_others_respond": "", - "level": "", - "library": "", - "library_options": "", - "light": "", - "link_options": "", - "link_to_oauth": "", - "linked_oauth_account": "", - "list": "", - "loading": "", - "loading_search_results_failed": "", - "log_out": "", - "log_out_all_devices": "", - "login_has_been_disabled": "", - "look": "", - "loop_videos": "", - "loop_videos_description": "", - "make": "", - "manage_shared_links": "", - "manage_sharing_with_partners": "", - "manage_the_app_settings": "", - "manage_your_account": "", - "manage_your_api_keys": "", - "manage_your_devices": "", - "manage_your_oauth_connection": "", - "map": "", - "map_marker_with_image": "", - "map_settings": "", - "matches": "", - "media_type": "", - "memories": "", - "memories_setting_description": "", - "menu": "", - "merge": "", - "merge_people": "", - "merge_people_successfully": "", - "minimize": "", - "minute": "", - "missing": "", - "model": "", - "month": "", - "more": "", - "moved_to_trash": "", - "my_albums": "", - "name": "", - "name_or_nickname": "", - "never": "", - "new_api_key": "", - "new_password": "", - "new_person": "", - "new_user_created": "", - "newest_first": "", - "next": "", - "next_memory": "", - "no": "", - "no_albums_message": "", - "no_archived_assets_message": "", - "no_assets_message": "", - "no_duplicates_found": "", - "no_exif_info_available": "", - "no_explore_results_message": "", - "no_favorites_message": "", - "no_libraries_message": "", - "no_name": "", - "no_places": "", - "no_results": "", - "no_shared_albums_message": "", - "not_in_any_album": "", - "note_apply_storage_label_to_previously_uploaded assets": "", - "notes": "", - "notification_toggle_setting_description": "", - "notifications": "", - "notifications_setting_description": "", - "oauth": "", - "offline": "", - "offline_paths": "", - "offline_paths_description": "", - "ok": "", - "oldest_first": "", - "online": "", - "only_favorites": "", - "open_the_search_filters": "", - "options": "", - "organize_your_library": "", - "other": "", - "other_devices": "", - "other_variables": "", - "owned": "", - "owner": "", - "partner_can_access": "", - "partner_can_access_assets": "", - "partner_can_access_location": "", - "partner_sharing": "", - "partners": "", - "password": "", - "password_does_not_match": "", - "password_required": "", - "password_reset_success": "", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, - "path": "", - "pattern": "", - "pause": "", - "pause_memories": "", - "paused": "", - "pending": "", - "people": "", - "people_sidebar_description": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", - "permanently_delete": "", - "permanently_deleted_asset": "", - "photos": "", - "photos_count": "", - "photos_from_previous_years": "", - "pick_a_location": "", - "place": "", - "places": "", - "play": "", - "play_memories": "", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", - "profile_picture_set": "", - "public_share": "", - "reaction_options": "", - "read_changelog": "", - "recent": "", - "recent_searches": "", - "refresh": "", - "refreshed": "", - "refreshes_every_file": "", - "remove": "", - "remove_deleted_assets": "", - "remove_from_album": "", - "remove_from_favorites": "", - "remove_from_shared_link": "", - "removed_api_key": "", - "rename": "", - "repair": "", - "repair_no_results_message": "", - "replace_with_upload": "", - "require_password": "", - "require_user_to_change_password_on_first_login": "", - "reset": "", - "reset_password": "", - "reset_people_visibility": "", - "restore": "", - "restore_all": "", - "restore_user": "", - "resume": "", - "retry_upload": "", - "review_duplicates": "", - "role": "", - "save": "", - "saved_api_key": "", - "saved_profile": "", - "saved_settings": "", - "say_something": "", - "scan_all_libraries": "", - "scan_settings": "", - "search": "", - "search_albums": "", - "search_by_context": "", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", - "search_for_existing_person": "", - "search_people": "", - "search_places": "", - "search_state": "", - "search_timezone": "", - "search_type": "", - "search_your_photos": "", - "searching_locales": "", - "second": "", - "select_album_cover": "", - "select_all": "", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_keep_all": "", - "select_library_owner": "", - "select_new_face": "", - "select_photos": "", - "select_trash_all": "", - "selected": "", - "send_message": "", - "send_welcome_email": "", - "server_stats": "", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", - "settings": "", - "settings_saved": "", - "share": "", - "shared": "", - "shared_by": "", - "shared_by_you": "", - "shared_from_partner": "", - "shared_links": "", - "shared_photos_and_videos_count": "", - "shared_with_partner": "", - "sharing": "", - "sharing_sidebar_description": "", - "show_album_options": "", - "show_and_hide_people": "", - "show_file_location": "", - "show_gallery": "", - "show_hidden_people": "", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", - "show_metadata": "", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", - "shuffle": "", - "sign_out": "", - "sign_up": "", - "size": "", - "skip_to_content": "", - "slideshow": "", - "slideshow_settings": "", - "sort_albums_by": "", - "stack": "", - "stack_selected_photos": "", - "stacktrace": "", - "start": "", - "start_date": "", - "state": "", - "status": "", - "stop_motion_photo": "", - "stop_photo_sharing": "", - "stop_photo_sharing_description": "", - "stop_sharing_photos_with_user": "", - "storage": "", - "storage_label": "", - "storage_usage": "", - "submit": "", - "suggestions": "", - "sunrise_on_the_beach": "", - "swap_merge_direction": "", - "sync": "", - "template": "", - "theme": "", - "theme_selection": "", - "theme_selection_description": "", - "time_based_memories": "", - "timezone": "", - "to_archive": "", - "to_favorite": "", - "toggle_settings": "", - "toggle_theme": "", - "total_usage": "", - "trash": "", - "trash_all": "", - "trash_no_results_message": "", - "trashed_items_will_be_permanently_deleted_after": "", - "type": "", - "unarchive": "", - "unfavorite": "", - "unhide_person": "", - "unknown": "", - "unknown_year": "", - "unlimited": "", - "unlink_oauth": "", - "unlinked_oauth_account": "", - "unselect_all": "", - "unstack": "", - "untracked_files": "", - "untracked_files_decription": "", - "up_next": "", - "updated_password": "", - "upload": "", - "upload_concurrency": "", - "url": "", - "usage": "", - "user": "", - "user_id": "", - "user_usage_detail": "", - "username": "", - "users": "", - "utilities": "", - "validate": "", - "variables": "", - "version": "", - "video": "", - "video_hover_setting": "", - "video_hover_setting_description": "", - "videos": "", - "videos_count": "", - "view_all": "", - "view_all_users": "", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", - "waiting": "", - "week": "", - "welcome": "", - "welcome_to_immich": "", - "year": "", - "yes": "", - "you_dont_have_any_shared_links": "", - "zoom_image": "" + "acknowledge": "Ø¯Ø§Ų†ŲžÛŽØ¯Ø§Ų†Ø§Ų†" } diff --git a/i18n/kn.json b/i18n/kn.json index 0967ef424b..388f06704c 100644 --- a/i18n/kn.json +++ b/i18n/kn.json @@ -1 +1,26 @@ -{} +{ + "about": "➕⺁➰ā˛ŋ➤⺁", + "account": "ā˛–ā˛žā˛¤āŗ†", + "account_settings": "ā˛–ā˛žā˛¤āŗ† ā˛¸āŗ†ā˛Ÿāŗā˛Ÿā˛ŋā˛‚ā˛—āŗâ€Œā˛—ā˛ŗāŗ", + "acknowledge": "➅➂➗⺀➕➰ā˛ŋ➏ā˛ŋ", + "action": "ā˛•ā˛žā˛°āŗā˛¯", + "action_common_update": "➍ā˛ĩ⺀➕➰ā˛ŋ➏ā˛ŋ", + "actions": "ā˛•āŗā˛°ā˛ŋ➝⺆➗➺⺁", + "active": "ā˛¸ā˛•āŗā˛°ā˛ŋ➝", + "activity": "➚➟⺁ā˛ĩ➟ā˛ŋ➕⺆", + "add": "➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_a_description": "ā˛ĩā˛ŋā˛ĩā˛°ā˛Ŗāŗ†ā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_a_location": "ā˛¸āŗā˛Ĩ➺ā˛ĩā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_a_name": "ā˛šāŗ†ā˛¸ā˛°ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_a_title": "ā˛ļāŗ€ā˛°āŗā˛ˇā˛ŋā˛•āŗ†ā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_endpoint": "ā˛Žā˛‚ā˛Ąāŗâ€Œā˛Ēā˛žā˛¯ā˛ŋā˛‚ā˛Ÿāŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_exclusion_pattern": "ā˛šāŗŠā˛°ā˛—ā˛ŋā˛Ąāŗā˛ĩā˛ŋ➕⺆ ā˛Žā˛žā˛Ļ➰ā˛ŋā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_import_path": "ā˛†ā˛Žā˛Ļ⺁ ā˛Žā˛žā˛°āŗā˛—ā˛ĩā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_location": "ā˛¸āŗā˛Ĩ➺ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_more_users": "ā˛šāŗ†ā˛šāŗā˛šā˛ŋ➍ ā˛Ŧ➺➕⺆ā˛Ļā˛žā˛°ā˛°ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_partner": "ā˛Ēā˛žā˛˛āŗā˛Ļā˛žā˛°ā˛°ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_path": "ā˛šā˛žā˛Ļā˛ŋā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_photos": "ā˛Ģāŗ‹ā˛Ÿāŗ‹ā˛—ā˛ŗā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_to": "➏⺇➰ā˛ŋ➏ā˛ŋâ€Ļ", + "add_to_album": "ā˛†ā˛˛āŗā˛Ŧā˛Žāŗâ€Œā˛—āŗ† ➏⺇➰ā˛ŋ➏ā˛ŋ" +} diff --git a/i18n/ko.json b/i18n/ko.json index def2ad2a5e..38a88ecd3c 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -14,7 +14,7 @@ "add_a_location": "ėœ„ėš˜ ėļ”ę°€", "add_a_name": "ė´ëĻ„ ėļ”ę°€", "add_a_title": "렜ëĒŠ ėļ”ę°€", - "add_endpoint": "Add endpoint", + "add_endpoint": "ė—”ë“œíŦė¸íŠ¸ ėļ”ę°€", "add_exclusion_pattern": "ė œė™¸ ęˇœėš™ ėļ”ę°€", "add_import_path": "氀렏ė˜Ŧ ę˛Ŋ로 ėļ”ę°€", "add_location": "ėœ„ėš˜ ėļ”ę°€", @@ -25,74 +25,76 @@ "add_to": "ė•¨ë˛”ė— ėļ”ę°€â€Ļ", "add_to_album": "ė•¨ë˛”ė— ėļ”ę°€", "add_to_album_bottom_sheet_added": "{album}뗐 ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "add_to_album_bottom_sheet_already_exists": "{album}뗐 ė´ë¯¸ ėĄ´ėžŦ하는 항ëĒŠėž…ë‹ˆë‹¤.", + "add_to_album_bottom_sheet_already_exists": "{album}뗐 ė´ë¯¸ ėĄ´ėžŦ합니다.", + "add_to_locked_folder": "ėž ę¸´ 폴더로 ė´ë™", "add_to_shared_album": "ęŗĩ뜠 ė•¨ë˛”ė— ėļ”ę°€", "add_url": "URL ėļ”ę°€", "added_to_archive": "ëŗ´ę´€í•¨ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", "added_to_favorites": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė— 항ëĒŠ {count, number}氜 ėļ”가됨", + "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė— {count, number}氜 ėļ”가됨", "admin": { "add_exclusion_pattern_description": "ęˇœėš™ė— *, ** 및 ? ëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. ė´ëĻ„ė´ \"Raw\"ė¸ 디렉터ëĻŦė˜ ëĒ¨ë“  파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/Raw/**\"ëĨŧ, \".tif\"로 끝나는 ëĒ¨ë“  파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/*.tif\"ëĨŧ ė‚ŦėšŠí•˜ęŗ , ė ˆëŒ€ ę˛ŊëĄœė˜ ę˛Ŋ뚰 \"/path/to/ignore/**\"뙀 ę°™ė€ ë°Šė‹ėœŧ로 ė‚ŦėšŠí•Šë‹ˆë‹¤.", - "asset_offline_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ뗐 íŦ함된 ė´ 항ëĒŠė„ ë””ėŠ¤íŦė—ė„œ ë”ė´ėƒ ė°žė„ 눘 뗆떴 íœ´ė§€í†ĩėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다. 파ėŧė´ ëŧė´ë¸ŒëŸŦëĻŦ ë‚´ė—ė„œ ė´ë™ëœ ę˛Ŋ뚰 íƒ€ėž„ëŧė¸ė—ė„œ ėƒˆëĄœ ė—°ę˛°ëœ 항ëĒŠė„ í™•ė¸í•˜ė„¸ėš”. ė´ 항ëĒŠė„ ëŗĩė›í•˜ë ¤ëŠ´ ė•„ëž˜ 파ėŧ ę˛ŊëĄœė— Immich가 ė ‘ęˇŧ할 눘 ėžˆëŠ”ė§€ í™•ė¸í•˜ęŗ  ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė„ ė§„í–‰í•˜ė„¸ėš”.", + "asset_offline_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ뗐 íŦ함된 ė´ 항ëĒŠė„ ë””ėŠ¤íŦė—ė„œ ë”ė´ėƒ ė°žė„ 눘 뗆떴 íœ´ė§€í†ĩėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다. 파ėŧė´ ëŧė´ë¸ŒëŸŦëĻŦ ë‚´ė—ė„œ ė´ë™ëœ ę˛Ŋ뚰 íƒ€ėž„ëŧė¸ė—ė„œ ėƒˆëĄœ ė—°ę˛°ëœ 항ëĒŠė„ í™•ė¸í•˜ė„¸ėš”. 항ëĒŠė„ ëŗĩė›í•˜ë ¤ëŠ´ ė•„ëž˜ė˜ 파ėŧ ę˛ŊëĄœė— Immich가 ė ‘ęˇŧ할 눘 ėžˆëŠ”ė§€ í™•ė¸í•˜ęŗ  ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė„ ė§„í–‰í•˜ė„¸ėš”.", "authentication_settings": "ė¸ėĻ 네렕", "authentication_settings_description": "비밀번호, OAuth 및 기타 ė¸ėĻ 네렕 관ëĻŦ", - "authentication_settings_disable_all": "ëĄœęˇ¸ė¸ 기ëŠĨė„ ëĒ¨ë‘ ëš„í™œė„ąí™”í•˜ė‹œę˛ ėŠĩ니까? ëĄœęˇ¸ė¸í•˜ė§€ ė•Šė•„ë„ ė„œë˛„ė— ė ‘ęˇŧ할 눘 ėžˆėŠĩ니다.", - "authentication_settings_reenable": "ë‹¤ė‹œ í™œė„ąí™”í•˜ë ¤ëŠ´ ė„œë˛„ ėģ¤ë§¨ë“œëĨŧ ė‚ŦėšŠí•˜ė„¸ėš”.", + "authentication_settings_disable_all": "ëĄœęˇ¸ė¸ ėˆ˜ë‹¨ė„ ëĒ¨ë‘ ëš„í™œė„ąí™”í•˜ė‹œę˛ ėŠĩ니까? ëĄœęˇ¸ė¸ė´ ė™„ė „ížˆ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", + "authentication_settings_reenable": "ë‹¤ė‹œ í™œė„ąí™”í•˜ë ¤ëŠ´ ė„œë˛„ ëĒ…ë šė–´ëĨŧ ė‚ŦėšŠí•˜ė„¸ėš”.", "background_task_job": "밹꡸ëŧėš´ë“œ ėž‘ė—…", - "backup_database": "ë°ė´í„°ë˛ ė´ėŠ¤ ë°ąė—…", - "backup_database_enable_description": "ë°ė´í„°ë˛ ė´ėŠ¤ ë°ąė—… í™œė„ąí™”", - "backup_keep_last_amount": "ëŗ´ę´€í•  ë°ąė—…ė˜ 氜눘", - "backup_settings": "ë°ąė—… 네렕", - "backup_settings_description": "ë°ė´í„°ë˛ ė´ėŠ¤ ë°ąė—… 네렕 관ëĻŦ", + "backup_database": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ėƒė„ą", + "backup_database_enable_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 í™œė„ąí™”", + "backup_keep_last_amount": "ëŗ´ę´€í•  ė´ė „ ë¤í”„ė˜ 눘", + "backup_settings": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 네렕", + "backup_settings_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ė„¤ė •ė„ 관ëĻŦ합니다. 및溠: ė´ ėž‘ė—…ė€ ė§„í–‰ 및 ė‹¤íŒ¨ ė—Ŧëļ€ëĨŧ í™•ė¸í•  눘 ė—†ėŠĩ니다.", "check_all": "ëĒ¨ë‘ í™•ė¸", "cleanup": "ė •ëĻŦ", "cleared_jobs": "ėž‘ė—… ė¤‘ë‹¨: {job}", - "config_set_by_file": "현ėžŦ ė„¤ė •ė€ ęĩŦė„ą 파ėŧ뗐 ė˜í•´ 관ëĻŦ됩니다.", + "config_set_by_file": "현ėžŦ ęĩŦė„ąė€ 네렕 파ėŧė„ í†ĩ해 ė§€ė •ë˜ė–´ ėžˆėŠĩ니다.", "confirm_delete_library": "{library} ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "confirm_delete_library_assets": "ė´ ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? Immichė—ė„œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}가 ė‚­ė œë˜ëŠ° 되돌ëĻ´ 눘 ė—†ėŠĩ니다. ė›ëŗ¸ 파ėŧė€ ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", "confirm_email_below": "ęŗ„ė† ė§„í–‰í•˜ë ¤ëŠ´ ė•„ëž˜ė— \"{email}\" ėž…ë Ĩ", "confirm_reprocess_all_faces": "ëĒ¨ë“  ė–ŧęĩ´ė„ ë‹¤ė‹œ 래ëĻŦí•˜ė‹œę˛ ėŠĩ니까? ė´ëĻ„ė´ ė§€ė •ëœ ė¸ëŦŧė„ íŦ함한 ëĒ¨ë“  ė¸ëŦŧė´ ė‚­ė œëŠë‹ˆë‹¤.", "confirm_user_password_reset": "{user}ë‹˜ė˜ 비밀번호ëĨŧ ėžŦė„¤ė •í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_user_pin_code_reset": "{user}ë‹˜ė˜ PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í•˜ė‹œę˛ ėŠĩ니까?", "create_job": "ėž‘ė—… ėƒė„ą", "cron_expression": "Cron í‘œí˜„ė‹", "cron_expression_description": "Cron í˜•ė‹ė„ ė‚ŦėšŠí•˜ė—Ŧ 늤ėē” ėŖŧ기ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ėžė„¸í•œ ë‚´ėšŠęŗŧ ė˜ˆė‹œëŠ” Crontab GuruëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", "cron_expression_presets": "Cron í‘œí˜„ė‹ ė‚Ŧė „ 네렕", "disable_login": "ëĄœęˇ¸ė¸ ëš„í™œė„ąí™”", "duplicate_detection_job_description": "揰溄 학ėŠĩė„ í†ĩ해 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĨŧ ę°ė§€í•Šë‹ˆë‹¤. ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ í™œė„ąí™”ë˜ė–´ ėžˆė–´ė•ŧ 합니다.", - "exclusion_pattern_description": "ė œė™¸ ęˇœėš™ė„ ė‚ŦėšŠí•˜ė—Ŧ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” ė‹œ íŠšė • 파ėŧęŗŧ 폴더ëĨŧ ė œė™¸í•  눘 ėžˆėŠĩ니다. í´ë”ė— ė›í•˜ė§€ ė•ŠëŠ” 파ėŧ(RAW 파ėŧ 등)ė´ ėĄ´ėžŦ하는 ę˛Ŋ뚰 ėœ ėšŠí•Šë‹ˆë‹¤.", + "exclusion_pattern_description": "ė œė™¸ ęˇœėš™ė„ ė‚ŦėšŠí•˜ė—Ŧ íŠšė • 파ėŧęŗŧ 폴더ëĨŧ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė—ė„œ ė œė™¸í•  눘 ėžˆėŠĩ니다. ę°€ė ¸ė˜¤ę¸° ė›í•˜ė§€ ė•ŠëŠ” 파ėŧ(RAW 파ėŧ 등)ė´ í´ë”ė— ėĄ´ėžŦ하는 ę˛Ŋ뚰 ėœ ėšŠí•Šë‹ˆë‹¤.", "external_library_created_at": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ ({date}뗐 ėƒė„ąë¨)", "external_library_management": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 관ëĻŦ", "face_detection": "ė–ŧęĩ´ 氐맀", - "face_detection_description": "揰溄 학ėŠĩė„ í†ĩ해 항ëĒŠė— ėĄ´ėžŦ하는 ė–ŧęĩ´ė„ ę°ė§€í•Šë‹ˆë‹¤. ë™ė˜ėƒė˜ ę˛Ŋ뚰 ė„Ŧ네ėŧ만 ė‚ŦėšŠí•Šë‹ˆë‹¤. \"ėƒˆëĄœęŗ ėš¨\"ė€ ė´ë¯¸ 래ëĻŦ된 항ëĒŠė„ íŦ함한 ëĒ¨ë“  항ëĒŠė„ ë‹¤ė‹œ 래ëĻŦ합니다. \"ė´ˆę¸°í™”\"는 ëĒ¨ë“  ė–ŧęĩ´ ë°ė´í„°ëĨŧ ė‚­ė œí•Šë‹ˆë‹¤. \"누ëŊ\"ė€ 래ëĻŦë˜ė§€ ė•Šė€ 항ëĒŠė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤. ė–ŧęĩ´ 氐맀 ėž‘ė—…ė´ ė™„ëŖŒë˜ëŠ´ ė–ŧęĩ´ ė¸ė‹ ėž‘ė—…ė´ ė§„í–‰ë˜ė–´ ę°ė§€ëœ ė–ŧęĩ´ė„ ę¸°ėĄ´ ė¸ëŦŧė´ë‚˜ 냈 ė¸ëŦŧ로 ęˇ¸ëŖší™”í•Šë‹ˆë‹¤.", - "facial_recognition_job_description": "ę°ė§€ëœ ė–ŧęĩ´ė„ ė¸ëŦŧ로 ęˇ¸ëŖší™”í•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ ė–ŧęĩ´ 氐맀 ėž‘ė—…ė´ ė™„ëŖŒëœ 후 ė§„í–‰ëŠë‹ˆë‹¤. \"ė´ˆę¸°í™”\"는 ëĒ¨ë“  ė–ŧęĩ´ė˜ ęˇ¸ëŖší™”ëĨŧ ë‹¤ė‹œ ė§„í–‰í•Šë‹ˆë‹¤. \"누ëŊ\"ė€ ęˇ¸ëŖší™”ë˜ė§€ ė•Šė€ ė–ŧęĩ´ė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤.", + "face_detection_description": "揰溄 학ėŠĩė„ í†ĩ해 항ëĒŠė—ė„œ ė–ŧęĩ´ė„ ę°ė§€í•Šë‹ˆë‹¤. ë™ė˜ėƒė˜ ę˛Ŋ뚰 ė„Ŧ네ėŧ만 ëļ„ė„ė— ė‚ŦėšŠëŠë‹ˆë‹¤. \"ėƒˆëĄœęŗ ėš¨\"ė€ ëĒ¨ë“  항ëĒŠė„ (ėžŦ)래ëĻŦ하며, \"ė´ˆę¸°í™”\"는 현ėžŦ ëĒ¨ë“  ė–ŧęĩ´ ë°ė´í„°ëĨŧ ėļ”ę°€ëĄœ ė‚­ė œí•Šë‹ˆë‹¤. \"누ëŊ됨\"ė€ 땄링 래ëĻŦë˜ė§€ ė•Šė€ 항ëĒŠė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤. ė–ŧęĩ´ 氐맀氀 ė™„ëŖŒë˜ëŠ´ ę°ė§€ëœ ė–ŧęĩ´ë“¤ė€ ė–ŧęĩ´ ė¸ė‹ ë‹¨ęŗ„ëĄœ ë„˜ė–´ę°€ëŠ°, ę¸°ėĄ´ ė¸ëŦŧė´ë‚˜ ėƒˆëĄœėš´ ė¸ëŦŧ로 ęˇ¸ëŖší™”ëŠë‹ˆë‹¤.", + "facial_recognition_job_description": "ę°ė§€ëœ ė–ŧęĩ´ė„ ė¸ëŦŧëŗ„ëĄœ ęˇ¸ëŖší™”í•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ ė–ŧęĩ´ 氐맀 ėž‘ė—…ė´ ė™„ëŖŒëœ 후 ė§„í–‰ëŠë‹ˆë‹¤. \"ė´ˆę¸°í™”\"는 ëĒ¨ë“  ė–ŧęĩ´ė˜ ęˇ¸ëŖší™”ëĨŧ ë‹¤ė‹œ ė§„í–‰í•Šë‹ˆë‹¤. \"누ëŊ\"ė€ ęˇ¸ëŖší™”ë˜ė§€ ė•Šė€ ė–ŧęĩ´ė„ ëŒ€ę¸°ė—´ė— ėļ”ę°€í•Šë‹ˆë‹¤.", "failed_job_command": "{job} ėž‘ė—…ė—ė„œ {command} ė‹¤íŒ¨", - "force_delete_user_warning": "ę˛Ŋęŗ : ė‚ŦėšŠėž 및 ė‚ŦėšŠėžę°€ ė—…ëĄœë“œí•œ ëĒ¨ë“  항ëĒŠė´ ėĻ‰ė‹œ ė‚­ė œëŠë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėœŧ늰 파ėŧė„ ëŗĩęĩŦ할 눘 ė—†ėŠĩ니다.", - "forcing_refresh_library_files": "ëŧė´ë¸ŒëŸŦëĻŦė˜ ëĒ¨ë“  파ėŧė„ ë‹¤ė‹œ 늤ėē”하는 뤑...", + "force_delete_user_warning": "ę˛Ŋęŗ : ė´ ėž‘ė—…ė€ 해당 ė‚ŦėšŠėžė™€ ꡸ ė‚ŦėšŠėžę°€ ė†Œėœ í•œ ëĒ¨ë“  항ëĒŠė„ ėĻ‰ė‹œ ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėœŧ늰, ė‚­ė œëœ 파ėŧė€ ëŗĩęĩŦ할 눘 ė—†ėŠĩ니다.", + "forcing_refresh_library_files": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ 파ėŧ ę°•ė œ ėƒˆëĄœęŗ ėš¨ 뤑...", "image_format": "í˜•ė‹", - "image_format_description": "WebP는 JPEGëŗ´ë‹¤ 파ėŧ íŦ기가 ėž‘ė§€ë§Œ ëŗ€í™˜ė— 더 ë§Žė€ ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤.", - "image_fullsize_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 풀ė‚Ŧė´ėψ ė´ë¯¸ė§€ (확대 ė‹œ ė‚ŦėšŠ)", - "image_fullsize_enabled": "풀ė‚Ŧė´ėψ ė´ë¯¸ė§€ ėƒė„ą í™œė„ąí™”", - "image_fullsize_enabled_description": "ė›š ėšœí™”ė ė´ė§€ ė•Šė€ í˜•ė‹ė˜ ę˛Ŋ뚰 풀ė‚Ŧė´ėψ ė´ë¯¸ė§€ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤. 'ėž„ë˛ ë“œëœ 미ëĻŦëŗ´ę¸° ė„ í˜¸'ëĨŧ í™œė„ąí™”í•˜ëŠ´ ëŗ€í™˜ ė—†ė´ ėž„ë˛ ë“œëœ 미ëĻŦëŗ´ę¸°ę°€ 바로 ė‚ŦėšŠëŠë‹ˆë‹¤. JPEG뙀 ę°™ė€ ė›š ėšœí™”ė ė¸ í˜•ė‹ė—ëŠ” 똁í–Ĩė„ ë¯¸ėš˜ė§€ ė•ŠėŠĩ니다.", - "image_fullsize_quality_description": "풀ė‚Ŧė´ėψ ė´ë¯¸ė§€ í’ˆė§ˆė€ 1~100ėž…ë‹ˆë‹¤. ë†’ė„ėˆ˜ëĄ ėĸ‹ė§€ë§Œ 파ėŧė´ ėģ¤ė§‘니다.", - "image_fullsize_title": "풀ė‚Ŧė´ėψ ė´ë¯¸ė§€ 네렕", - "image_prefer_embedded_preview": "íŦ함된 미ëĻŦ ëŗ´ę¸° ė„ í˜¸", - "image_prefer_embedded_preview_setting_description": "가ëŠĨ한 ę˛Ŋ뚰 ė´ë¯¸ė§€ 래ëĻŦ ė‹œ RAW ė‚Ŧ맄뗐 íŦ함된 미ëĻŦ ëŗ´ę¸°ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. íŦ함된 미ëĻŦ ëŗ´ę¸°ëŠ” ėš´ëŠ”ëŧė—ė„œ ėƒė„ąëœ 것ėœŧ로 ėš´ëŠ”ëŧ마다 í’ˆė§ˆė´ 다ëĻ…ë‹ˆë‹¤. ėŧëļ€ ė´ë¯¸ė§€ė˜ ę˛Ŋ뚰 더 ė •í™•í•œ ėƒ‰ėƒė´ 표현될 눘 ėžˆė§€ë§Œ 반대로 더 ë§Žė€ ė•„í‹°íŒŠíŠ¸ę°€ ėžˆė„ ėˆ˜ë„ ėžˆėŠĩ니다.", - "image_prefer_wide_gamut": "ë„“ė€ ėƒ‰ ė˜ė—­ ė„ í˜¸", - "image_prefer_wide_gamut_setting_description": "ė„Ŧ네ėŧ ė´ë¯¸ė§€ė— Display P3ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. ë§Žė€ ėƒ‰ėƒė„ 표현할 눘 ėžˆė–´ 더 ė •í™•í•œ í‘œí˜„ė´ 가ëŠĨí•˜ė§€ë§Œ, ė˜¤ëž˜ëœ 브ëŧėš°ė €ëĨŧ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ė´ë¯¸ė§€ę°€ 다ëĨ´ę˛Œ ëŗ´ėŧ 눘 ėžˆėŠĩ니다. ėƒ‰ėƒ ė™œęŗĄė„ ë°Šė§€í•˜ę¸° ėœ„í•´ sRGB ė´ë¯¸ė§€ëŠ” ė´ ė„¤ė •ė´ ė ėšŠë˜ė§€ ė•ŠėŠĩ니다.", - "image_preview_description": "ëŠ”íƒ€ë°ė´í„°ëĨŧ ė œęą°í•œ 뤑氄 íŦę¸°ė˜ ė´ë¯¸ė§€, 단ėŧ 항ëĒŠė„ ëŗ´ëŠ” ę˛Ŋ뚰 및 揰溄 학ėŠĩ뗐 ė‚ŦėšŠë¨", - "image_preview_quality_description": "1ëļ€í„° 100 ė‚Ŧė´ė˜ 미ëĻŦëŗ´ę¸° í’ˆė§ˆ. ę°’ė´ ë†’ė„ėˆ˜ëĄ ėĸ‹ė§€ë§Œ 파ėŧ íŦ기가 ėģ¤ė ¸ ė•ąė˜ ë°˜ė‘ė„ąė´ ë–¨ė–´ė§ˆ 눘 ėžˆėœŧ늰, ę°’ė´ 낮ėœŧ늴 揰溄 학ėŠĩė˜ í’ˆė§ˆė´ ë–¨ė–´ė§ˆ 눘 ėžˆėŠĩ니다.", + "image_format_description": "WebP는 JPEGëŗ´ë‹¤ 파ėŧ íŦ기가 ėž‘ė§€ë§Œ, ė¸ėŊ”ë”Šė— 더 ë§Žė€ ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤.", + "image_fullsize_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 렄랴 íŦ기 ė´ë¯¸ė§€ëĄœ, 확대 ëŗ´ę¸° ė‹œ ė‚ŦėšŠëŠë‹ˆë‹¤.", + "image_fullsize_enabled": "렄랴 íŦ기 ė´ë¯¸ė§€ ėƒė„ą í™œė„ąí™”", + "image_fullsize_enabled_description": "ė›šė— ė í•Ší•˜ė§€ ė•Šė€ í˜•ė‹ė¸ ę˛Ŋ뚰 렄랴 íŦ기 ė´ë¯¸ė§€ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤. \"내ėžĨ 미ëĻŦëŗ´ę¸° ėš°ė„  ė‚ŦėšŠ\"ė´ í™œė„ąí™”ë˜ė–´ ėžˆėœŧ늴, ëŗ€í™˜ ė—†ė´ 내ėžĨ된 미ëĻŦëŗ´ę¸°ëĨŧ 그대로 ė‚ŦėšŠí•Šë‹ˆë‹¤. JPEGęŗŧ ę°™ė€ ė›š ėšœí™”ė ė¸ í˜•ė‹ė—ëŠ” 똁í–Ĩė„ ėŖŧė§€ ė•ŠėŠĩ니다.", + "image_fullsize_quality_description": "렄랴 íŦ기 ė´ë¯¸ė§€ė˜ í’ˆė§ˆ (1~100). ėˆĢėžę°€ ë†’ė„ėˆ˜ëĄ í’ˆė§ˆė´ ėĸ‹ė§€ë§Œ 파ėŧ íŦ기도 ėģ¤ė§‘니다.", + "image_fullsize_title": "렄랴 íŦ기 ė´ë¯¸ė§€ 네렕", + "image_prefer_embedded_preview": "내ėžĨ 미ëĻŦëŗ´ę¸° ėš°ė„  ė‚ŦėšŠ", + "image_prefer_embedded_preview_setting_description": "RAW ė‚Ŧ맄뗐 íŦ함된 내ėžĨ 미ëĻŦëŗ´ę¸°ëĨŧ ė´ë¯¸ė§€ 래ëĻŦ ė‹œ ėž…ë Ĩėœŧ로 ė‚ŦėšŠí•Šë‹ˆë‹¤(ė‚ŦėšŠ 가ëŠĨ한 ę˛Ŋėš°ė— 한함). ė´ ë°Šė‹ė€ ėŧëļ€ ė´ë¯¸ė§€ė—ė„œ 더 ė •í™•í•œ ėƒ‰ėƒė„ ė–ģė„ 눘 ėžˆė§€ë§Œ, 미ëĻŦëŗ´ę¸°ė˜ í’ˆė§ˆė€ ėš´ëŠ”ëŧ뗐 따ëŧ 다ëĨ´ëа ė••ėļ•ėœŧ로 ė¸í•œ í’ˆė§ˆ ė €í•˜ę°€ 나타날 눘 ėžˆėŠĩ니다.", + "image_prefer_wide_gamut": "ę´‘ėƒ‰ė—­ ėš°ė„  ė‚ŦėšŠ", + "image_prefer_wide_gamut_setting_description": "ė„Ŧ네ėŧ뗐 Display P3 ėƒ‰ė—­ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤. ę´‘ėƒ‰ė—­ ė´ë¯¸ė§€ëĨŧ ëŗ´ë‹¤ ėƒėƒí•˜ę˛Œ 표현할 눘 ėžˆė§€ë§Œ, ęĩŦ형 브ëŧėš°ė €ë‚˜ ėžĨėš˜ė—ė„œëŠ” 다ëĨ´ę˛Œ ëŗ´ėŧ 눘 ėžˆėŠĩ니다. sRGB ė´ë¯¸ė§€ė˜ ę˛Ŋ뚰 ėƒ‰ėƒ ė™œęŗĄė„ ë°Šė§€í•˜ę¸° ėœ„í•´ 그대로 ėœ ė§€ëŠë‹ˆë‹¤.", + "image_preview_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 뤑氄 íŦ기 ė´ë¯¸ė§€ëĄœ, 단ėŧ 항ëĒŠė„ ëŗ´ęą°ë‚˜ 揰溄 학ėŠĩ뗐 ė‚ŦėšŠëŠë‹ˆë‹¤.", + "image_preview_quality_description": "미ëĻŦëŗ´ę¸° í’ˆė§ˆ (1~100). ėˆĢėžę°€ ë†’ė„ėˆ˜ëĄ í’ˆė§ˆė´ ėĸ‹ė•„ė§€ė§€ë§Œ, 파ėŧ íŦ기가 ėģ¤ė§€ęŗ  ė•ą ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다. 너ëŦ´ 낮게 ė„¤ė •í•˜ëŠ´ 揰溄 학ėŠĩ í’ˆė§ˆė— 똁í–Ĩė„ 뤄 눘 ėžˆėŠĩ니다.", "image_preview_title": "미ëĻŦëŗ´ę¸° 네렕", "image_quality": "í’ˆė§ˆ", "image_resolution": "í•´ėƒë„", - "image_resolution_description": "í•´ėƒë„ę°€ ë†’ė„ ėˆ˜ëĄ 디테ėŧė´ ëŗ´ėĄ´ë˜ė§€ë§Œ 파ėŧė´ íŦęŗ  ė¸ėŊ”ë”Šė´ ė˜¤ëž˜ 깸ëĻŦ늰 ė•ą ė‘ë‹ĩė„ąė´ ë–¨ė–´ė§ˆ 눘 ėžˆėŠĩ니다.", + "image_resolution_description": "í•´ėƒë„ę°€ ë†’ė„ėˆ˜ëĄ 넏ëļ€ ė •ëŗ´ę°€ ėž˜ ëŗ´ėĄ´ë˜ė§€ë§Œ, ė¸ėŊ”ë”Šė´ ëŠë ¤ė§€ęŗ  파ėŧė´ ėģ¤ė§€ëа ė•ą ë°˜ė‘ ė†ë„ę°€ ë–¨ė–´ė§ˆ 눘 ėžˆėŠĩ니다.", "image_settings": "ė´ë¯¸ė§€ 네렕", "image_settings_description": "ėƒė„ąëœ ė´ë¯¸ė§€ė˜ í’ˆė§ˆ 및 í•´ėƒë„ 관ëĻŦ", - "image_thumbnail_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ ėž‘ė€ ė„Ŧ네ėŧ ė´ë¯¸ė§€, íƒ€ėž„ëŧė¸ 등 ė‚Ŧė§„ė„ ęˇ¸ëŖší™”í•˜ė—Ŧ ëŗ´ëŠ” ę˛Ŋėš°ė— ė‚ŦėšŠë¨", - "image_thumbnail_quality_description": "ė„Ŧ네ėŧ í’ˆė§ˆ(1~100). ë†’ė„ėˆ˜ëĄ ėĸ‹ė§€ë§Œ 파ėŧíŦ기가 ėģ¤ė ¸ ė•ąė˜ ë°˜ė‘ė„ąė´ ë–¨ė–´ė§ˆ 눘 ėžˆėŠĩ니다.", + "image_thumbnail_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ ėž‘ė€ ė„Ŧ네ėŧ로, ëŠ”ė¸ íƒ€ėž„ëŧė¸ ë“ąė—ė„œ ė—ŦëŸŦ 항ëĒŠė„ ëŗŧ 때 ė‚ŦėšŠëŠë‹ˆë‹¤.", + "image_thumbnail_quality_description": "ė„Ŧ네ėŧ í’ˆė§ˆ (1~100). ėˆĢėžę°€ ë†’ė„ėˆ˜ëĄ í””ė§ˆė´ ėĸ‹ė§€ë§Œ, 파ėŧ íŦ기가 ėģ¤ė§€ęŗ  ė•ą ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", "image_thumbnail_title": "ė„Ŧ네ėŧ 네렕", "job_concurrency": "{job} ë™ė‹œė„ą", "job_created": "ėž‘ė—…ė´ ėƒė„ąë˜ė—ˆėŠĩ니다.", - "job_not_concurrency_safe": "ė´ ėž‘ė—…ė€ ë™ė‹œ ė‹¤í–‰ė´ ė œí•œëŠë‹ˆë‹¤.", + "job_not_concurrency_safe": "ė´ ėž‘ė—…ė€ ë™ė‹œ ė‹¤í–‰ė— ė•ˆė „í•˜ė§€ ė•ŠėŠĩ니다.", "job_settings": "ėž‘ė—… 네렕", "job_settings_description": "ėž‘ė—… ë™ė‹œė„ą 관ëĻŦ", "job_status": "ėž‘ė—… ėƒíƒœ", @@ -100,25 +102,25 @@ "jobs_failed": "{jobCount, plural, other {#氜}} ė‹¤íŒ¨", "library_created": "{library} ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒė„ąí–ˆėŠĩ니다.", "library_deleted": "ëŧė´ë¸ŒëŸŦëĻŦ가 ė‚­ė œë˜ė—ˆėŠĩ니다.", - "library_import_path_description": "氀렏ė˜Ŧ 폴더ëĨŧ ė„ íƒí•˜ė„¸ėš”. ė„ íƒí•œ 폴더 및 í•˜ėœ„ í´ë”ė—ė„œ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ 늤ėē”핊니다.", + "library_import_path_description": "氀렏ė˜Ŧ 폴더ëĨŧ ė§€ė •í•˜ė„¸ėš”. 해당 í´ë”ė™€ ëĒ¨ë“  í•˜ėœ„ í´ë”ė—ė„œ ė´ë¯¸ė§€ė™€ ë™ė˜ėƒė„ 늤ėē”핊니다.", "library_scanning": "ėŖŧ揰렁 늤ėē”", - "library_scanning_description": "ėŖŧę¸°ė ė¸ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” ęĩŦė„ą", - "library_scanning_enable_description": "ėŖŧę¸°ė ė¸ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” í™œė„ąí™”", + "library_scanning_description": "ëŧė´ë¸ŒëŸŦëĻŦ ėŖŧ揰렁 늤ėē” ęĩŦė„ą", + "library_scanning_enable_description": "ëŧė´ë¸ŒëŸŦëĻŦ ėŖŧ揰렁 늤ėē” í™œė„ąí™”", "library_settings": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ", "library_settings_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 네렕 관ëĻŦ", - "library_tasks_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė—ė„œ 냈 ėžė‚° 및/또는 ëŗ€ę˛Ŋ된 ėžė‚°ė„ ę˛€ėƒ‰í•Šë‹ˆë‹¤", - "library_watching_enable_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė˜ 파ėŧ ëŗ€ę˛Ŋ ę°ė‹œ", - "library_watching_settings": "ëŧė´ë¸ŒëŸŦëĻŦ ę°ė‹œ (ė‹¤í—˜ 기ëŠĨ)", - "library_watching_settings_description": "파ėŧ ëŗ€ę˛…ė„ ėžë™ėœŧ로 氐맀", - "logging_enable_description": "로그 기록 í™œė„ąí™”", - "logging_level_description": "í™œė„ąí™”ëœ ę˛Ŋ뚰 ė‚ŦėšŠí•  로그 ë ˆë˛¨ė„ ė„ íƒí•Šë‹ˆë‹¤.", - "logging_settings": "로그 네렕", + "library_tasks_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė—ė„œ 냈 항ëĒŠ 또는 ëŗ€ę˛Ŋ된 항ëĒŠė„ 늤ėē”", + "library_watching_enable_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 파ėŧ ëŗ€ę˛Ŋ ę°ė‹œ", + "library_watching_settings": "ëŧė´ë¸ŒëŸŦëĻŦ ę°ė‹œ (ė‹¤í—˜ė )", + "library_watching_settings_description": "파ėŧ ëŗ€ę˛Ŋ ėžë™ 氐맀", + "logging_enable_description": "로깅 í™œė„ąí™”", + "logging_level_description": "í™œė„ąí™” ė‹œ ė‚ŦėšŠí•  로그 ë ˆë˛¨ė„ ė„ íƒí•Šë‹ˆë‹¤.", + "logging_settings": "로깅", "machine_learning_clip_model": "CLIP ëĒ¨ë¸", - "machine_learning_clip_model_description": "CLIP ëĒ¨ë¸ė˜ ėĸ…ëĨ˜ëŠ” ė´ęŗŗė„ ė°¸ėĄ°í•˜ė„¸ėš”. 한ęĩ­ė–´ 등 다ęĩ­ė–´ ę˛€ėƒ‰ė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Multilingual CLIP ëĒ¨ë¸ė„ ė„ íƒí•˜ė„¸ėš”. ëĒ¨ë¸ė„ ëŗ€ę˛Ŋ한 후 ëĒ¨ë“  항ëĒŠė— 대한 ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ ėž‘ė—…ė„ ë‹¤ė‹œ ė§„í–‰í•´ė•ŧ 합니다.", - "machine_learning_duplicate_detection": "ëš„ėŠˇí•œ 항ëĒŠ 氐맀", - "machine_learning_duplicate_detection_enabled": "ëš„ėŠˇí•œ 항ëĒŠ 氐맀 í™œė„ąí™”", + "machine_learning_clip_model_description": "CLIP ëĒ¨ë¸ė˜ ėĸ…ëĨ˜ëŠ” ė´ęŗŗė„ ė°¸ėĄ°í•˜ė„¸ėš”. 한ęĩ­ė–´ëĨŧ íŦ함한 다ęĩ­ė–´ ę˛€ėƒ‰ė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Multilingual CLIP ëĒ¨ë¸ė„ ė„ íƒí•˜ė„¸ėš”. ëĒ¨ë¸ė„ ëŗ€ę˛Ŋ한 ę˛Ŋ뚰, ëĒ¨ë“  항ëĒŠė— 대해 'ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰' ėž‘ė—…ė„ ë‹¤ė‹œ ė‹¤í–‰í•´ė•ŧ 합니다.", + "machine_learning_duplicate_detection": "뤑ëŗĩ 氐맀", + "machine_learning_duplicate_detection_enabled": "뤑ëŗĩ 氐맀 í™œė„ąí™”", "machine_learning_duplicate_detection_enabled_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋėš°ė—ë„ ė™„ė „ížˆ 동ėŧ한 항ëĒŠė€ 뤑ëŗĩ ė œęą°ëŠë‹ˆë‹¤.", - "machine_learning_duplicate_detection_setting_description": "CLIP ėž„ë˛ ë”Šė„ ė‚ŦėšŠí•˜ė—Ŧ ëš„ėŠˇí•œ 항ëĒŠ ė°žę¸°", + "machine_learning_duplicate_detection_setting_description": "CLIP ėž„ë˛ ë”Šė„ ė‚ŦėšŠí•˜ė—Ŧ 뤑ëŗĩ 가ëŠĨė„ąė´ ë†’ė€ 항ëĒŠ ė°žę¸°", "machine_learning_enabled": "揰溄 학ėŠĩ í™œė„ąí™”", "machine_learning_enabled_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė•„ëž˜ 네렕 ė—Ŧëļ€ė™€ ę´€ęŗ„ė—†ė´ ëĒ¨ë“  揰溄 학ėŠĩ 기ëŠĨė´ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", "machine_learning_facial_recognition": "ė–ŧęĩ´ ė¸ė‹", @@ -128,7 +130,7 @@ "machine_learning_facial_recognition_setting": "ė–ŧęĩ´ ė¸ė‹ í™œė„ąí™”", "machine_learning_facial_recognition_setting_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė´ë¯¸ė§€ė—ė„œ ė–ŧęĩ´ ė¸ė‹ė„ ė§„í–‰í•˜ė§€ ė•Šėœŧ늰, íƒėƒ‰ íŽ˜ė´ė§€ė— ė¸ëŦŧ ëĒŠëĄė´ í‘œė‹œë˜ė§€ ė•ŠėŠĩ니다.", "machine_learning_max_detection_distance": "ėĩœëŒ€ 氐맀 ęą°ëĻŦ", - "machine_learning_max_detection_distance_description": "두 ė´ë¯¸ė§€ëĨŧ 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĄœ 간ėŖŧ하는 ęą°ëĻŦė˜ ėĩœëŒ“ę°’ė„ 0.001ė—ė„œ 0.1 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 높ėœŧ늴 ë¯ŧ감도가 ë‚Žė•„ė ¸ 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĄœ ę°ė§€í•˜ëŠ” ëš„ėœ¨ė´ ë†’ė•„ė§€ë‚˜, ėž˜ëĒģ된 결ęŗŧëĨŧ ëŗ´ėŧ 눘 ėžˆėŠĩ니다.", + "machine_learning_max_detection_distance_description": "두 ė´ë¯¸ė§€ëĨŧ 뤑ëŗĩėœŧ로 간ėŖŧ하기 ėœ„í•œ ėĩœëŒ€ ęą°ëĻŋ값 (0.001~0.1). ę°’ė´ í´ėˆ˜ëĄ 더 ë§Žė€ 뤑ëŗĩė„ ę°ė§€í•  눘 ėžˆė§€ë§Œ, ėž˜ëĒģ된 氐맀氀 ë°œėƒí•  눘 ėžˆėŠĩ니다.", "machine_learning_max_recognition_distance": "ėĩœëŒ€ ė¸ė‹ ęą°ëĻŦ", "machine_learning_max_recognition_distance_description": "두 ė–ŧęĩ´ė„ 동ėŧė¸ėœŧ로 ė¸ė‹í•˜ëŠ” ęą°ëĻŦė˜ ėĩœëŒ“ę°’ė„ 0ė—ė„œ 2 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ė´ ę°’ė„ 낮ėļ”늴 다ëĨ¸ ė¸ëŦŧė„ 동ėŧė¸ėœŧ로 ė¸ė‹í•˜ëŠ” ę˛ƒė„ ë°Šė§€í•  눘 ėžˆęŗ , ę°’ė„ ë†’ė´ëŠ´ 동ėŧė¸ė„ 다ëĨ¸ ė¸ëŦŧ로 ė¸ė‹í•˜ëŠ” ę˛ƒė„ ë°Šė§€í•  눘 ėžˆėŠĩ니다. 두 ė¸ëŦŧė„ ëŗ‘í•Ší•˜ëŠ” ę˛ƒė´ 한 ė¸ëŦŧė„ 두 ëĒ…ėœŧ로 ëļ„ëĻŦ하는 ę˛ƒëŗ´ë‹¤ ė‰Ŧėš°ë¯€ëĄœ, 가ëŠĨ한 ë‚Žė€ ėž„ęŗ„ę°’ė„ ė‚ŦėšŠí•˜ė„¸ėš”.", "machine_learning_min_detection_score": "ėĩœė†Œ ė‹ ëĸ°ë„ 렐눘", @@ -138,7 +140,7 @@ "machine_learning_settings": "揰溄 학ėŠĩ 네렕", "machine_learning_settings_description": "揰溄 학ėŠĩ 기ëŠĨ 및 네렕 관ëĻŦ", "machine_learning_smart_search": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰", - "machine_learning_smart_search_description": "CLIP ėž„ë˛ ë”Šėœŧ로 ėžė—°ė–´ëĨŧ ė‚ŦėšŠí•˜ė—Ŧ ė´ë¯¸ė§€ ę˛€ėƒ‰", + "machine_learning_smart_search_description": "CLIP ėž„ë˛ ë”Šė„ ė‚ŦėšŠí•œ ë‚´ėšŠ 기반 ė´ë¯¸ė§€ ę˛€ėƒ‰", "machine_learning_smart_search_enabled": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ í™œė„ąí™”", "machine_learning_smart_search_enabled_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė„ ėœ„í•œ ė´ë¯¸ė§€ 래ëĻŦëĨŧ ė§„í–‰í•˜ė§€ ė•ŠėŠĩ니다.", "machine_learning_url_description": "揰溄 학ėŠĩ ė„œë˛„ė˜ URLė„ ėž…ë Ĩ합니다. URLė´ ė—ŦëŸŦ ę°œė¸ ę˛Ŋ뚰 ė˛Ģ ë˛ˆė§¸ ė„œë˛„ëļ€í„° ë§ˆė§€ë§‰ęšŒė§€ ė„ąęŗĩ렁ėœŧ로 ė‘ë‹ĩ할 ë•ŒęšŒė§€ 한 ë˛ˆė— í•˜ë‚˜ė”Š ėˆœė„œëŒ€ëĄœ ėš”ė˛­ė„ ė‹œë„í•Šë‹ˆë‹¤. ė‘ë‹ĩí•˜ė§€ ė•ŠëŠ” ė„œë˛„ëŠ” ë‹¤ė‹œ ė‚ŦėšŠ 가ëŠĨ할 ë•ŒęšŒė§€ ėŧė‹œė ėœŧ로 ė œė™¸ëŠë‹ˆë‹¤.", @@ -157,8 +159,8 @@ "map_settings": "ė§€ë„", "map_settings_description": "ė§€ë„ 네렕 관ëĻŦ", "map_style_description": "ė§€ë„ 테마 style.json URL", - "memory_cleanup_job": "메ëǍëĻŦ ė •ëĻŦ", - "memory_generate_job": "메ëǍëĻŦ ėƒė„ą", + "memory_cleanup_job": "ėļ”ė–ĩ ė •ëĻŦ", + "memory_generate_job": "ėļ”ė–ĩ ėƒė„ą", "metadata_extraction_job": "ëŠ”íƒ€ë°ė´í„° ėļ”ėļœ", "metadata_extraction_job_description": "각 항ëĒŠė—ė„œ GPS, ė¸ëŦŧ 및 í•´ėƒë„ ë“ąė˜ ëŠ”íƒ€ë°ė´í„° ė •ëŗ´ ėļ”ėļœ", "metadata_faces_import_setting": "ė–ŧęĩ´ ę°€ė ¸ė˜¤ę¸° í™œė„ąí™”", @@ -192,26 +194,22 @@ "oauth_auto_register": "ėžë™ ę°€ėž…", "oauth_auto_register_description": "OAuth로 냈 ė‚ŦėšŠėžę°€ ëĄœęˇ¸ė¸í•˜ëŠ” ę˛Ŋ뚰 ėžë™ėœŧ로 ę°€ėž…", "oauth_button_text": "버íŠŧ í…ėŠ¤íŠ¸", - "oauth_client_id": "클ëŧė´ė–¸íŠ¸ ID", - "oauth_client_secret": "클ëŧė´ė–¸íŠ¸ ė‹œíŦëĻŋ", + "oauth_client_secret_description": "OAuth 렜ęŗĩėžę°€ PKCE(Proof Key for Code Exchange)ëĨŧ ė§€ė›í•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í•„ėš”í•Šë‹ˆë‹¤.", "oauth_enable_description": "OAuth ëĄœęˇ¸ė¸", - "oauth_issuer_url": "ë°œę¸‰ėž URL", "oauth_mobile_redirect_uri": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI", "oauth_mobile_redirect_uri_override": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI ėžŦė •ė˜", "oauth_mobile_redirect_uri_override_description": "OAuth ęŗĩę¸‰ėžę°€ '{callback}'ęŗŧ ę°™ė€ ëĒ¨ë°”ėŧ URIëĨŧ 렜ęŗĩí•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", - "oauth_profile_signing_algorithm": "ė‚ŦėšŠėž ė •ëŗ´ ė„œëĒ… ė•Œęŗ ëĻŦėϘ", - "oauth_profile_signing_algorithm_description": "ė‚ŦėšŠėž ė •ëŗ´ ė„œëDž뗐 ė‚ŦėšŠë˜ëŠ” ė•Œęŗ ëĻŦėĻ˜ė„ ė„ íƒí•Šë‹ˆë‹¤.", - "oauth_scope": "늤ėŊ”프", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth ëĄœęˇ¸ė¸ 네렕 관ëĻŦ", "oauth_settings_more_details": "ė´ 기ëŠĨ뗐 대한 ėžė„¸í•œ ë‚´ėšŠė€ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", - "oauth_signing_algorithm": "ė„œëĒ… ė•Œęŗ ëĻŦėϘ", "oauth_storage_label_claim": "ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸” ė„ íƒ", "oauth_storage_label_claim_description": "ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ė„ ė‚ŦėšŠėžę°€ ėž…ë Ĩ한 값ėœŧ로 ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", "oauth_storage_quota_claim": "ėŠ¤í† ëĻŦė§€ 할당량 ė„ íƒ", "oauth_storage_quota_claim_description": "ėŠ¤í† ëĻŦė§€ í• ë‹šëŸ‰ė„ ė‚ŦėšŠėžę°€ ėž…ë Ĩ한 값ėœŧ로 ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", "oauth_storage_quota_default": "ėŠ¤í† ëĻŦė§€ 할당량 ę¸°ëŗ¸ę°’ (GiB)", "oauth_storage_quota_default_description": "ėž…ë Ĩí•˜ė§€ ė•Šė€ ę˛Ŋ뚰 ė‚ŦėšŠí•  GiB ë‹¨ėœ„ė˜ ę¸°ëŗ¸ 할당량 (ëŦ´ė œí•œ í• ë‹šëŸ‰ė˜ ę˛Ŋ뚰 0 ėž…ë Ĩ)", + "oauth_timeout": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ", + "oauth_timeout_description": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ (밀ëĻŦ봈 ë‹¨ėœ„)", "offline_paths": "누ëŊ된 파ėŧ", "offline_paths_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė˜ 항ëĒŠė´ ė•„ë‹Œ 파ėŧė„ ėˆ˜ë™ėœŧ로 ė‚­ė œí•œ ę˛Ŋ뚰 ë°œėƒí•  눘 ėžˆėŠĩ니다.", "password_enable_description": "ė´ëŠ”ėŧęŗŧ 비밀번호로 ëĄœęˇ¸ė¸", @@ -222,20 +220,20 @@ "quota_size_gib": "할당량 (GiB)", "refreshing_all_libraries": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ ë‹¤ė‹œ 늤ėē” ė¤‘...", "registration": "관ëĻŦėž ęŗ„ė • ėƒė„ą", - "registration_description": "ė˛Ģ ë˛ˆė§¸ëĄœ ėƒė„ąë˜ëŠ” ė‚ŦėšŠėžëŠ” 관ëĻŦėž ęļŒí•œė„ ëļ€ė—Ŧ받ėœŧ늰, 관ëĻŦ 및 ė‚ŦėšŠėž ėƒė„ąė´ 가ëŠĨ합니다.", + "registration_description": "ë‹šė‹ ė€ ė˛Ģ ë˛ˆė§¸ ė‚ŦėšŠėžė´ëŠ° 관ëĻŦėž ęļŒí•œė´ ėžë™ėœŧ로 ëļ€ė—Ŧ됩니다. 관ëĻŦ ėž‘ė—…ęŗŧ ė‚ŦėšŠėž ėƒė„ąė´ 가ëŠĨ합니다.", "repair_all": "ëĒ¨ë‘ 눘ëĻŦ", - "repair_matched_items": "동ėŧ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ í™•ė¸í–ˆėŠĩ니다.", - "repaired_items": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ 눘ëĻŦ했ėŠĩ니다.", + "repair_matched_items": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 ėŧėš˜í•Šë‹ˆë‹¤.", + "repaired_items": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ëŗĩęĩŦ했ėŠĩ니다.", "require_password_change_on_login": "ė˛Ģ ëĄœęˇ¸ė¸ ė‹œ 비밀번호 ëŗ€ę˛Ŋ ėš”ęĩŦ", "reset_settings_to_default": "ė„¤ė •ė„ ę¸°ëŗ¸ę°’ėœŧ로 ëŗĩ뛐", - "reset_settings_to_recent_saved": "ë§ˆė§€ë§‰ėœŧ로 ė €ėžĨ된 네렕ėœŧ로 ëŗĩ뛐", + "reset_settings_to_recent_saved": "ë§ˆė§€ë§‰ėœŧ로 ė €ėžĨ된 네렕 ëŗĩ뛐", "scanning_library": "ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” ė¤‘", "search_jobs": "ėž‘ė—… ę˛€ėƒ‰â€Ļ", "send_welcome_email": "í™˜ė˜ ė´ëŠ”ėŧ ė „ė†Ą", "server_external_domain_settings": "뙏ëļ€ ë„ëŠ”ė¸", "server_external_domain_settings_description": "ęŗĩ氜 ęŗĩ뜠 링íŦ뗐 ė‚ŦėšŠí•  ë„ëŠ”ė¸ (http(s):// íŦ함)", "server_public_users": "ëĒ¨ë“  ė‚ŦėšŠėž", - "server_public_users_description": "ęŗĩ뜠 ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”가할 ę˛Ŋ뚰 ëĒ¨ë“  ė‚ŦėšŠėž(ė´ëĻ„, ė´ëŠ”ėŧ)가 ë‚˜ė—´ëŠë‹ˆë‹¤. ëš„í™œė„ąí™” 할 ę˛Ŋ뚰, 관ëĻŦėžë§Œė´ ė‚ŦėšŠėž ëĒŠëĄė„ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다.", + "server_public_users_description": "ęŗĩ뜠 ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”가할 때 ëĒ¨ë“  ė‚ŦėšŠėžė˜ ė´ëĻ„ęŗŧ ė´ëŠ”ėŧė„ ëĒŠëĄė— í‘œė‹œí•Šë‹ˆë‹¤. ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 관ëĻŦėžė¸ ę˛Ŋėš°ė—ë§Œ ė‚ŦėšŠėž ëĒŠëĄė´ í‘œė‹œëŠë‹ˆë‹¤.", "server_settings": "ė„œë˛„ 네렕", "server_settings_description": "ė„œë˛„ 네렕 관ëĻŦ", "server_welcome_message": "í™˜ė˜ ëŠ”ė‹œė§€", @@ -251,7 +249,7 @@ "storage_template_hash_verification_enabled_description": "í•´ė‹œ 검ėĻė„ í™œė„ąí™”í•Šë‹ˆë‹¤. ė´ ė„¤ė •ė˜ 결ęŗŧëĨŧ í™•ė‹¤ížˆ ė´í•´í•˜ė§€ ė•ŠëŠ” 한 ëš„í™œė„ąí™”í•˜ė§€ ë§ˆė„¸ėš”.", "storage_template_migration": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋ ë§ˆė´ęˇ¸ë ˆė´ė…˜", "storage_template_migration_description": "ė´ė „ė— ė—…ëĄœë“œëœ 항ëĒŠė— 현ėžŦ {template} ė ėšŠ", - "storage_template_migration_info": "ė €ėžĨė†Œ 템플ëĻŋė€ ëĒ¨ë“  확ėžĨėžëĨŧ ė†ŒëŦ¸ėžëĄœ ëŗ€í™˜í•Šë‹ˆë‹¤. 템플ëĻŋ ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ 냈 ėžė‚°ė—ë§Œ ė ėšŠëŠë‹ˆë‹¤. ė´ė „ė— ė—…ëĄœë“œí•œ ėžė‚°ė— 템플ëĻŋė„ ė ėšŠí•˜ë ¤ëŠ´ {job}ëĨŧ ė‹¤í–‰í•˜ė„¸ėš”.", + "storage_template_migration_info": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋė€ ëĒ¨ë“  확ėžĨėžëĨŧ ė†ŒëŦ¸ėžëĄœ ëŗ€í™˜í•˜ëŠ°, ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ ėƒˆëĄœ ė—…ëĄœë“œí•œ 항ëĒŠė—ë§Œ ė ėšŠëŠë‹ˆë‹¤. ę¸°ėĄ´ė— ė—…ëĄœë“œëœ 항ëĒŠė— ė ėšŠí•˜ë ¤ëŠ´ {job}ė„ ė‹¤í–‰í•˜ė„¸ėš”.", "storage_template_migration_job": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋ ë§ˆė´ęˇ¸ë ˆė´ė…˜ ėž‘ė—…", "storage_template_more_details": "ė´ 기ëŠĨ뗐 대한 ėžė„¸í•œ ë‚´ėšŠė€ ėŠ¤í† ëĻŦė§€ 템플ëĻŋ 및 네ëĒ…ė„ ė°¸ėĄ°í•˜ė„¸ėš”.", "storage_template_onboarding_description": "ė´ 기ëŠĨė„ í™œė„ąí™”í•˜ëŠ´ ė‚ŦėšŠėž ė •ė˜ 템플ëĻŋė„ ė‚ŦėšŠí•˜ė—Ŧ 파ėŧė„ ėžë™ėœŧ로 ė •ëĻŦ할 눘 ėžˆėŠĩ니다. ė•ˆė •ė„ą ëŦ¸ė œëĄœ ė¸í•´ 해당 기ëŠĨė€ ę¸°ëŗ¸ė ėœŧ로 ëš„í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ėžė„¸í•œ ë‚´ėšŠė€ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", @@ -261,14 +259,14 @@ "storage_template_user_label": "ė‚ŦėšŠėžė˜ ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”: {label}", "system_settings": "ė‹œėŠ¤í…œ 네렕", "tag_cleanup_job": "태그 ė •ëĻŦ", - "template_email_available_tags": "템플ëĻŋė—ė„œ ë‹¤ėŒ ëŗ€ėˆ˜ëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다: {tags}", + "template_email_available_tags": "템플ëĻŋ뗐 ë‹¤ėŒ ëŗ€ėˆ˜ëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다: {tags}", "template_email_if_empty": "ëš„ė–´ ėžˆëŠ” ę˛Ŋ뚰 ę¸°ëŗ¸ 템플ëĻŋė´ ė‚ŦėšŠëŠë‹ˆë‹¤.", - "template_email_invite_album": "ė•¨ë˛” 템플ëĻŋ ė´ˆëŒ€", + "template_email_invite_album": "ė•¨ë˛” ė´ˆëŒ€ėžĨ 템플ëĻŋ", "template_email_preview": "미ëĻŦëŗ´ę¸°", "template_email_settings": "ė´ëŠ”ėŧ 템플ëĻŋ", "template_email_settings_description": "ė‚ŦėšŠėž ė •ė˜ ė´ëŠ”ėŧ 템플ëĻŋ 관ëĻŦ", - "template_email_update_album": "ė•¨ë˛” 템플ëĻŋ ė—…ë°ė´íŠ¸", - "template_email_welcome": "ė´ëŠ”ėŧ 템플ëĻŋ뗐 ė˜¤ė‹ ę˛ƒė„ í™˜ė˜í•Šë‹ˆë‹¤", + "template_email_update_album": "ė•¨ë˛” ė—…ë°ė´íŠ¸ ė•ˆë‚´ 템플ëĻŋ", + "template_email_welcome": "í™˜ė˜ 메ėŧ 템플ëĻŋ", "template_settings": "ė•ŒëĻŧ 템플ëĻŋ", "template_settings_description": "ė•ŒëĻŧė„ ėœ„í•œ ė‚ŦėšŠėž 맀렕 템플ëĻŋė„ 관ëĻŦ합니다.", "theme_custom_css_settings": "ė‚ŦėšŠėž ė •ė˜ CSS", @@ -295,13 +293,13 @@ "transcoding_audio_codec_description": "Opus는 가ėžĨ ėĸ‹ė€ í’ˆė§ˆė˜ ė˜ĩė…˜ė´ė§€ë§Œ 기기 및 ė†Œí”„íŠ¸ė›¨ė–´ę°€ ė˜¤ëž˜ëœ ę˛Ŋ뚰 í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", "transcoding_bitrate_description": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ 봈ęŗŧ하는 ë™ė˜ėƒ 또는 í—ˆėšŠë˜ė§€ ė•ŠëŠ” í˜•ė‹ė˜ ë™ė˜ėƒ", "transcoding_codecs_learn_more": "ė—Ŧę¸°ė—ė„œ ė‚ŦėšŠë˜ëŠ” ėšŠė–´ė— 대한 ėžė„¸í•œ ë‚´ėšŠė€ FFmpeg ëŦ¸ė„œė˜ H.264 ėŊ”덱, HEVC ėŊ”덱 및 VP9 ėŊ”덱 항ëĒŠė„ ė°¸ėĄ°í•˜ė„¸ėš”.", - "transcoding_constant_quality_mode": "ęŗ ė • í’ˆė§ˆ ëĒ¨ë“œ", + "transcoding_constant_quality_mode": "Constant quality mode", "transcoding_constant_quality_mode_description": "ICQ는 CQPëŗ´ë‹¤ ë‚˜ė€ ė„ąëŠĨė„ ëŗ´ė´ë‚˜ ėŧëļ€ ę¸°ę¸°ė˜ í•˜ë“œė›¨ė–´ ę°€ė†ė—ė„œ ė§€ė›ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. ė´ ė˜ĩė…˜ė„ ė„¤ė •í•˜ëŠ´ í’ˆė§ˆ 기반 ė¸ėŊ”딊 ė‹œ ė§€ė •ëœ ëĒ¨ë“œëĨŧ ėš°ė„ ė ėœŧ로 ė‚ŦėšŠí•Šë‹ˆë‹¤. NVENCė—ė„œëŠ” ICQëĨŧ ė§€ė›í•˜ė§€ ė•Šė•„ ė´ ė„¤ė •ė´ ė ėšŠë˜ė§€ ė•ŠėŠĩ니다.", - "transcoding_constant_rate_factor": "냁눘 ëš„ėœ¨ ęŗ„ėˆ˜(-CRF)", + "transcoding_constant_rate_factor": "Constant rate factor (-crf)", "transcoding_constant_rate_factor_description": "ėŧë°˜ė ėœŧ로 H.264는 23, HEVC는 28, VP9는 31, AV1는 35ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. ę°’ė´ 낮ėœŧ늴 í’ˆė§ˆė´ í–Ĩėƒë˜ė§€ë§Œ 파ėŧ íŦ기가 ėĻę°€í•Šë‹ˆë‹¤.", "transcoding_disabled_description": "ë™ė˜ėƒė„ íŠ¸ëžœėŠ¤ėŊ”ë”Ší•˜ė§€ ė•ŠėŒ. ėŧëļ€ ę¸°ę¸°ė—ė„œ ėžŦėƒė´ ëļˆę°€ëŠĨ할 눘 ėžˆėŠĩ니다.", "transcoding_encoding_options": "ė¸ėŊ”딊 ė˜ĩė…˜", - "transcoding_encoding_options_description": "ė¸ėŊ”딊된 ë™ė˜ėƒė˜ ėŊ”덱, í•´ėƒë„, í’ˆė§ˆ 및 기타 ė˜ĩė…˜ė„ ė„¤ė •í•Šë‹ˆë‹¤", + "transcoding_encoding_options_description": "ė¸ėŊ”딊된 ë™ė˜ėƒė˜ ėŊ”덱, í•´ėƒë„, í’ˆė§ˆ 및 기타 ė˜ĩė…˜ 네렕", "transcoding_hardware_acceleration": "í•˜ë“œė›¨ė–´ ę°€ė†", "transcoding_hardware_acceleration_description": "ė‹¤í—˜ė ė¸ 기ëŠĨėž…ë‹ˆë‹¤. ė†ë„ę°€ í–Ĩėƒë˜ė§€ë§Œ 동ėŧ ëš„íŠ¸ë ˆė´íŠ¸ė—ė„œ í’ˆė§ˆė´ ėƒëŒ€ė ėœŧ로 ë‚Žė„ 눘 ėžˆėŠĩ니다.", "transcoding_hardware_decoding": "í•˜ë“œė›¨ė–´ 디ėŊ”딊", @@ -315,7 +313,7 @@ "transcoding_max_keyframe_interval_description": "í‚¤í”„ë ˆėž„ ė‚Ŧė´ ėĩœëŒ€ í”„ë ˆėž„ ęą°ëĻŦëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 낮ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ ė €í•˜ë˜ė§€ë§Œ ę˛€ėƒ‰ ė‹œę°„ė´ ę°œė„ ë˜ęŗ  ëš ëĨ¸ ė›€ė§ėž„ė´ ėžˆëŠ” ėžĨëŠ´ė—ė„œ í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", "transcoding_optimal_description": "ëĒŠí‘œ í•´ėƒë„ëŗ´ë‹¤ ë†’ė€ ë™ė˜ėƒ 또는 í—ˆėšŠë˜ė§€ ė•ŠëŠ” í˜•ė‹ė˜ ë™ė˜ėƒ", "transcoding_policy": "íŠ¸ëžœėŠ¤ėŊ”드 ė •ėą…", - "transcoding_policy_description": "ë™ė˜ėƒ íŠ¸ëžœėŠ¤ėŊ”딊 ė‹œę¸° ė„¤ė •í•˜ę¸°", + "transcoding_policy_description": "íŠ¸ëžœėŠ¤ėŊ”딊 ëŒ€ėƒ ë™ė˜ėƒ 네렕", "transcoding_preferred_hardware_device": "ė„ í˜¸í•˜ëŠ” í•˜ë“œė›¨ė–´ 기기", "transcoding_preferred_hardware_device_description": "í•˜ë“œė›¨ė–´ íŠ¸ëžœėŠ¤ėŊ”ë”Šė— ė‚ŦėšŠí•  dri 노드ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. (VAAPI뙀 QSV만 해당)", "transcoding_preset_preset": "프ëĻŦė…‹ (-preset)", @@ -324,10 +322,10 @@ "transcoding_reference_frames_description": "íŠšė • í”„ë ˆėž„ė„ ė••ėļ•í•  때 ė°¸ėĄ°í•˜ëŠ” í”„ë ˆėž„ 눘ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 높ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ë‚˜ ė¸ėŊ”딊 ė†ë„ę°€ ė €í•˜ëŠë‹ˆë‹¤. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", "transcoding_required_description": "í—ˆėšŠëœ í˜•ė‹ė´ ė•„ë‹Œ ë™ė˜ėƒë§Œ", "transcoding_settings": "ë™ė˜ėƒ íŠ¸ëžœėŠ¤ėŊ”딊 네렕", - "transcoding_settings_description": "íŠ¸ëžœėŠ¤ėŊ”딊할 ë™ė˜ėƒęŗŧ 래ëĻŦ 방법 관ëĻŦ하기", + "transcoding_settings_description": "íŠ¸ëžœėŠ¤ėŊ”딊할 ë™ė˜ėƒ 및 래ëĻŦ 방법 관ëĻŦ", "transcoding_target_resolution": "ëĒŠí‘œ í•´ėƒë„", "transcoding_target_resolution_description": "ë†’ė€ í•´ėƒë„ëĨŧ ė„ íƒí•œ ę˛Ŋ뚰 넏ëļ€ ëŦ˜ė‚Ŧė˜ ė†ė‹¤ė„ ėĩœė†Œí™”í•  눘 ėžˆė§€ë§Œ, ė¸ėŊ”딊 ė‹œę°„ęŗŧ 파ėŧ íŦ기가 ėĻę°€í•˜ė—Ŧ ė•ąė˜ ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", - "transcoding_temporal_aq": "ėŧė‹œė  AQ", + "transcoding_temporal_aq": "Temporal AQ", "transcoding_temporal_aq_description": "넏ëļ€ ëŦ˜ė‚Ŧ가 ë§Žęŗ  ė›€ė§ėž„ė´ ė ė€ ėžĨëŠ´ė˜ í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. ė˜¤ëž˜ëœ 揰揰뙀 í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. (NVENC만 해당)", "transcoding_threads": "ėŠ¤ë ˆë“œ", "transcoding_threads_description": "ę°’ė´ 높ėœŧ늴 ė¸ėŊ”딊 ė†ë„ę°€ í–Ĩėƒë˜ė§€ë§Œ ëĻŦė†ŒėŠ¤ ė‚ŦėšŠëŸ‰ė´ ėĻę°€í•Šë‹ˆë‹¤. ę°’ė€ CPU ėŊ”ė–´ ėˆ˜ëŗ´ë‹¤ ėž‘ė•„ė•ŧ 하며, ė„¤ė •í•˜ė§€ ė•Šėœŧ려늴 0ė„ ėž…ë Ĩ합니다.", @@ -340,18 +338,19 @@ "transcoding_video_codec": "ë™ė˜ėƒ ėŊ”덱", "transcoding_video_codec_description": "VP9는 íš¨ėœ¨ė ė´ęŗ  ė›š í˜¸í™˜ė„ąė´ ë†’ė§€ë§Œ íŠ¸ëžœėŠ¤ėŊ”ë”Šė— ë‹¤ė†Œ 긴 ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤. HEVC는 ė„ąëŠĨė€ ëš„ėŠˇí•˜ë‚˜ ė›š í˜¸í™˜ė„ąė´ 낮ėŠĩ니다. H.264는 í˜¸í™˜ė„ąė´ 가ėžĨ ë†’ė§€ë§Œ ė¸ėŊ”딊된 파ėŧ íŦ기가 íŦęŗ , AV1ė€ 가ėžĨ íš¨ėœ¨ė ė´ë‚˜ ė˜¤ëž˜ëœ ę¸°ę¸°ė™€ė˜ í˜¸í™˜ė„ąė´ 낮ėŠĩ니다.", "trash_enabled_description": "íœ´ė§€í†ĩ í™œė„ąí™”", - "trash_number_of_days": "ė‚­ė œ ëŗ´ëĨ˜ 기간", - "trash_number_of_days_description": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė˜ ė‚­ė œ ëŗ´ëĨ˜ 기간", + "trash_number_of_days": "ė‚­ė œ 뜠똈 기간", + "trash_number_of_days_description": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė˜ ė‚­ė œ 뜠똈 기간", "trash_settings": "íœ´ė§€í†ĩ 네렕", "trash_settings_description": "íœ´ė§€í†ĩ 네렕 관ëĻŦ", "untracked_files": "ėļ”ė ë˜ė§€ ė•ŠëŠ” 파ėŧ", "untracked_files_description": "ė• í”ŒëĻŦėŧ€ė´ė…˜ė—ė„œ ėļ”ė ë˜ė§€ ė•ŠëŠ” 파ėŧ ëĒŠëĄėž…ë‹ˆë‹¤. ė´ë™ ė‹¤íŒ¨, ė—…ëĄœë“œ ė¤‘ë‹¨ 또는 버그로 ė¸í•´ ë°œėƒí•  눘 ėžˆėŠĩ니다.", "user_cleanup_job": "ė‚ŦėšŠėž ė •ëĻŦ", "user_delete_delay": "{user}ë‹˜ė´ ė—…ëĄœë“œí•œ 항ëĒŠė´ {delay, plural, one {#ėŧ} other {#ėŧ}} 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "user_delete_delay_settings": "ė‚­ė œ ëŗ´ëĨ˜ 기간", - "user_delete_delay_settings_description": "ė‚ŦėšŠėžëĨŧ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ę¸° ė „ ëŗ´ëĨ˜ ę¸°ę°„ė„ ė„¤ė •í•Šë‹ˆë‹¤. ė‚ŦėšŠėž ė‚­ė œëŠ” 매ėŧ ë°¤ ėžė •, ëŗ´ëĨ˜ ę¸°ę°„ė´ ė§€ë‚œ ė‚ŦėšŠėžëĨŧ í™•ė¸í•œ 후 ė§„í–‰ëŠë‹ˆë‹¤. ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ ë‹¤ėŒ ėž‘ė—…ëļ€í„° ė ėšŠëŠë‹ˆë‹¤.", + "user_delete_delay_settings": "ė‚­ė œ 뜠똈 기간", + "user_delete_delay_settings_description": "ė‚ŦėšŠėž ęŗ„ė •ęŗŧ 항ëĒŠė´ ė™„ė „ížˆ ė‚­ė œë˜ę¸°ęšŒė§€ė˜ 뜠똈 기간(ėŧ)ė„ ė„¤ė •í•Šë‹ˆë‹¤. ė‚ŦėšŠėž ė‚­ė œ ėž‘ė—…ė€ 매ėŧ ėžė •ė— ė‹¤í–‰ë˜ė–´ ė‚­ė œ ëŒ€ėƒ ė—Ŧëļ€ëĨŧ í™•ė¸í•Šë‹ˆë‹¤. ė´ ė„¤ė •ė˜ ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ ë‹¤ėŒ ėž‘ė—… ė‹¤í–‰ ė‹œ ë°˜ė˜ëŠë‹ˆë‹¤.", "user_delete_immediately": "{user}ë‹˜ė´ ė—…ëĄœë“œí•œ 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "user_delete_immediately_checkbox": "ëŗ´ëĨ˜ 기간 ė—†ė´ ėĻ‰ė‹œ ė‚­ė œ", + "user_delete_immediately_checkbox": "뜠똈 기간 ė—†ė´ ėĻ‰ė‹œ ė‚­ė œ", + "user_details": "ė‚ŦėšŠėž ėƒė„¸", "user_management": "ė‚ŦėšŠėž 관ëĻŦ", "user_password_has_been_reset": "ė‚ŦėšŠėžė˜ 비밀번호가 ė´ˆę¸°í™”ë˜ė—ˆėŠĩ니다:", "user_password_reset_description": "ė´ 비밀번호ëĨŧ 해당 ė‚ŦėšŠėžė—ę˛Œ ė•Œë ¤ėŖŧė„¸ėš”. ėž„ė‹œ 비밀번호로 ëĄœęˇ¸ė¸í•œ 뒤 비밀번호ëĨŧ ë°˜ë“œė‹œ ëŗ€ę˛Ŋ해ė•ŧ 합니다.", @@ -371,13 +370,17 @@ "admin_password": "관ëĻŦėž 비밀번호", "administration": "관ëĻŦ", "advanced": "溠揉", - "advanced_settings_log_level_title": "로그 레벨: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "ė´ ė˜ĩė…˜ė„ ė‚ŦėšŠí•˜ëŠ´ 동기화 뤑 ë¯¸ë””ė–´ëĨŧ ëŒ€ė˛´ 揰뤀ėœŧ로 필터링할 눘 ėžˆėŠĩ니다. ė•ąė´ ëĒ¨ë“  ė•¨ë˛”ė„ ė œëŒ€ëĄœ ę°ė§€í•˜ė§€ ëĒģ할 때만 ė‚ŦėšŠí•˜ė„¸ėš”.", + "advanced_settings_enable_alternate_media_filter_title": "ëŒ€ė˛´ 기기 ė•¨ë˛” 동기화 필터 ė‚ŦėšŠ (ė‹¤í—˜ė )", + "advanced_settings_log_level_title": "로그 레벨: {level}", "advanced_settings_prefer_remote_subtitle": "ėŧëļ€ ę¸°ę¸°ė˜ ę˛Ŋ뚰 기기 ë‚´ė˜ ė„Ŧ네ėŧė„ 로드하는 ė†ë„ę°€ ë§¤ėš° 느ëĻŊ니다. ė„œë˛„ ė´ë¯¸ė§€ëĨŧ ëŒ€ė‹  로드하려면 ė´ ė„¤ė •ė„ í™œė„ąí™”í•˜ė„¸ėš”.", "advanced_settings_prefer_remote_title": "ė„œë˛„ ė´ë¯¸ė§€ ė„ í˜¸", - "advanced_settings_proxy_headers_subtitle": "각 ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė„ ëŗ´ë‚ŧ 때 Immich가 ė‚ŦėšŠí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", + "advanced_settings_proxy_headers_subtitle": "ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė„ ëŗ´ë‚ŧ 때 Immich가 ė‚ŦėšŠí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", "advanced_settings_proxy_headers_title": "í”„ëĄė‹œ 헤더", "advanced_settings_self_signed_ssl_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ė— 대한 SSL ė¸ėĻė„œ í™•ė¸ė„ 건너뜁니다. ėžė˛´ ė„œëĒ…ëœ ė¸ėĻė„œëĨŧ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", "advanced_settings_self_signed_ssl_title": "ėžė˛´ ė„œëĒ…ëœ SSL ė¸ėĻė„œ í—ˆėšŠ", + "advanced_settings_sync_remote_deletions_subtitle": "ė›šė—ė„œ ė‚­ė œí•˜ęą°ë‚˜ ëŗĩė›í•œ 항ëĒŠė„ ė´ ę¸°ę¸°ė—ė„œë„ ėžë™ėœŧ로 래ëĻŦ하도록 네렕", + "advanced_settings_sync_remote_deletions_title": "ė›ę˛Š ė‚­ė œ 동기화 (ė‹¤í—˜ė )", "advanced_settings_tile_subtitle": "溠揉 ė‚ŦėšŠėž 네렕", "advanced_settings_troubleshooting_subtitle": "ëŦ¸ė œ í•´ę˛°ė„ ėœ„í•œ ėļ”ę°€ 기ëŠĨ ė‚ŦėšŠ", "advanced_settings_troubleshooting_title": "ëŦ¸ė œ 해결", @@ -400,18 +403,18 @@ "album_remove_user_confirmation": "{user}ë‹˜ė„ ė•¨ë˛”ė—ė„œ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", "album_share_no_users": "ė´ë¯¸ ëĒ¨ë“  ė‚ŦėšŠėžė™€ ė•¨ë˛”ė„ ęŗĩ뜠 ė¤‘ė´ęą°ë‚˜ 다ëĨ¸ ė‚ŦėšŠėžę°€ ė—†ëŠ” 것 같ėŠĩ니다.", "album_thumbnail_card_item": "항ëĒŠ 1氜", - "album_thumbnail_card_items": "항ëĒŠ {}氜", + "album_thumbnail_card_items": "항ëĒŠ {count}氜", "album_thumbnail_card_shared": " ¡ ęŗĩėœ ë¨", - "album_thumbnail_shared_by": "{}ë‹˜ė´ ęŗĩėœ í•¨", + "album_thumbnail_shared_by": "{user}ë‹˜ė´ ęŗĩėœ í•¨", "album_updated": "항ëĒŠ ėļ”ę°€ ė•ŒëĻŧ", "album_updated_setting_description": "ęŗĩ뜠 ė•¨ë˛”ė— 항ëĒŠė´ ėļ”ę°€ëœ ę˛Ŋ뚰 ė´ëŠ”ėŧ ė•ŒëĻŧ 받기", "album_user_left": "{album} ė•¨ë˛”ė—ė„œ ë‚˜ė˜´", "album_user_removed": "{user}ë‹˜ė„ ė•¨ë˛”ė—ė„œ ė œęą°í•¨", "album_viewer_appbar_delete_confirm": "ė´ ė•¨ë˛”ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "album_viewer_appbar_share_err_delete": "ė•¨ë˛” ė‚­ė œė— ė‹¤íŒ¨í–ˆėŠĩ니다.", - "album_viewer_appbar_share_err_leave": "ė•¨ë˛” ë‚˜ę°€ę¸°ė— ė‹¤íŒ¨í–ˆėŠĩ니다.", - "album_viewer_appbar_share_err_remove": "ė•¨ë˛”ė—ė„œ 항ëĒŠė„ ė œęą°í•˜ė§€ ëĒģ했ėŠĩ니다.", - "album_viewer_appbar_share_err_title": "ė•¨ë˛”ëĒ… ëŗ€ę˛Ŋ뗐 ė‹¤íŒ¨í–ˆėŠĩ니다.", + "album_viewer_appbar_share_err_delete": "ė•¨ë˛” ė‚­ė œ ė‹¤íŒ¨", + "album_viewer_appbar_share_err_leave": "ė•¨ë˛”ė—ė„œ ë‚˜ę°€ė§€ ëĒģ했ėŠĩ니다.", + "album_viewer_appbar_share_err_remove": "ė•¨ë˛”ė—ė„œ 항ëĒŠė„ ė œęą°í•˜ëŠ” 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", + "album_viewer_appbar_share_err_title": "ė•¨ë˛” 렜ëĒŠė„ ëŗ€ę˛Ŋí•˜ė§€ ëĒģ했ėŠĩ니다.", "album_viewer_appbar_share_leave": "ė•¨ë˛” 나가기", "album_viewer_appbar_share_to": "ęŗĩ뜠 ëŒ€ėƒ", "album_viewer_page_share_add_users": "ė‚ŦėšŠėž ėļ”ę°€", @@ -426,7 +429,7 @@ "allow_edits": "íŽ¸ė§‘ėžëĄœ 네렕", "allow_public_user_to_download": "ëĒ¨ë“  ė‚ŦėšŠėžė˜ ë‹¤ėš´ëĄœë“œ í—ˆėšŠ", "allow_public_user_to_upload": "ëĒ¨ë“  ė‚ŦėšŠėžė˜ ė—…ëĄœë“œ í—ˆėšŠ", - "alt_text_qr_code": "QRėŊ”드 ė´ë¯¸ė§€", + "alt_text_qr_code": "QR ėŊ”드 ė´ë¯¸ė§€", "anti_clockwise": "ë°˜ė‹œęŗ„ ë°Ší–Ĩ", "api_key": "API 키", "api_key_description": "ė´ ę°’ė€ 한 번만 í‘œė‹œëŠë‹ˆë‹¤. ė°Ŋė„ ë‹Ģ기 ė „ ë°˜ë“œė‹œ ëŗĩė‚Ŧ해ėŖŧė„¸ėš”.", @@ -438,17 +441,17 @@ "app_settings": "ė•ą 네렕", "appears_in": "ë‹¤ėŒ ė•¨ë˛”ė— íŦ함됨", "archive": "ëŗ´ę´€í•¨", - "archive_or_unarchive_photo": "ëŗ´ę´€í•¨ėœŧ로 ė´ë™ 또는 ė œęą°", + "archive_or_unarchive_photo": "ëŗ´ę´€ 래ëĻŦ 또는 í•´ė œ", "archive_page_no_archived_assets": "ëŗ´ę´€ëœ 항ëĒŠ ė—†ėŒ", - "archive_page_title": "ëŗ´ę´€í•¨ ({})", + "archive_page_title": "ëŗ´ę´€í•¨ ({count})", "archive_size": "ė••ėļ• íŒŒėŧ íŦ기", "archive_size_description": "ë‹¤ėš´ëĄœë“œí•  ė••ėļ• íŒŒėŧė˜ íŦ기 ęĩŦė„ą (GiB ë‹¨ėœ„)", "archived": "ëŗ´ę´€í•¨", - "archived_count": "ëŗ´ę´€í•¨ėœŧ로 항ëĒŠ {count, plural, other {#氜}} ė´ë™ë¨", + "archived_count": "ëŗ´ę´€í•¨ėœŧ로 {count, plural, other {#氜}} 항ëĒŠ ė´ë™ë¨", "are_these_the_same_person": "동ėŧ한 ė¸ëŦŧė¸ę°€ėš”?", "are_you_sure_to_do_this": "ęŗ„ė† ė§„í–‰í•˜ė‹œę˛ ėŠĩ니까?", - "asset_action_delete_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė€ ė‚­ė œí•  눘 ė—†ėŠĩ니다. 건너뜁니다.", - "asset_action_share_err_offline": "누ëŊ된 항ëĒŠė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다. 건너뜁니다.", + "asset_action_delete_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė€ ė‚­ė œí•  눘 ė—†ėœŧë¯€ëĄœ 건너뜁니다.", + "asset_action_share_err_offline": "ė˜¤í”„ëŧė¸ 항ëĒŠė„ 氀렏ė˜Ŧ 눘 ė—†ėœŧë¯€ëĄœ 건너뜁니다.", "asset_added_to_album": "ė•¨ë˛”ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", "asset_adding_to_album": "ė•¨ë˛”ė— ėļ”ę°€ 뤑â€Ļ", "asset_description_updated": "항ëĒŠė˜ 네ëĒ…ė´ ė—…ë°ė´íŠ¸ë˜ė—ˆėŠĩ니다.", @@ -470,47 +473,47 @@ "asset_skipped_in_trash": "íœ´ė§€í†ĩė˜ 항ëĒŠ", "asset_uploaded": "ė—…ëĄœë“œ ė™„ëŖŒ", "asset_uploading": "ė—…ëĄœë“œ 뤑â€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", + "asset_viewer_settings_subtitle": "ę°¤ëŸŦëĻŦ ëˇ°ė–´ 네렕 관ëĻŦ", "asset_viewer_settings_title": "ëŗ´ę¸° ė˜ĩė…˜", "assets": "항ëĒŠ", - "assets_added_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 ėļ”ę°€ë˜ė—ˆėŠĩ니다.", + "assets_added_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠ ėļ”가됨", "assets_added_to_album_count": "ė•¨ë˛”ė— 항ëĒŠ {count, plural, one {#氜} other {#氜}} ėļ”가됨", "assets_added_to_name_count": "{hasName, select, true {{name}} other {냈 ė•¨ë˛”}}뗐 항ëĒŠ {count, plural, one {#氜} other {#氜}} ėļ”가됨", "assets_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠ", - "assets_deleted_permanently": "{}氜 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", - "assets_deleted_permanently_from_server": "Immichė—ė„œ 항ëĒŠ {}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_deleted_permanently": "{count}氜 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_deleted_permanently_from_server": "ė„œë˛„ė—ė„œ 항ëĒŠ {count}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", "assets_moved_to_trash_count": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", "assets_permanently_deleted_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", "assets_removed_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė œęą°í–ˆėŠĩ니다.", - "assets_removed_permanently_from_device": "ę¸°ę¸°ė—ė„œ 항ëĒŠ {}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_removed_permanently_from_device": "ę¸°ę¸°ė—ė„œ 항ëĒŠ {count}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", "assets_restore_confirmation": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė„ ëĒ¨ë‘ ëŗĩė›í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다! 누ëŊ된 항ëĒŠė˜ ę˛Ŋ뚰 ëŗĩė›ë˜ė§€ ė•ŠėŠĩ니다.", "assets_restored_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ëŗĩė›í–ˆėŠĩ니다.", - "assets_restored_successfully": "항ëĒŠ {}氜ëĨŧ ëŗĩė›í–ˆėŠĩ니다.", - "assets_trashed": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {}개가 ė´ë™ë˜ė—ˆėŠĩ니다.", + "assets_restored_successfully": "항ëĒŠ {count}氜ëĨŧ ëŗĩė›í–ˆėŠĩ니다.", + "assets_trashed": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count}氜 ė´ë™ë¨", "assets_trashed_count": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", - "assets_trashed_from_server": "íœ´ė§€í†ĩėœŧ로 Immich 항ëĒŠ {}개가 ė´ë™ë˜ė—ˆėŠĩ니다.", + "assets_trashed_from_server": "ė„œë˛„ė—ė„œ 항ëĒŠ {count}개가 íœ´ė§€í†ĩėœŧ로 ė´ë™ë¨", "assets_were_part_of_album_count": "ė•¨ë˛”ė— ė´ë¯¸ ėĄ´ėžŦ하는 {count, plural, one {항ëĒŠ} other {항ëĒŠ}}ėž…ë‹ˆë‹¤.", "authorized_devices": "ė¸ėĻëœ 기기", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "ė§€ė •ëœ Wi-Fi가 ė‚ŦėšŠ 가ëŠĨ한 ę˛Ŋ뚰 내ëļ€ë§ė„ í†ĩ해 ė—°ę˛°í•˜ęŗ , ęˇ¸ë ‡ė§€ ė•Šėœŧ늴 다ëĨ¸ 뗰枰 ë°Šė‹ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤.", + "automatic_endpoint_switching_title": "ėžë™ URL ė „í™˜", "back": "뒤로", "back_close_deselect": "뒤로, ë‹Ģ기, ė„ íƒ ėˇ¨ė†Œ", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "ę¸°ę¸°ė˜ ė•¨ë˛” ({})", - "backup_album_selection_page_albums_tap": "한 번 눌ëŸŦ ė„ íƒ, 두 번 눌ëŸŦ ė œė™¸í•˜ė„¸ėš”.", + "background_location_permission": "밹꡸ëŧėš´ë“œ ėœ„ėš˜ ęļŒí•œ", + "background_location_permission_content": "밹꡸ëŧėš´ë“œė—ė„œ ë„¤íŠ¸ė›ŒíŦëĨŧ ė „í™˜í•˜ë ¤ëŠ´, Immich가 Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•  눘 ėžˆë„ëĄ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė„ í•­ėƒ í—ˆėšŠí•´ė•ŧ 합니다.", + "backup_album_selection_page_albums_device": "ę¸°ę¸°ė˜ ė•¨ë˛” ({count})", + "backup_album_selection_page_albums_tap": "한 번 탭하면 íŦí•¨ë˜ęŗ , 두 번 탭하면 ė œė™¸ëŠë‹ˆë‹¤.", "backup_album_selection_page_assets_scatter": "각 항ëĒŠė€ ė—ŦëŸŦ ė•¨ë˛”ė— íŦ함될 눘 ėžˆėœŧ늰, ë°ąė—… ė§„í–‰ ė¤‘ė—ë„ ëŒ€ėƒ ė•¨ë˛”ė„ íŦ함하거나 ė œė™¸í•  눘 ėžˆėŠĩ니다.", "backup_album_selection_page_select_albums": "ė•¨ë˛” ė„ íƒ", "backup_album_selection_page_selection_info": "ė„ íƒí•œ ė•¨ë˛”", "backup_album_selection_page_total_assets": "렄랴 항ëĒŠ", "backup_all": "ëĒ¨ë‘", - "backup_background_service_backup_failed_message": "항ëĒŠė„ ë°ąė—…í•˜ė§€ ëĒģ했ėŠĩ니다. ë‹¤ė‹œ ė‹œë„í•˜ëŠ” 뤑...", - "backup_background_service_connection_failed_message": "ė„œë˛„ė— ė—°ę˛°í•˜ė§€ ëĒģ했ėŠĩ니다. ë‹¤ė‹œ ė‹œë„í•˜ëŠ” 뤑...", - "backup_background_service_current_upload_notification": "{} ė—…ëĄœë“œ 뤑", - "backup_background_service_default_notification": "ë°ąė—…í•  항ëĒŠė„ í™•ė¸í•˜ëŠ” 뤑...", + "backup_background_service_backup_failed_message": "항ëĒŠė„ ë°ąė—…í•˜ė§€ ëĒģ했ėŠĩ니다. ë‹¤ė‹œ ė‹œë„í•˜ëŠ” 뤑â€Ļ", + "backup_background_service_connection_failed_message": "ė„œë˛„ė— ė—°ę˛°í•˜ė§€ ëĒģ했ėŠĩ니다. ë‹¤ė‹œ ė‹œë„í•˜ëŠ” 뤑â€Ļ", + "backup_background_service_current_upload_notification": "{filename} ė—…ëĄœë“œ 뤑", + "backup_background_service_default_notification": "ėƒˆëĄœėš´ 항ëĒŠė„ í™•ė¸í•˜ëŠ” 뤑â€Ļ", "backup_background_service_error_title": "ë°ąė—… 똤ëĨ˜", - "backup_background_service_in_progress_notification": "ė„ íƒí•œ 항ëĒŠė„ ë°ąė—…í•˜ëŠ” 뤑...", - "backup_background_service_upload_failure_notification": "{} ė—…ëĄœë“œ ė‹¤íŒ¨", + "backup_background_service_in_progress_notification": "항ëĒŠė„ ë°ąė—…í•˜ëŠ” 뤑â€Ļ", + "backup_background_service_upload_failure_notification": "{filename} ė—…ëĄœë“œ ė‹¤íŒ¨", "backup_controller_page_albums": "ë°ąė—…í•  ė•¨ë˛”", "backup_controller_page_background_app_refresh_disabled_content": "밹꡸ëŧėš´ë“œ ë°ąė—…ė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ 네렕 > ėŧ반 > 밹꡸ëŧėš´ë“œ ė•ą ėƒˆëĄœ ęŗ ėš¨ė—ė„œ 밹꡸ëŧėš´ë“œ ė•ą ėƒˆëĄœ ęŗ ėš¨ė„ í™œė„ąí™”í•˜ė„¸ėš”.", "backup_controller_page_background_app_refresh_disabled_title": "밹꡸ëŧėš´ë“œ ėƒˆëĄœ ęŗ ėš¨ ëš„í™œė„ąí™”ë¨", @@ -521,31 +524,30 @@ "backup_controller_page_background_battery_info_title": "배터ëĻŦ ėĩœė í™”", "backup_controller_page_background_charging": "ėļŠė „ ė¤‘ė—ë§Œ", "backup_controller_page_background_configure_error": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ ęĩŦė„ą ė‹¤íŒ¨", - "backup_controller_page_background_delay": "냈 ėŊ˜í…ė¸  ë°ąė—… 간격: {}", - "backup_controller_page_background_description": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ëĨŧ í™œė„ąí™”í•˜ė—Ŧ ė•ąė„ ė‹¤í–‰í•˜ė§€ ė•Šęŗ  냈 항ëĒŠė„ ėžë™ėœŧ로 ë°ąė—…í•˜ė„¸ėš”.", - "backup_controller_page_background_is_off": "밹꡸ëŧėš´ë“œ ë°ąė—…ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", - "backup_controller_page_background_is_on": "밹꡸ëŧėš´ë“œ ë°ąė—…ė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "backup_controller_page_background_delay": "냈 ë¯¸ë””ė–´ ë°ąė—… ë”œë ˆė´: {duration}", + "backup_controller_page_background_description": "ė•ąė„ ė—´ė§€ ė•Šė•„ë„ ėƒˆëĄœ ėļ”ę°€ëœ 항ëĒŠė´ ėžë™ėœŧ로 ë°ąė—…ë˜ë„ëĄ 하려면 밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ëĨŧ í™œė„ąí™”í•˜ė„¸ėš”.", + "backup_controller_page_background_is_off": "밹꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "backup_controller_page_background_is_on": "밹꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "backup_controller_page_background_turn_off": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ ëš„í™œė„ąí™”", "backup_controller_page_background_turn_on": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ í™œė„ąí™”", "backup_controller_page_background_wifi": "Wi-Fiė—ė„œë§Œ", "backup_controller_page_backup": "ë°ąė—…", - "backup_controller_page_backup_selected": "ė„ íƒë¨:", + "backup_controller_page_backup_selected": "ė„ íƒë¨: ", "backup_controller_page_backup_sub": "ë°ąė—…ëœ ė‚Ŧė§„ 및 ë™ė˜ėƒ", - "backup_controller_page_created": "ėƒė„ąėŧ: {}", + "backup_controller_page_created": "ėƒė„ąėŧ: {date}", "backup_controller_page_desc_backup": "íŦ꡸ëŧėš´ë“œ ë°ąė—…ė„ í™œė„ąí™”í•˜ė—Ŧ ė•ąė„ ė‹œėž‘í•  때 냈 항ëĒŠė„ ė„œë˛„ė— ėžë™ėœŧ로 ė—…ëĄœë“œí•˜ė„¸ėš”.", - "backup_controller_page_excluded": "ė œė™¸ë¨:", - "backup_controller_page_failed": "ė‹¤íŒ¨ ({})", - "backup_controller_page_filename": "파ėŧëĒ…: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "ė œė™¸ë¨: ", + "backup_controller_page_failed": "ė‹¤íŒ¨ ({count})", + "backup_controller_page_filename": "파ėŧëĒ…: {filename} [{size}]", "backup_controller_page_info": "ë°ąė—… ė •ëŗ´", - "backup_controller_page_none_selected": "ė„ íƒí•œ 항ëĒŠė´ ė—†ėŠĩ니다.", + "backup_controller_page_none_selected": "ė„ íƒëœ 항ëĒŠ ė—†ėŒ", "backup_controller_page_remainder": "ë‚¨ė€ 항ëĒŠ", "backup_controller_page_remainder_sub": "ë°ąė—… 대기 ė¤‘ė¸ ė‚Ŧė§„ 및 ë™ė˜ėƒ", "backup_controller_page_server_storage": "ė €ėžĨ ęŗĩ간", "backup_controller_page_start_backup": "ë°ąė—… ė‹œėž‘", - "backup_controller_page_status_off": "íŦ꡸ëŧėš´ë“œ ë°ąė—…ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", - "backup_controller_page_status_on": "íŦ꡸ëŧėš´ë“œ ë°ąė—…ė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", - "backup_controller_page_storage_format": "{} ė‚ŦėšŠ 뤑, 렄랴 {}", + "backup_controller_page_status_off": "íŦ꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "backup_controller_page_status_on": "íŦ꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "backup_controller_page_storage_format": "{total} 뤑 {used} ė‚ŦėšŠ", "backup_controller_page_to_backup": "ë°ąė—…í•  ė•¨ë˛” ëĒŠëĄ", "backup_controller_page_total_sub": "ė„ íƒí•œ ė•¨ë˛”ė˜ ęŗ ėœ í•œ ė‚Ŧė§„ 및 ë™ė˜ėƒ", "backup_controller_page_turn_off": "ëš„í™œė„ąí™”", @@ -558,33 +560,37 @@ "backup_manual_success": "ė„ąęŗĩ", "backup_manual_title": "ė—…ëĄœë“œ ėƒíƒœ", "backup_options_page_title": "ë°ąė—… ė˜ĩė…˜", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_setting_subtitle": "밹꡸ëŧėš´ë“œ 및 íŦ꡸ëŧėš´ë“œ ė—…ëĄœë“œ 네렕 관ëĻŦ", "backward": "뒤로", + "biometric_auth_enabled": "ėƒė˛´ ė¸ėĻė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "biometric_locked_out": "ėƒė˛´ ė¸ėĻė´ ėŧė‹œė ėœŧ로 ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "biometric_no_options": "ė‚ŦėšŠ 가ëŠĨ한 ėƒė˛´ ė¸ėĻ ė˜ĩė…˜ ė—†ėŒ", + "biometric_not_available": "ė´ 기기는 ėƒė˛´ ė¸ėĻė„ ė§€ė›í•˜ė§€ ė•ŠėŠĩ니다.", "birthdate_saved": "ėƒë…„ė›”ėŧė´ ė„ąęŗĩ렁ėœŧ로 ė €ėžĨë˜ė—ˆėŠĩ니다.", "birthdate_set_description": "ėƒë…„ė›”ėŧė€ ė‚Ŧė§„ ė´Ŧ똁 ë‹šė‹œ ė¸ëŦŧė˜ ë‚˜ė´ëĨŧ ęŗ„ė‚°í•˜ëŠ” 데 ė‚ŦėšŠëŠë‹ˆë‹¤.", "blurred_background": "흐ëϰ ë°°ę˛Ŋ", "bugs_and_feature_requests": "버그 ė œëŗ´ & 기ëŠĨ ėš”ė˛­", "build": "빌드", "build_image": "빌드 ė´ë¯¸ė§€", - "bulk_delete_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? íŦ기가 가ėžĨ 큰 항ëĒŠė„ ė œė™¸í•œ ë‚˜ë¨¸ė§€ 항ëĒŠë“¤ė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", - "bulk_keep_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ėœ ė§€í•˜ė‹œę˛ ėŠĩ니까? 파ėŧė„ ė‚­ė œí•˜ė§€ ė•Šęŗ  í™•ė¸ëœ 것ėœŧ로 판단합니다.", - "bulk_trash_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ íœ´ė§€í†ĩėœŧ로 ė´ë™í•˜ė‹œę˛ ėŠĩ니까? íŦ기가 가ėžĨ 큰 항ëĒŠė„ ė œė™¸í•œ ë‚˜ë¨¸ė§€ 항ëĒŠë“¤ė´ ëĒ¨ë‘ íœ´ė§€í†ĩėœŧ로 ė´ë™ëŠë‹ˆë‹¤.", + "bulk_delete_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėŧ괄 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? 각 ęˇ¸ëŖšė—ė„œ 가ėžĨ 큰 항ëĒŠë§Œ ë‚¨ę¸°ęŗ  ë‚˜ë¨¸ė§€ 뤑ëŗĩ 항ëĒŠė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", + "bulk_keep_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} 그대로 ėœ ė§€í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ ė–´ë–¤ 항ëĒŠë„ ė‚­ė œí•˜ė§€ ė•Šęŗ , ëĒ¨ë“  뤑ëŗĩ ęˇ¸ëŖšė„ í™•ė¸í•œ 것ėœŧ로 래ëĻŦ합니다.", + "bulk_trash_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėŧ괄 íœ´ė§€í†ĩėœŧ로 ė´ë™í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 각 ęˇ¸ëŖšė—ė„œ 가ėžĨ 큰 항ëĒŠë§Œ ë‚¨ę¸°ęŗ  ë‚˜ë¨¸ė§€ 뤑ëŗĩ 항ëĒŠė„ íœ´ė§€í†ĩėœŧ로 ė´ë™í•Šë‹ˆë‹¤.", "buy": "Immich ęĩŦ매", - "cache_settings_album_thumbnails": "ëŧė´ë¸ŒëŸŦëĻŦ ė„Ŧ네ėŧ ({})", + "cache_settings_album_thumbnails": "ëŧė´ë¸ŒëŸŦëĻŦ íŽ˜ė´ė§€ ė„Ŧ네ėŧ ({count} 항ëĒŠ)", "cache_settings_clear_cache_button": "ėēė‹œ ė§€ėš°ę¸°", "cache_settings_clear_cache_button_title": "ė•ą ėēė‹œëĨŧ ė§€ė›ë‹ˆë‹¤. ė´ ėž‘ė—…ė€ ėēė‹œę°€ ë‹¤ė‹œ ėƒė„ąë  ë•ŒęšŒė§€ ė•ą ė„ąëŠĨ뗐 ėƒë‹ší•œ 똁í–Ĩė„ ë¯¸ėš  눘 ėžˆėŠĩ니다.", "cache_settings_duplicated_assets_clear_button": "ė§€ėš°ę¸°", "cache_settings_duplicated_assets_subtitle": "ė—…ëĄœë“œë˜ė§€ ė•ŠëŠ” ė‚Ŧė§„ 및 ë™ė˜ėƒ", - "cache_settings_duplicated_assets_title": "뤑ëŗĩ 항ëĒŠ ({}氜)", - "cache_settings_image_cache_size": "ė´ë¯¸ė§€ ėēė‹œ íŦ기 ({})", + "cache_settings_duplicated_assets_title": "뤑ëŗĩ 항ëĒŠ ({count})", + "cache_settings_image_cache_size": "ė´ë¯¸ė§€ ėēė‹œ íŦ기 ({count} 항ëĒŠ)", "cache_settings_statistics_album": "ëŧė´ë¸ŒëŸŦëĻŦ ė„Ŧ네ėŧ", - "cache_settings_statistics_assets": "항ëĒŠ {}氜 ({})", + "cache_settings_statistics_assets": "항ëĒŠ {count}氜 ({size})", "cache_settings_statistics_full": "렄랴 ė´ë¯¸ė§€", "cache_settings_statistics_shared": "ęŗĩ뜠 ė•¨ë˛” ė„Ŧ네ėŧ", "cache_settings_statistics_thumbnail": "ė„Ŧ네ėŧ", "cache_settings_statistics_title": "ėēė‹œ ė‚ŦėšŠëĨ ", "cache_settings_subtitle": "Immich ëĒ¨ë°”ėŧ ė•ąė˜ ėēė‹ą ë™ėž‘ ė œė–´", - "cache_settings_thumbnail_size": "ė„Ŧ네ėŧ ėēė‹œ íŦ기 ({})", + "cache_settings_thumbnail_size": "ė„Ŧ네ėŧ ėēė‹œ íŦ기 ({count} 항ëĒŠ)", "cache_settings_tile_subtitle": "로ėģŦ ėŠ¤í† ëĻŦė§€ ë™ėž‘ ė œė–´", "cache_settings_tile_title": "로ėģŦ ėŠ¤í† ëĻŦė§€", "cache_settings_title": "ėēė‹œ 네렕", @@ -593,12 +599,14 @@ "camera_model": "ėš´ëŠ”ëŧ ëĒ¨ë¸", "cancel": "ë‹Ģ기", "cancel_search": "ę˛€ėƒ‰ ë‹Ģ기", - "canceled": "Canceled", + "canceled": "ė¤‘ë‹¨ë¨", "cannot_merge_people": "ė¸ëŦŧė„ ëŗ‘í•Ší•  눘 ė—†ėŠĩ니다.", "cannot_undo_this_action": "ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", "cannot_update_the_description": "네ëĒ…ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", + "cast": "ėēėŠ¤íŠ¸", "change_date": "ë‚ ė§œ ëŗ€ę˛Ŋ", - "change_display_order": "Change display order", + "change_description": "네ëĒ… ëŗ€ę˛Ŋ", + "change_display_order": "í‘œė‹œ ėˆœė„œ ëŗ€ę˛Ŋ", "change_expiration_time": "ë§ŒëŖŒėŧ ëŗ€ę˛Ŋ", "change_location": "ėœ„ėš˜ ëŗ€ę˛Ŋ", "change_name": "ė´ëĻ„ ëŗ€ę˛Ŋ", @@ -610,12 +618,13 @@ "change_password_form_new_password": "냈 비밀번호 ėž…ë Ĩ", "change_password_form_password_mismatch": "비밀번호가 ėŧėš˜í•˜ė§€ ė•ŠėŠĩ니다.", "change_password_form_reenter_new_password": "냈 비밀번호 í™•ė¸", + "change_pin_code": "PIN ėŊ”드 ëŗ€ę˛Ŋ", "change_your_password": "비밀번호 ëŗ€ę˛Ŋ", "changed_visibility_successfully": "í‘œė‹œ ė—Ŧëļ€ę°€ ė„ąęŗĩ렁ėœŧ로 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "check_all": "ëĒ¨ë‘ í™•ė¸", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "ë°ąė—…ëœ 항ëĒŠė˜ ė†ėƒ ė—Ŧëļ€ í™•ė¸", + "check_corrupt_asset_backup_button": "í™•ė¸ ėˆ˜í–‰", + "check_corrupt_asset_backup_description": "ė´ 검ė‚Ŧ는 ëĒ¨ë“  항ëĒŠė´ ë°ąė—…ëœ 후 Wi-Fi가 ė—°ę˛°ëœ ėƒíƒœė—ė„œë§Œ ė‹¤í–‰í•˜ė„¸ėš”. ė´ ėž‘ė—…ė€ ëLJ ëļ„ ė •ë„ ė†Œėš”ë  눘 ėžˆėŠĩ니다.", "check_logs": "로그 í™•ė¸", "choose_matching_people_to_merge": "ëŗ‘í•Ší•  ė¸ëŦŧ ė„ íƒ", "city": "ë„ė‹œ", @@ -627,10 +636,10 @@ "client_cert_dialog_msg_confirm": "í™•ė¸", "client_cert_enter_password": "비밀번호 ėž…ë Ĩ", "client_cert_import": "ę°€ė ¸ė˜¤ę¸°", - "client_cert_import_success_msg": "클ëŧė´ė–¸íŠ¸ ė¸ėĻė„œëĨŧ 氀렏뙔ėŠĩ니다.", - "client_cert_invalid_msg": "ėœ íš¨í•˜ė§€ ė•Šė€ ė¸ėĻė„œ 또는 íŒ¨ėŠ¤í”„ë ˆė´ėĻˆę°€ ėŧėš˜í•˜ė§€ ė•ŠėŠĩ니다.", - "client_cert_remove_msg": "클ëŧė´ė–¸íŠ¸ ė¸ėĻė„œę°€ ė œęą°ë˜ė—ˆėŠĩ니다.", - "client_cert_subtitle": "ė¸ėĻė„œ ę°€ė ¸ė˜¤ę¸°/ė œęą°ëŠ” ëĄœęˇ¸ė¸ ė „ė—ë§Œ 가ëŠĨ합니다. PKCS12 (.p12, .pfx) í˜•ė‹ė„ ė§€ė›í•Šë‹ˆë‹¤.", + "client_cert_import_success_msg": "클ëŧė´ė–¸íŠ¸ ė¸ėĻė„œ ę°€ė ¸ė˜¤ę¸° ė™„ëŖŒ", + "client_cert_invalid_msg": "ė¸ėĻė„œę°€ ėœ íš¨í•˜ė§€ ė•Šęą°ë‚˜ 비밀번호가 ė˜Ŧ바ëĨ´ė§€ ė•ŠėŒ", + "client_cert_remove_msg": "클ëŧė´ė–¸íŠ¸ ė¸ėĻė„œ ė œęą°ë¨", + "client_cert_subtitle": "PKCS12 (.p12, .pfx) í˜•ė‹ė„ ė§€ė›í•Šë‹ˆë‹¤. ė¸ėĻė„œ ę°€ė ¸ė˜¤ę¸° 및 ė œęą°ëŠ” ëĄœęˇ¸ė¸ ė „ė—ë§Œ 가ëŠĨ합니다.", "client_cert_title": "SSL 클ëŧė´ė–¸íŠ¸ ė¸ėĻė„œ", "clockwise": "ė‹œęŗ„ ë°Ší–Ĩ", "close": "ë‹Ģ기", @@ -644,23 +653,24 @@ "comments_are_disabled": "ëŒ“ę¸€ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "common_create_new_album": "ė•¨ë˛” ėƒė„ą", "common_server_error": "ë„¤íŠ¸ė›ŒíŦ 뗰枰 ėƒíƒœëĨŧ í™•ė¸í•˜ęŗ , ė„œë˛„ė— ė ‘ė†í•  눘 ėžˆëŠ”ė§€, ė•ą/ė„œë˛„ ë˛„ė „ė´ í˜¸í™˜ë˜ëŠ”ė§€ í™•ė¸í•´ėŖŧė„¸ėš”.", - "completed": "Completed", + "completed": "ė™„ëŖŒë¨", "confirm": "í™•ė¸", "confirm_admin_password": "관ëĻŦėž 비밀번호 í™•ė¸", - "confirm_delete_face": "ė—ė…‹ė—ė„œ {name} ė–ŧęĩ´ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", + "confirm_delete_face": "항ëĒŠė—ė„œ {name}ė˜ ė–ŧęĩ´ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "confirm_delete_shared_link": "ė´ ęŗĩ뜠 링íŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "confirm_keep_this_delete_others": "ė´ ė—ė…‹ė„ ė œė™¸í•œ ėŠ¤íƒė˜ 다ëĨ¸ ëĒ¨ë“  ė—ė…‹ė´ ė‚­ė œëŠë‹ˆë‹¤. ęŗ„ė†í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_keep_this_delete_others": "ė´ 항ëĒŠė„ ė œė™¸í•œ ėŠ¤íƒė˜ ëĒ¨ë“  항ëĒŠė´ ė‚­ė œëŠë‹ˆë‹¤. ęŗ„ė†í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_new_pin_code": "냈 PIN ėŊ”드 í™•ė¸", "confirm_password": "비밀번호 í™•ė¸", "contain": "맞ėļ¤", "context": "ë‚´ėšŠ", "continue": "ęŗ„ė†", - "control_bottom_app_bar_album_info_shared": "항ëĒŠ {}氜 ¡ ęŗĩėœ ë¨", + "control_bottom_app_bar_album_info_shared": "항ëĒŠ {count}氜 ¡ ęŗĩėœ ë¨", "control_bottom_app_bar_create_new_album": "ė•¨ë˛” ėƒė„ą", "control_bottom_app_bar_delete_from_immich": "Immichė—ė„œ ė‚­ė œ", "control_bottom_app_bar_delete_from_local": "ę¸°ę¸°ė—ė„œ ė‚­ė œ", "control_bottom_app_bar_edit_location": "ėœ„ėš˜ íŽ¸ė§‘", - "control_bottom_app_bar_edit_time": "ë‚ ė§œ 및 ė‹œę°„ ëŗ€ę˛Ŋ", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_edit_time": "ë‚ ė§œ ëŗ€ę˛Ŋ", + "control_bottom_app_bar_share_link": "ęŗĩ뜠 링íŦ", "control_bottom_app_bar_share_to": "ęŗĩ뜠 ëŒ€ėƒ", "control_bottom_app_bar_trash_from_immich": "íœ´ė§€í†ĩ", "copied_image_to_clipboard": "ė´ë¯¸ė§€ę°€ 클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧë˜ė—ˆėŠĩ니다.", @@ -692,10 +702,12 @@ "create_tag_description": "냈 태그ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤. í•˜ėœ„ íƒœęˇ¸ė˜ ę˛Ŋ뚰 /ëĨŧ íŦ함한 렄랴 태그ëĒ…ė„ ėž…ë Ĩí•˜ė„¸ėš”.", "create_user": "ė‚ŦėšŠėž ėƒė„ą", "created": "ėƒė„ąë¨", + "created_at": "ėƒė„ąë¨", "crop": "ėžëĨ´ę¸°", "curated_object_page_title": "ė‚ŦëŦŧ", "current_device": "현ėžŦ 기기", - "current_server_address": "Current server address", + "current_pin_code": "현ėžŦ PIN ėŊ”드", + "current_server_address": "현ėžŦ ė„œë˛„ ėŖŧė†Œ", "custom_locale": "ė‚ŦėšŠėž 맀렕 로ėŧ€ėŧ", "custom_locale_description": "떏떴 및 맀뗭뗐 따ëĨ¸ ë‚ ė§œ 및 ėˆĢėž í˜•ė‹ 맀렕", "daily_title_text_date": "Mė›” dėŧ EEEE", @@ -709,29 +721,29 @@ "date_range": "ë‚ ė§œ ë˛”ėœ„", "day": "ėŧ", "deduplicate_all": "ëĒ¨ë‘ ė‚­ė œ", - "deduplication_criteria_1": "ė´ë¯¸ė§€ íŦ기(ë°”ė´íŠ¸)", - "deduplication_criteria_2": "EXIF ë°ė´í„° 氜눘", + "deduplication_criteria_1": "ė´ë¯¸ė§€ íŦ기 (ë°”ė´íŠ¸)", + "deduplication_criteria_2": "EXIF ė •ëŗ´ 항ëĒŠ 눘", "deduplication_info": "뤑ëŗĩ ė œęą° ė •ëŗ´", - "deduplication_info_description": "ėžė‚°ė„ ėžë™ėœŧ로 미ëĻŦ ė„ íƒí•˜ęŗ  ėŧ洄렁ėœŧ로 뤑ëŗĩė„ ė œęą°í•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‚´íŽ´ëŗ´ė„¸ėš”:", + "deduplication_info_description": "항ëĒŠė„ ėžë™ėœŧ로 미ëĻŦ ė„ íƒí•˜ęŗ  뤑ëŗĩ 항ëĒŠė„ ėŧ괄 ė œęą°í•˜ë ¤ëŠ´ ë‹¤ėŒė„ í™•ė¸í•˜ė„¸ėš”:", "default_locale": "ę¸°ëŗ¸ 로ėŧ€ėŧ", "default_locale_description": "브ëŧėš°ė € 로ėŧ€ėŧ뗐 따ëĨ¸ ë‚ ė§œ 및 ėˆĢėž í˜•ė‹ 맀렕", "delete": "ė‚­ė œ", "delete_album": "ė•¨ë˛” ė‚­ė œ", "delete_api_key_prompt": "API 키ëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "delete_dialog_alert": "ė´ 항ëĒŠė´ Immich 및 ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "delete_dialog_alert_local": "ė´ 항ëĒŠė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤. Immichė—ė„œëŠ” ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", - "delete_dialog_alert_local_non_backed_up": "ėŧëļ€ í•­ëĒŠė´ ë°ąė—…ë˜ė§€ ė•Šė•˜ėŠĩ니다. ë°ąė—…ë˜ė§€ ė•Šė€ 항ëĒŠė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "delete_dialog_alert_remote": "ė´ 항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "delete_dialog_alert": "ė´ 항ëĒŠë“¤ė´ Immich뙀 ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "delete_dialog_alert_local": "ė´ 항ëĒŠë“¤ė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤. Immich ė„œë˛„ė—ė„œëŠ” ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", + "delete_dialog_alert_local_non_backed_up": "ėŧëļ€ í•­ëĒŠė´ Immich뗐 ë°ąė—…ë˜ė§€ ė•Šė•˜ėœŧ늰, ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "delete_dialog_alert_remote": "ė´ 항ëĒŠë“¤ė´ Immich ė„œë˛„ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "delete_dialog_ok_force": "ëŦ´ė‹œí•˜ęŗ  ė‚­ė œ", "delete_dialog_title": "똁ęĩŦ렁ėœŧ로 ė‚­ė œ", - "delete_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠë“¤ė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", + "delete_duplicates_confirmation": "ė´ 뤑ëŗĩ 항ëĒŠë“¤ė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "delete_face": "ė–ŧęĩ´ ė‚­ė œ", "delete_key": "키 ė‚­ė œ", "delete_library": "ëŧė´ë¸ŒëŸŦëĻŦ ė‚­ė œ", "delete_link": "링íŦ ė‚­ė œ", "delete_local_dialog_ok_backed_up_only": "ë°ąė—…ëœ 항ëĒŠë§Œ ė‚­ė œ", "delete_local_dialog_ok_force": "ëŦ´ė‹œí•˜ęŗ  ė‚­ė œ", - "delete_others": "다ëĨ¸ ė‚Ŧ람 ė‚­ė œ", + "delete_others": "다ëĨ¸ ė¸ëŦŧ ė‚­ė œ", "delete_shared_link": "ęŗĩ뜠 링íŦ ė‚­ė œ", "delete_shared_link_dialog_title": "ęŗĩ뜠 링íŦ ė‚­ė œ", "delete_tag": "태그 ė‚­ė œ", @@ -741,12 +753,11 @@ "deletes_missing_assets": "ë””ėŠ¤íŦ뗐 ėĄ´ėžŦí•˜ė§€ ė•ŠëŠ” 항ëĒŠ ė œęą°", "description": "네ëĒ…", "description_input_hint_text": "네ëĒ… ėļ”ę°€...", - "description_input_submit_error": "네ëĒ…ė„ ëŗ€ę˛Ŋ하는 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다. ėžė„¸í•œ ë‚´ėšŠė€ 로그ëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", + "description_input_submit_error": "네ëĒ… ė—…ë°ė´íŠ¸ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ėžė„¸í•œ ë‚´ėšŠė€ 로그ëĨŧ í™•ė¸í•˜ė„¸ėš”.", "details": "ėƒė„¸ ė •ëŗ´", "direction": "ë°Ší–Ĩ", "disabled": "ëš„í™œė„ąí™”ë¨", "disallow_edits": "ëˇ°ė–´ëĄœ 네렕", - "discord": "ë””ėŠ¤ėŊ”드", "discover": "íƒėƒ‰", "dismiss_all_errors": "ëĒ¨ë“  똤ëĨ˜ ëŦ´ė‹œ", "dismiss_error": "똤ëĨ˜ ëŦ´ė‹œ", @@ -761,9 +772,9 @@ "download_canceled": "ë‹¤ėš´ëĄœë“œę°€ ėˇ¨ė†Œë˜ė—ˆėŠĩ니다.", "download_complete": "ë‹¤ė€ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "download_enqueue": "ëŒ€ę¸°ė—´ė— ë‹¤ėš´ëĄœë“œ", - "download_error": "ë‹¤ėš´ëĄœë“œ 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", - "download_failed": "ë‹¤ėš´ëĄœë“œė— ė‹¤íŒ¨í•˜ė˜€ėŠĩ니다.", - "download_filename": "파ėŧ: {}", + "download_error": "ë‹¤ėš´ëĄœë“œ 똤ëĨ˜", + "download_failed": "ë‹¤ėš´ëĄœë“œ ė‹¤íŒ¨", + "download_filename": "파ėŧ: {filename}", "download_finished": "ë‹¤ėš´ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "download_include_embedded_motion_videos": "내ėžĨ된 ë™ė˜ėƒ", "download_include_embedded_motion_videos_description": "ëĒ¨ė…˜ íŦí† ė— 내ėžĨ된 ë™ė˜ėƒė„ ę°œëŗ„ 파ėŧ로 íŦ함", @@ -773,20 +784,21 @@ "download_settings_description": "ë‹¤ėš´ëĄœë“œ 네렕 관ëĻŦ", "download_started": "ë‹¤ėš´ëĄœë“œę°€ ė‹œėž‘ë˜ė—ˆėŠĩ니다.", "download_sucess": "ë‹¤ėš´ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", - "download_sucess_android": "ë¯¸ë””ė–´ę°€ DCIM/Immich뗐 ė €ėžĨë˜ė—ˆėŠĩ니다.", + "download_sucess_android": "ë¯¸ë””ė–´ę°€ DCIM/Immich í´ë”ė— ė €ėžĨë˜ė—ˆėŠĩ니다.", "download_waiting_to_retry": "ėžŦė‹œë„ 대기 뤑", "downloading": "ë‹¤ėš´ëĄœë“œ", "downloading_asset_filename": "{filename} ë‹¤ėš´ëĄœë“œ 뤑...", "downloading_media": "ë¯¸ë””ė–´ ë‹¤ėš´ëĄœë“œ 뤑", "drop_files_to_upload": "ė•„ëŦ´ ęŗŗė—ë‚˜ 파ėŧė„ 드롭하ė—Ŧ ė—…ëĄœë“œ", - "duplicates": "ëš„ėŠˇí•œ 항ëĒŠ", - "duplicates_description": "ëš„ėŠˇí•œ 항ëĒŠë“¤ė„ í™•ė¸í•˜ęŗ , ėœ ė§€í•˜ęą°ë‚˜ ė‚­ė œí•  항ëĒŠ ė„ íƒ", + "duplicates": "뤑ëŗĩ 항ëĒŠ", + "duplicates_description": "각 ęˇ¸ëŖšė—ė„œ 뤑ëŗĩ된 항ëĒŠė„ í™•ė¸í•˜ęŗ  ė‚­ė œí•  항ëĒŠė„ ė„ íƒí•˜ė„¸ėš”.", "duration": "기간", "edit": "íŽ¸ė§‘", "edit_album": "ė•¨ë˛” ėˆ˜ė •", "edit_avatar": "프로필 ėˆ˜ė •", "edit_date": "ë‚ ė§œ ëŗ€ę˛Ŋ", "edit_date_and_time": "ë‚ ė§œ 및 ė‹œę°„ ëŗ€ę˛Ŋ", + "edit_description": "네ëĒ… íŽ¸ė§‘", "edit_exclusion_pattern": "ė œė™¸ ęˇœėš™ ėˆ˜ė •", "edit_faces": "ė–ŧęĩ´ ėˆ˜ė •", "edit_import_path": "氀렏ė˜Ŧ ę˛Ŋ로 ėˆ˜ė •", @@ -807,19 +819,23 @@ "editor_crop_tool_h2_aspect_ratios": "ėĸ…횥뚄", "editor_crop_tool_h2_rotation": "íšŒė „", "email": "ė´ëŠ”ėŧ", - "empty_folder": "This folder is empty", + "email_notifications": "ė´ëŠ”ėŧ ė•ŒëĻŧ", + "empty_folder": "폴더가 ëš„ė–´ ėžˆėŒ", "empty_trash": "íœ´ė§€í†ĩ ëš„ėš°ę¸°", "empty_trash_confirmation": "íœ´ė§€í†ĩė„ ëš„ėš°ė‹œę˛ ėŠĩ니까? íœ´ė§€í†ĩ뗐 ėžˆëŠ” ëĒ¨ë“  항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.\nė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", "enable": "í™œė„ąí™”", + "enable_biometric_auth_description": "ėƒė˛´ ė¸ėĻė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ PIN ėŊ”드ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "enabled": "í™œė„ąí™”ë¨", "end_date": "ėĸ…ëŖŒėŧ", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "ëŒ€ę¸°ė—´ė— ėļ”가됨", + "enter_wifi_name": "Wi-Fi ė´ëĻ„ ėž…ë Ĩ", + "enter_your_pin_code": "PIN ėŊ”드 ėž…ë Ĩ", + "enter_your_pin_code_subtitle": "ėž ę¸´ í´ë”ė— ė ‘ęˇŧ하려면 PIN ėŊ”드ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "error": "똤ëĨ˜", - "error_change_sort_album": "Failed to change album sort order", - "error_delete_face": "ė—ė…‹ė—ė„œ ė–ŧęĩ´ ė‚­ė œ 똤ëĨ˜", + "error_change_sort_album": "ė•¨ë˛” í‘œė‹œ ėˆœė„œ ëŗ€ę˛Ŋ ė‹¤íŒ¨", + "error_delete_face": "ė–ŧęĩ´ ė‚­ė œ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", "error_loading_image": "ė´ë¯¸ė§€ 로드 똤ëĨ˜", - "error_saving_image": "똤ëĨ˜: {}", + "error_saving_image": "똤ëĨ˜: {error}", "error_title": "똤ëĨ˜ - ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다", "errors": { "cannot_navigate_next_asset": "ë‹¤ėŒ 항ëĒŠėœŧ로 ė´ë™í•  눘 ė—†ėŠĩ니다.", @@ -846,13 +862,15 @@ "failed_to_create_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_edit_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėˆ˜ė •í•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_get_people": "ė¸ëŦŧ 로드 ė‹¤íŒ¨", - "failed_to_keep_this_delete_others": "ė´ ėžė‚°ė„ ėœ ė§€í•˜ęŗ  다ëĨ¸ ėžė‚°ė„ ė‚­ė œí•˜ė§€ ëĒģ했ėŠĩ니다", + "failed_to_keep_this_delete_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  다ëĨ¸ 항ëĒŠė„ ė‚­ė œí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_load_asset": "항ëĒŠ 로드 ė‹¤íŒ¨", "failed_to_load_assets": "항ëĒŠ 로드 ė‹¤íŒ¨", + "failed_to_load_notifications": "ė•ŒëĻŧ 로드 ė‹¤íŒ¨", "failed_to_load_people": "ė¸ëŦŧ 로드 ė‹¤íŒ¨", "failed_to_remove_product_key": "ė œí’ˆ 키ëĨŧ ė œęą°í•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_stack_assets": "ėŠ¤íƒė„ ë§Œë“¤ė§€ ëĒģ했ėŠĩ니다.", "failed_to_unstack_assets": "ėŠ¤íƒė„ í•´ė œí•˜ė§€ ëĒģ했ėŠĩ니다.", + "failed_to_update_notification_status": "ė•ŒëĻŧ ėƒíƒœ ė—…ë°ė´íŠ¸ ė‹¤íŒ¨", "import_path_already_exists": "ė´ 氀렏ė˜Ŧ ę˛Ŋ로는 ė´ë¯¸ ėĄ´ėžŦ합니다.", "incorrect_email_or_password": "ėž˜ëĒģ된 ė´ëŠ”ėŧ 또는 비밀번호", "paths_validation_failed": "ę˛Ŋ로 {paths, plural, one {#氜} other {#氜}}ëĨŧ 검ėĻí•˜ė§€ ëĒģ했ėŠĩ니다.", @@ -870,6 +888,7 @@ "unable_to_archive_unarchive": "{archived, select, true {ëŗ´ę´€í•¨ėœŧ로 항ëĒŠė„ ė´ë™í• } other {ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠė„ ė œęą°í• }} 눘 ė—†ėŠĩ니다.", "unable_to_change_album_user_role": "ė‚ŦėšŠėžė˜ ė—­í• ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_date": "ë‚ ė§œëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", + "unable_to_change_description": "네ëĒ…ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_favorite": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€/ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_change_location": "ėœ„ėš˜ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_password": "비밀번호ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", @@ -907,6 +926,7 @@ "unable_to_log_out_all_devices": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•  눘 ė—†ėŠĩ니다.", "unable_to_log_out_device": "ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•  눘 ė—†ėŠĩ니다.", "unable_to_login_with_oauth": "OAuth로 ëĄœęˇ¸ė¸í•  눘 ė—†ėŠĩ니다.", + "unable_to_move_to_locked_folder": "ėž ę¸´ 폴더로 ė´ë™í•  눘 ė—†ėŠĩ니다.", "unable_to_play_video": "ë™ė˜ėƒė„ ėžŦėƒí•  눘 ė—†ėŠĩ니다.", "unable_to_reassign_assets_existing_person": "항ëĒŠė„ {name, select, null {다ëĨ¸ ė¸ëŦŧė—ę˛Œ} other {{name}ė—ę˛Œ}} 할당할 눘 ė—†ėŠĩ니다.", "unable_to_reassign_assets_new_person": "항ëĒŠė„ 냈 ė¸ëŦŧ뗐 할당할 눘 ė—†ėŠĩ니다.", @@ -920,7 +940,8 @@ "unable_to_remove_reaction": "ë°˜ė‘ė„ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_repair_items": "항ëĒŠė„ 눘ëĻŦ할 눘 ė—†ėŠĩ니다.", "unable_to_reset_password": "비밀번호ëĨŧ ė´ˆę¸°í™”í•  눘 ė—†ėŠĩ니다.", - "unable_to_resolve_duplicate": "ëš„ėŠˇí•œ 항ëĒŠė„ 래ëĻŦ할 눘 ė—†ėŠĩ니다.", + "unable_to_reset_pin_code": "PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í•  눘 ė—†ėŒ", + "unable_to_resolve_duplicate": "뤑ëŗĩ된 항ëĒŠė„ 래ëĻŦ할 눘 ė—†ėŠĩ니다.", "unable_to_restore_assets": "항ëĒŠė„ ëŗĩė›í•  눘 ė—†ėŠĩ니다.", "unable_to_restore_trash": "íœ´ė§€í†ĩė—ė„œ 항ëĒŠė„ ëŗĩė›í•  눘 ė—†ėŒ", "unable_to_restore_user": "ė‚ŦėšŠėž ė‚­ė œëĨŧ ėˇ¨ė†Œí•  눘 ė—†ėŠĩ니다.", @@ -953,10 +974,10 @@ "exif_bottom_sheet_location": "ėœ„ėš˜", "exif_bottom_sheet_people": "ė¸ëŦŧ", "exif_bottom_sheet_person_add_person": "ė´ëĻ„ ėļ”ę°€", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "{age}넏", + "exif_bottom_sheet_person_age_months": "ėƒí›„ {months}ę°œė›”", + "exif_bottom_sheet_person_age_year_months": "ėƒí›„ 1년 {months}ę°œė›”", + "exif_bottom_sheet_person_age_years": "{years}넏", "exit_slideshow": "ėŠŦëŧė´ë“œ ė‡ŧ ėĸ…ëŖŒ", "expand_all": "ëĒ¨ë‘ 확ėžĨ", "experimental_settings_new_asset_list_subtitle": "ė§„í–‰ 뤑", @@ -973,12 +994,13 @@ "extension": "확ėžĨėž", "external": "뙏ëļ€", "external_libraries": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "뙏ëļ€ ë„¤íŠ¸ė›ŒíŦ", + "external_network_sheet_info": "ė„ í˜¸í•˜ëŠ” Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆė§€ ė•Šė€ ę˛Ŋ뚰, ė•ąė€ ė•„ëž˜ė— ë‚˜ė—´ëœ URL 뤑 뗰枰 가ëŠĨ한 ė˛Ģ ë˛ˆė§¸ ėŖŧė†ŒëĨŧ ėœ„ė—ė„œëļ€í„° ėˆœė„œëŒ€ëĄœ ė‚ŦėšŠí•Šë‹ˆë‹¤.", "face_unassigned": "ė•Œ 눘 ė—†ėŒ", - "failed": "Failed", - "failed_to_load_assets": "뗐녋 ëĄœë“œė— ė‹¤íŒ¨í–ˆėŠĩ니다", - "failed_to_load_folder": "Failed to load folder", + "failed": "ė‹¤íŒ¨í•¨", + "failed_to_authenticate": "ė¸ėĻė— ė‹¤íŒ¨í–ˆėŠĩ니다.", + "failed_to_load_assets": "항ëĒŠ 로드 ė‹¤íŒ¨", + "failed_to_load_folder": "폴더 로드 ė‹¤íŒ¨", "favorite": "ėĻę˛¨ė°žę¸°", "favorite_or_unfavorite_photo": "ėĻę˛¨ė°žę¸° ėļ”ę°€/ė œęą°", "favorites": "ėĻę˛¨ė°žę¸°", @@ -992,26 +1014,27 @@ "filetype": "파ėŧ í˜•ė‹", "filter": "필터", "filter_people": "ė¸ëŦŧ 필터", + "filter_places": "ėžĨė†Œ 필터링", "find_them_fast": "ė´ëĻ„ėœŧ로 ę˛€ėƒ‰í•˜ė—Ŧ ëš ëĨ´ę˛Œ ė°žę¸°", "fix_incorrect_match": "ėž˜ëĒģ된 ëļ„ëĨ˜ ėˆ˜ė •", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "폴더", + "folder_not_found": "폴더ëĨŧ ė°žė„ 눘 ė—†ėŒ", "folders": "폴더", - "folders_feature_description": "파ėŧ ė‹œėŠ¤í…œė˜ ė‚Ŧė§„ 및 ë™ė˜ėƒė„ 폴더 뷰로 íƒėƒ‰", + "folders_feature_description": "파ėŧ ė‹œėŠ¤í…œė— ėžˆëŠ” ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ 폴더 ëŗ´ę¸°ëĄœ íƒėƒ‰", "forward": "ė•žėœŧ로", "general": "ėŧ반", "get_help": "ë„ė›€ ėš”ė˛­", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "Wi-Fi ė´ëĻ„ė„ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다. í•„ėš”í•œ ęļŒí•œė´ í—ˆėšŠë˜ė–´ ėžˆęŗ  Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆëŠ”ė§€ í™•ė¸í•˜ė„¸ėš”.", "getting_started": "ė‹œėž‘í•˜ę¸°", "go_back": "뒤로", "go_to_folder": "폴더로 ė´ë™", "go_to_search": "ę˛€ėƒ‰ėœŧ로 ė´ë™", - "grant_permission": "Grant permission", + "grant_permission": "ęļŒí•œ ëļ€ė—Ŧ", "group_albums_by": "ë‹¤ėŒėœŧ로 ė•¨ë˛” ęˇ¸ëŖší™”...", - "group_country": "ęĩ­ę°€ëŗ„ ęˇ¸ëŖší™”", + "group_country": "ęĩ­ę°€ëŗ„ëĄœ ęˇ¸ëŖší™”", "group_no": "ęˇ¸ëŖší™” ė—†ėŒ", "group_owner": "ė†Œėœ ėžëĄœ ęˇ¸ëŖší™”", - "group_places_by": "ėžĨė†Œ ęˇ¸ëŖší™” 揰뤀...", + "group_places_by": "ë‹¤ėŒėœŧ로 ėžĨė†Œ ęˇ¸ëŖší™”â€Ļ", "group_year": "ė—°ë„ëĄœ ęˇ¸ëŖší™”", "haptic_feedback_switch": "햅틱 í”ŧ드백 í™œė„ąí™”", "haptic_feedback_title": "햅틱 í”ŧ드백", @@ -1020,7 +1043,7 @@ "header_settings_field_validator_msg": "ę°’ė€ ëš„ė›Œë‘˜ 눘 ė—†ėŠĩ니다.", "header_settings_header_name_input": "헤더 ė´ëĻ„", "header_settings_header_value_input": "헤더 값", - "headers_settings_tile_subtitle": "각 ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė„ ëŗ´ë‚ŧ 때 ė‚ŦėšŠí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", + "headers_settings_tile_subtitle": "ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė— 함ęģ˜ ė „ė†Ąí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", "headers_settings_tile_title": "ė‚ŦėšŠėž ė •ė˜ í”„ëĄė‹œ 헤더", "hi_user": "ė•ˆë…•í•˜ė„¸ėš” {name}님, ({email})", "hide_all_people": "ëĒ¨ë“  ė¸ëŦŧ 눍揰揰", @@ -1040,13 +1063,13 @@ "home_page_delete_remote_err_local": "ė„œë˛„ė—ė„œ ė‚­ė œëœ 항ëĒŠėž…ë‹ˆë‹¤. 건너뜁니다.", "home_page_favorite_err_local": "ę¸°ę¸°ė˜ 항ëĒŠė€ ėĻę˛¨ė°žę¸°ė— ėļ”가할 눘 ė—†ėŠĩ니다. 건너뜁니다.", "home_page_favorite_err_partner": "íŒŒíŠ¸ë„ˆė˜ 항ëĒŠė€ ėĻę˛¨ė°žę¸°ė— ėļ”가할 눘 ė—†ėŠĩ니다. 건너뜁니다.", - "home_page_first_time_notice": "ė•ąė„ ė˛˜ėŒ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 íƒ€ėž„ëŧė¸ė— ė•¨ë˛”ė˜ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ėą„ėš¸ 눘 ėžˆë„ëĄ ë°ąė—…í•  ė•¨ë˛”ė„ ė„ íƒí•˜ė„¸ėš”.", - "home_page_share_err_local": "ę¸°ę¸°ė˜ 항ëĒŠė€ 링íŦ로 ęŗĩėœ í•  눘 ė—†ėŠĩ니다. 건너뜁니다.", + "home_page_first_time_notice": "ė•ąė„ ė˛˜ėŒ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰, 揰揰뗐 ėžˆëŠ” ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ íƒ€ėž„ëŧė¸ė— í‘œė‹œí•˜ęŗ  ë°ąė—…í•˜ë ¤ëŠ´ ë°ąė—…í•  ė•¨ë˛”ė„ ė„ íƒí•˜ė„¸ėš”.", + "home_page_share_err_local": "ę¸°ę¸°ė—ë§Œ ė €ėžĨ된 항ëĒŠė€ 링íŦ로 ęŗĩėœ í•  눘 뗆떴 건너뜁니다.", "home_page_upload_err_limit": "한 ë˛ˆė— ėĩœëŒ€ 30ę°œė˜ 항ëĒŠë§Œ ė—…ëĄœë“œí•  눘 ėžˆėŠĩ니다.", "host": "í˜¸ėŠ¤íŠ¸", "hour": "ė‹œę°„", "ignore_icloud_photos": "iCloud ė‚Ŧė§„ ė œė™¸", - "ignore_icloud_photos_description": "iCloud뗐 ė €ėžĨ된 ė‚Ŧė§„ė€ Immich ė„œë˛„ė— ė—…ëĄœë“œë˜ė§€ ė•ŠėŠĩ니다.", + "ignore_icloud_photos_description": "iCloud뗐 ė €ėžĨ된 ė‚Ŧė§„ė´ Immich뗐 ė—…ëĄœë“œë˜ė§€ ė•ŠėŠĩ니다.", "image": "ė´ë¯¸ė§€", "image_alt_text_date": "{date} ė´Ŧė˜í•œ {isVideo, select, true {ë™ė˜ėƒ} other {ė‚Ŧė§„}}", "image_alt_text_date_1_person": "{date} {person1}님ęŗŧ 함ęģ˜í•œ {isVideo, select, true {ë™ė˜ėƒ} other {ė‚Ŧė§„}}", @@ -1080,16 +1103,16 @@ "night_at_midnight": "매ėŧ ë°¤ ėžė •", "night_at_twoam": "매ėŧ 냈ë˛Ŋ 2ė‹œ" }, - "invalid_date": "ėž˜ëĒģ된 ë‚ ė§œėž…ë‹ˆë‹¤.", - "invalid_date_format": "ėž˜ëĒģ된 ë‚ ė§œ í˜•ė‹ėž…ë‹ˆë‹¤.", + "invalid_date": "ėœ íš¨í•˜ė§€ ė•Šė€ ë‚ ė§œ", + "invalid_date_format": "ėœ íš¨í•˜ė§€ ė•Šė€ ë‚ ė§œ í˜•ė‹", "invite_people": "ė‚ŦėšŠėž ė´ˆëŒ€", "invite_to_album": "ė•¨ë˛”ėœŧ로 ė´ˆëŒ€", "items_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠ", "jobs": "ėž‘ė—…", "keep": "ėœ ė§€", "keep_all": "ëĒ¨ë‘ ėœ ė§€", - "keep_this_delete_others": "ė´ 항ëĒŠė€ ëŗ´ę´€í•˜ęŗ  다ëĨ¸ 항ëĒŠė€ ė‚­ė œ", - "kept_this_deleted_others": "ė´ ėžė‚°ė„ ėœ ė§€í•˜ęŗ  {count, plural, one {# asset} other {# assets}}ė„ ė‚­ė œí–ˆėŠĩ니다", + "keep_this_delete_others": "ė´ 항ëĒŠė€ ėœ ė§€í•˜ęŗ  ë‚˜ë¨¸ė§€ëŠ” ė‚­ė œ", + "kept_this_deleted_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  {count, plural, one {#ę°œė˜ 항ëĒŠ} other {#ę°œė˜ 항ëĒŠ}}ė„ ė‚­ė œí–ˆėŠĩ니다.", "keyboard_shortcuts": "í‚¤ëŗ´ë“œ 단ėļ•키", "language": "떏떴", "language_setting_description": "ė„ í˜¸í•˜ëŠ” 떏떴 ė„ íƒ", @@ -1117,15 +1140,16 @@ "list": "ëĒŠëĄ", "loading": "로드 뤑", "loading_search_results_failed": "ę˛€ėƒ‰ 결ęŗŧ 로드 ė‹¤íŒ¨", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "ė§€ė •í•œ Wi-Fi뗐 ė—°ę˛°ëœ ę˛Ŋ뚰 ė•ąė€ 해당 URLė„ í†ĩ해 ė„œë˛„ė— ė—°ę˛°í•Šë‹ˆë‹¤.", + "location_permission": "ėœ„ėš˜ ęļŒí•œ", + "location_permission_content": "ėžë™ ė „í™˜ 기ëŠĨė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Immich가 현ėžŦ Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•˜ę¸° ėœ„í•œ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", "location_picker_choose_on_map": "ė§€ë„ė—ė„œ ė„ íƒ", "location_picker_latitude_error": "ėœ íš¨í•œ ėœ„ë„ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "location_picker_latitude_hint": "ė´ęŗŗė— ėœ„ë„ ėž…ë Ĩ", "location_picker_longitude_error": "ėœ íš¨í•œ ę˛Ŋ도ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "location_picker_longitude_hint": "ė´ęŗŗė— ę˛Ŋ도 ėž…ë Ĩ", + "lock": "ėž ę¸ˆ", + "locked_folder": "ėž ę¸´ 폴더", "log_out": "ëĄœęˇ¸ė•„ė›ƒ", "log_out_all_devices": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒ", "logged_out_all_devices": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒë˜ė—ˆėŠĩ니다.", @@ -1134,17 +1158,15 @@ "login_disabled": "ëĄœęˇ¸ė¸ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "login_form_api_exception": "API ė˜ˆė™¸ę°€ ë°œėƒí–ˆėŠĩ니다. ė„œë˛„ URLė„ í™•ė¸í•œ 후 ë‹¤ė‹œ ė‹œë„í•˜ė„¸ėš”.", "login_form_back_button_text": "뒤로", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ URL", "login_form_err_http": "http:// 또는 https://로 ė‹œėž‘í•´ė•ŧ 합니다.", "login_form_err_invalid_email": "ėœ íš¨í•˜ė§€ ė•Šė€ ė´ëŠ”ėŧ", "login_form_err_invalid_url": "ėž˜ëĒģ된 URLėž…ë‹ˆë‹¤.", - "login_form_err_leading_whitespace": "ëŦ¸ėž ė‹œėž‘ė— ęŗĩë°ąė´ ėžˆėŠĩ니다.", - "login_form_err_trailing_whitespace": "ëŦ¸ėž ëė— ęŗĩë°ąė´ ėžˆėŠĩ니다.", - "login_form_failed_get_oauth_server_config": "OAuth ëĄœęˇ¸ė¸ 뤑 ëŦ¸ė œ ë°œėƒ, ė„œë˛„ URLė„ í™•ė¸í•˜ė„¸ėš”.", + "login_form_err_leading_whitespace": "ė„ í–‰ ęŗĩë°ąė„ í™•ė¸í•˜ė„¸ėš”.", + "login_form_err_trailing_whitespace": "후행 ęŗĩë°ąė„ í™•ė¸í•˜ė„¸ėš”.", + "login_form_failed_get_oauth_server_config": "OAuth ëĄœęˇ¸ė¸ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ė„œë˛„ URLė„ í™•ė¸í•˜ė„¸ėš”.", "login_form_failed_get_oauth_server_disable": "ė´ ė„œë˛„ëŠ” OAuth 기ëŠĨė„ ė§€ė›í•˜ė§€ ė•ŠėŠĩ니다.", - "login_form_failed_login": "ëĄœęˇ¸ė¸ 똤ëĨ˜. ė„œë˛„ URL, ė´ëŠ”ėŧ 및 비밀번호ëĨŧ í™•ė¸í•˜ė„¸ėš”.", + "login_form_failed_login": "ëĄœęˇ¸ė¸ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ė„œë˛„ URL, ė´ëŠ”ėŧ, 비밀번호ëĨŧ í™•ė¸í•˜ė„¸ėš”.", "login_form_handshake_exception": "ė„œë˛„ė™€ í†ĩė‹  뤑 ė¸ėĻė„œ ė˜ˆė™¸ę°€ ë°œėƒí–ˆėŠĩ니다. ėžė˛´ ė„œëĒ…ëœ ė¸ėĻė„œëĨŧ ė‚ŦėšŠ ė¤‘ė´ëŧ늴, ė„¤ė •ė—ė„œ ėžė˛´ ė„œëĒ…ëœ ė¸ėĻė„œ í—ˆėšŠė„ í™œė„ąí™”í•˜ė„¸ėš”.", "login_form_password_hint": "비밀번호", "login_form_save_login": "ëĄœęˇ¸ė¸ ėœ ė§€", @@ -1152,7 +1174,7 @@ "login_form_server_error": "ė„œë˛„ė— ė—°ę˛°í•  눘 ė—†ėŠĩ니다.", "login_has_been_disabled": "ëĄœęˇ¸ė¸ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "login_password_changed_error": "비밀번호ëĨŧ ëŗ€ę˛Ŋ하던 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", - "login_password_changed_success": "비밀번호가 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", + "login_password_changed_success": "비밀번호가 ė„ąęŗĩ렁ėœŧ로 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "logout_all_device_confirmation": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•˜ė‹œę˛ ėŠĩ니까?", "logout_this_device_confirmation": "ė´ ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•˜ė‹œę˛ ėŠĩ니까?", "longitude": "ę˛Ŋ도", @@ -1170,9 +1192,9 @@ "manage_your_devices": "ëĄœęˇ¸ė¸ëœ 기기 관ëĻŦ", "manage_your_oauth_connection": "OAuth 뗰枰 관ëĻŦ", "map": "ė§€ë„", - "map_assets_in_bound": "ė‚Ŧė§„ {}氜", - "map_assets_in_bounds": "ė‚Ŧė§„ {}氜", - "map_cannot_get_user_location": "ė‚ŦėšŠėžė˜ ėœ„ėš˜ëĨŧ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다.", + "map_assets_in_bound": "ė‚Ŧė§„ {count}氜", + "map_assets_in_bounds": "ė‚Ŧė§„ {count}氜", + "map_cannot_get_user_location": "ė‚ŦėšŠėžė˜ ėœ„ėš˜ëĨŧ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다.", "map_location_dialog_yes": "똈", "map_location_picker_page_use_location": "ė´ ėœ„ėš˜ ė‚ŦėšŠ", "map_location_service_disabled_content": "현ėžŦ ėœ„ėš˜ė˜ 항ëĒŠė„ í‘œė‹œí•˜ë ¤ëŠ´ ėœ„ėš˜ ė„œëš„ėŠ¤ëĨŧ í™œė„ąí™”í•´ė•ŧ 합니다. ė§€ę¸ˆ í™œė„ąí™”í•˜ė‹œę˛ ėŠĩ니까?", @@ -1185,15 +1207,18 @@ "map_settings": "ė§€ë„ 네렕", "map_settings_dark_mode": "다íŦ ëĒ¨ë“œ", "map_settings_date_range_option_day": "ė§€ë‚œ 24ė‹œę°„", - "map_settings_date_range_option_days": "ė§€ë‚œ {}ėŧ", + "map_settings_date_range_option_days": "ė§€ë‚œ {days}ėŧ", "map_settings_date_range_option_year": "ė§€ë‚œ 1년", - "map_settings_date_range_option_years": "ė§€ë‚œ {}년", + "map_settings_date_range_option_years": "ė§€ë‚œ {years}년", "map_settings_dialog_title": "ė§€ë„ 네렕", "map_settings_include_show_archived": "ëŗ´ę´€ëœ 항ëĒŠ íŦ함", "map_settings_include_show_partners": "파트너가 ęŗĩėœ í•œ 항ëĒŠ íŦ함", "map_settings_only_show_favorites": "ėĻę˛¨ė°žę¸°ë§Œ í‘œė‹œ", "map_settings_theme_settings": "ė§€ë„ 테마", "map_zoom_to_see_photos": "ėļ•ė†Œí•˜ė—Ŧ ė‚Ŧė§„ ëŗ´ę¸°", + "mark_all_as_read": "ëĒ¨ë‘ ėŊėŒėœŧ로 í‘œė‹œ", + "mark_as_read": "ėŊėŒėœŧ로 í‘œė‹œ", + "marked_all_as_read": "ëĒ¨ë‘ ėŊė€ 것ėœŧ로 í‘œė‹œí–ˆėŠĩ니다.", "matches": "ėŧėš˜", "media_type": "ë¯¸ë””ė–´ ėĸ…ëĨ˜", "memories": "ėļ”ė–ĩ", @@ -1202,8 +1227,6 @@ "memories_setting_description": "ėļ”ė–ĩ í‘œė‹œ 네렕 관ëĻŦ", "memories_start_over": "ë‹¤ė‹œ ëŗ´ę¸°", "memories_swipe_to_close": "ėœ„ëĄœ ë°€ė–´ė„œ ë‹Ģ기", - "memories_year_ago": "1년 ė „", - "memories_years_ago": "{}년 ė „", "memory": "ėļ”ė–ĩ", "memory_lane_title": "{title} ėļ”ė–ĩ", "menu": "메뉴", @@ -1220,6 +1243,11 @@ "month": "ė›”", "monthly_title_text_date_format": "yyyy년 Mė›”", "more": "ë”ëŗ´ę¸°", + "move": "ė´ë™", + "move_to_locked_folder": "ėž ę¸´ 폴더로 ė´ë™", + "move_to_locked_folder_confirmation": "ė´ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė´ ëĒ¨ë“  ė•¨ë˛”ė—ė„œ ė œęą°ë˜ëŠ°, ėž ę¸´ í´ë”ė—ė„œë§Œ ëŗŧ 눘 ėžˆėŠĩ니다.", + "moved_to_archive": "ëŗ´ę´€í•¨ėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", + "moved_to_library": "ëŧė´ë¸ŒëŸŦëĻŦ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", "moved_to_trash": "íœ´ė§€í†ĩėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다.", "multiselect_grid_edit_date_time_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė˜ ë‚ ė§œëŠ” ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다. 건너뜁니다.", "multiselect_grid_edit_gps_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė˜ ėœ„ėš˜ëŠ” ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다. 건너뜁니다.", @@ -1227,13 +1255,15 @@ "my_albums": "내 ė•¨ë˛”", "name": "ė´ëĻ„", "name_or_nickname": "ė´ëĻ„ 또는 ë‹‰ë„¤ėž„", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "뗰枰", + "networking_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ 네렕 관ëĻŦ", "never": "ė—†ėŒ", "new_album": "냈 ė•¨ë˛”", "new_api_key": "API 키 ėƒė„ą", "new_password": "냈 비밀번호", "new_person": "냈 ė¸ëŦŧ ėƒė„ą", + "new_pin_code": "냈 PIN ėŊ”드", + "new_pin_code_subtitle": "ėž ę¸´ 폴더 ė„¤ė •ė„ ė‹œėž‘í•Šë‹ˆë‹¤. ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ė•ˆė „í•˜ę˛Œ ëŗ´í˜¸í•˜ę¸° ėœ„í•œ PIN ėŊ”드ëĨŧ ė„¤ė •í•˜ė„¸ėš”.", "new_user_created": "ė‚ŦėšŠėžę°€ ėƒė„ąë˜ė—ˆėŠĩ니다.", "new_version_available": "냈 ë˛„ė „ ė‚ŦėšŠ 가ëŠĨ", "newest_first": "ėĩœė‹ ėˆœ", @@ -1246,20 +1276,24 @@ "no_archived_assets_message": "ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ëŗ´ę´€í•¨ėœŧ로 ė´ë™í•˜ė—Ŧ ëĒŠëĄė—ė„œ 눍揰揰", "no_assets_message": "ė—Ŧ기ëĨŧ 클ëĻ­í•˜ė—Ŧ ė˛Ģ ė‚Ŧė§„ė„ ė—…ëĄœë“œí•˜ė„¸ėš”.", "no_assets_to_show": "í‘œė‹œí•  항ëĒŠ ė—†ėŒ", - "no_duplicates_found": "ëš„ėŠˇí•œ 항ëĒŠė„ ė°žė„ 눘 ė—†ėŠĩ니다.", + "no_duplicates_found": "뤑ëŗĩ된 항ëĒŠė´ ė—†ėŠĩ니다.", "no_exif_info_available": "EXIF ė •ëŗ´ ė—†ėŒ", "no_explore_results_message": "더 ë§Žė€ ė‚Ŧė§„ė„ ė—…ëĄœë“œí•˜ė—Ŧ íƒėƒ‰ 기ëŠĨė„ ė‚ŦėšŠí•˜ė„¸ėš”.", "no_favorites_message": "ėĻę˛¨ė°žę¸°ė— ėĸ‹ė•„하는 ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ėļ”ę°€í•˜ę¸°", "no_libraries_message": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒė„ąí•˜ė—Ŧ ę¸°ėĄ´ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ í™•ė¸í•˜ė„¸ėš”.", + "no_locked_photos_message": "ėž ę¸´ í´ë”ė˜ ė‚Ŧė§„ 및 ë™ė˜ėƒė€ ėˆ¨ę˛¨ė§€ëŠ° ëŧė´ë¸ŒëŸŦëĻŦëĨŧ íƒėƒ‰í•  때 í‘œė‹œë˜ė§€ ė•ŠėŠĩ니다.", "no_name": "ė´ëĻ„ ė—†ėŒ", + "no_notifications": "ė•ŒëĻŧ ė—†ėŒ", + "no_people_found": "ėŧėš˜í•˜ëŠ” ė¸ëŦŧ ė—†ėŒ", "no_places": "ėžĨė†Œ ė—†ėŒ", "no_results": "결ęŗŧ가 ė—†ėŠĩ니다.", "no_results_description": "ë™ė˜ė–´ 또는 더 ėŧë°˜ė ė¸ ë‹¨ė–´ëĨŧ ė‚ŦėšŠí•´ ëŗ´ė„¸ėš”.", "no_shared_albums_message": "ęŗĩ뜠 ė•¨ë˛”ė„ ë§Œë“¤ė–´ ėŖŧëŗ€ ė‚Ŧ람들ęŗŧ ė‚Ŧė§„ 및 ë™ė˜ėƒ ęŗĩ뜠", "not_in_any_album": "ė•¨ë˛”ė— ė—†ėŒ", - "not_selected": "Not selected", + "not_selected": "ė„ íƒë˜ė§€ ė•ŠėŒ", "note_apply_storage_label_to_previously_uploaded assets": "및溠: ė´ė „ė— ė—…ëĄœë“œí•œ 항ëĒŠė—ë„ ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ė„ ė ėšŠí•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‹¤í–‰í•Šë‹ˆë‹¤,", "notes": "및溠", + "nothing_here_yet": "땄링 ė•„ëŦ´ę˛ƒë„ ė—†ėŒ", "notification_permission_dialog_content": "ė•ŒëĻŧė„ í™œė„ąí™”í•˜ë ¤ëŠ´ ė„¤ė •ė—ė„œ ė•ŒëĻŧ ęļŒí•œė„ í—ˆėšŠí•˜ė„¸ėš”.", "notification_permission_list_tile_content": "ė•ŒëĻŧė„ í™œė„ąí™”í•˜ë ¤ëŠ´ ęļŒí•œė„ ëļ€ė—Ŧí•˜ė„¸ėš”.", "notification_permission_list_tile_enable_button": "ė•ŒëĻŧ í™œė„ąí™”", @@ -1267,7 +1301,6 @@ "notification_toggle_setting_description": "ė´ëŠ”ėŧ ė•ŒëĻŧ í™œė„ąí™”", "notifications": "ė•ŒëĻŧ", "notifications_setting_description": "ė•ŒëĻŧ 네렕 관ëĻŦ", - "oauth": "OAuth", "official_immich_resources": "Immich ęŗĩė‹ ëĻŦė†ŒėŠ¤", "offline": "ė˜¤í”„ëŧė¸", "offline_paths": "누ëŊ된 파ėŧ", @@ -1282,6 +1315,7 @@ "onboarding_welcome_user": "{user}님, í™˜ė˜í•Šë‹ˆë‹¤", "online": "똍ëŧė¸", "only_favorites": "ėĻę˛¨ė°žę¸°ë§Œ", + "open": "뗴揰", "open_in_map_view": "ė§€ë„ ëŗ´ę¸°ė—ė„œ 뗴揰", "open_in_openstreetmap": "OpenStreetMapė—ė„œ 뗴揰", "open_the_search_filters": "ę˛€ėƒ‰ 필터 뗴揰", @@ -1305,7 +1339,7 @@ "partner_page_partner_add_failed": "파트너ëĨŧ ėļ”ę°€í•˜ė§€ ëĒģ했ėŠĩ니다.", "partner_page_select_partner": "파트너 ė„ íƒ", "partner_page_shared_to_title": "ęŗĩ뜠 ëŒ€ėƒ", - "partner_page_stop_sharing_content": "더 ė´ėƒ {}ë‹˜ė´ ė‚Ŧ맄뗐 ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", + "partner_page_stop_sharing_content": "더 ė´ėƒ {partner}ë‹˜ė´ ė‚Ŧ맄뗐 ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", "partner_sharing": "íŒŒíŠ¸ë„ˆė™€ ęŗĩ뜠", "partners": "파트너", "password": "비밀번호", @@ -1325,7 +1359,7 @@ "pending": "ė§„í–‰ 뤑", "people": "ė¸ëŦŧ", "people_edits_count": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė„ ėˆ˜ė •í–ˆėŠĩ니다.", - "people_feature_description": "ė‚Ŧė§„ 및 ë™ė˜ėƒė„ ė¸ëŦŧ ęˇ¸ëŖšëŗ„ëĄœ íƒėƒ‰", + "people_feature_description": "ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ė¸ëŦŧ ęˇ¸ëŖšëŗ„ëĄœ íƒėƒ‰", "people_sidebar_description": "ė‚Ŧė´ë“œë°”ė— ė¸ëŦŧ 링íŦ í‘œė‹œ", "permanent_deletion_warning": "똁ęĩŦ ė‚­ė œ ę˛Ŋęŗ ", "permanent_deletion_warning_setting_description": "항ëĒŠė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ę¸° ė „ ę˛Ŋęŗ  ëŠ”ė‹œė§€ í‘œė‹œ", @@ -1351,27 +1385,33 @@ "photos_count": "ė‚Ŧė§„ {count, plural, one {{count, number}氜} other {{count, number}氜}}", "photos_from_previous_years": "ė§€ë‚œ ëLJ ë…„ę°„ė˜ ė‚Ŧė§„", "pick_a_location": "ėœ„ėš˜ ė„ íƒ", + "pin_code_changed_successfully": "PIN ėŊ”드ëĨŧ ëŗ€ę˛Ŋ했ėŠĩ니다.", + "pin_code_reset_successfully": "PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í–ˆėŠĩ니다.", + "pin_code_setup_successfully": "PIN ėŊ”드ëĨŧ ė„¤ė •í–ˆėŠĩ니다.", + "pin_verification": "PIN ėŊ”드 ė¸ėĻ", "place": "ėžĨė†Œ", "places": "ėžĨė†Œ", "places_count": "{count, plural, one {{count, number} ėžĨė†Œ} other {{count, number} ėžĨė†Œ}}", "play": "ėžŦėƒ", "play_memories": "ėļ”ė–ĩ ėžŦėƒ", "play_motion_photo": "ëĒ¨ė…˜ íŦ토 ėžŦėƒ", - "play_or_pause_video": "ë™ė˜ėƒ ėžŦėƒ, ėŧė‹œ ė •ė§€", + "play_or_pause_video": "ë™ė˜ėƒ ėžŦėƒ/ėŧė‹œ ė •ė§€", + "please_auth_to_access": "ęŗ„ė† ė§„í–‰í•˜ë ¤ëŠ´ ė¸ėĻí•˜ė„¸ėš”.", "port": "íŦ트", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "네렕", + "preferences_settings_subtitle": "ė•ą 네렕 관ëĻŦ", + "preferences_settings_title": "ę°œė¸ 네렕", "preset": "ė‚Ŧė „ 네렕", "preview": "미ëĻŦ ëŗ´ę¸°", "previous": "ė´ė „", "previous_memory": "ė´ė „ ėļ”ė–ĩ", - "previous_or_next_photo": "ė´ė „ 또는 ë‹¤ėŒ ė´ë¯¸ė§€ëĄœ", + "previous_or_next_photo": "ė´ė „/ë‹¤ėŒ ė‚Ŧė§„ėœŧ로", "primary": "ėŖŧėš”", "privacy": "ę°œė¸ ė •ëŗ´", + "profile": "프로필", "profile_drawer_app_logs": "로그", "profile_drawer_client_out_of_date_major": "ëĒ¨ë°”ėŧ ė•ąė´ ėĩœė‹  ë˛„ė „ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_drawer_client_out_of_date_minor": "ëĒ¨ë°”ėŧ ė•ąė´ ėĩœė‹  ë˛„ė „ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", - "profile_drawer_client_server_up_to_date": "클ëŧė´ė–¸íŠ¸ė™€ ė„œë˛„ę°€ ėĩœė‹ ėž…니다.", + "profile_drawer_client_server_up_to_date": "클ëŧė´ė–¸íŠ¸ė™€ ė„œë˛„ę°€ ėĩœė‹  ėƒíƒœėž…ë‹ˆë‹¤.", "profile_drawer_github": "Github", "profile_drawer_server_out_of_date_major": "ė„œë˛„ ë˛„ė „ė´ ėĩœė‹ ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_drawer_server_out_of_date_minor": "ė„œë˛„ ë˛„ė „ė´ ėĩœė‹ ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", @@ -1381,7 +1421,7 @@ "public_share": "ëĒ¨ë“  ė‚ŦėšŠėžė™€ ęŗĩ뜠", "purchase_account_info": "ė„œíŦ터", "purchase_activated_subtitle": "Immich뙀 ė˜¤í”ˆ ė†ŒėŠ¤ ė†Œí”„íŠ¸ė›¨ė–´ëĨŧ ė§€ė›í•´ėŖŧė…”ė„œ 감ė‚Ŧ합니다.", - "purchase_activated_time": "{date, date} 등록됨", + "purchase_activated_time": "{date} 등록됨", "purchase_activated_title": "ė œí’ˆ 키가 ė„ąęŗĩ렁ėœŧ로 ë“ąëĄë˜ė—ˆėŠĩ니다.", "purchase_button_activate": "등록", "purchase_button_buy": "ęĩŦ매", @@ -1426,6 +1466,8 @@ "recent_searches": "ėĩœęˇŧ ę˛€ėƒ‰", "recently_added": "ėĩœęˇŧ ėļ”ę°€", "recently_added_page_title": "ėĩœęˇŧ ėļ”ę°€", + "recently_taken": "ėĩœęˇŧ 항ëĒŠ", + "recently_taken_page_title": "ėĩœęˇŧ ė´Ŧė˜ë¨", "refresh": "ėƒˆëĄœęŗ ėš¨", "refresh_encoded_videos": "ë™ė˜ėƒ ėžŦė¸ėŊ”딊", "refresh_faces": "ė–ŧęĩ´ ėƒˆëĄœęŗ ėš¨", @@ -1445,17 +1487,18 @@ "remove_deleted_assets": "누ëŊ된 파ėŧ ė œęą°", "remove_from_album": "ė•¨ë˛”ė—ė„œ ė œęą°", "remove_from_favorites": "ėĻę˛¨ė°žę¸°ė—ė„œ ė œęą°", + "remove_from_locked_folder": "ėž ę¸´ í´ë”ė—ė„œ ë‚´ëŗ´ë‚´ę¸°", "remove_from_shared_link": "ęŗĩ뜠 링íŦė—ė„œ ė œęą°", "remove_memory": "ėļ”ė–ĩ ė œęą°", - "remove_photo_from_memory": "ė´ ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ ė œęą°", + "remove_photo_from_memory": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ ė œęą°", "remove_url": "URL ė œęą°", "remove_user": "ė‚ŦėšŠėž ė‚­ė œ", "removed_api_key": "API 키 ė‚­ė œ: {name}", "removed_from_archive": "ëŗ´ę´€í•¨ė—ė„œ ė œęą°ë˜ė—ˆėŠĩ니다.", "removed_from_favorites": "ėĻę˛¨ė°žę¸°ė—ė„œ ė œęą°ë˜ė—ˆėŠĩ니다.", "removed_from_favorites_count": "ėĻę˛¨ė°žę¸°ė—ė„œ 항ëĒŠ {count, plural, other {#氜}} ė œęą°ë¨", - "removed_memory": "ėļ”ė–ĩ ė œęą°", - "removed_photo_from_memory": "ė´ ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ ė œęą°", + "removed_memory": "ėļ”ė–ĩė´ ė œęą°ë˜ė—ˆėŠĩ니다.", + "removed_photo_from_memory": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ė„ ė œęą°í–ˆėŠĩ니다.", "removed_tagged_assets": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė—ė„œ 태그ëĨŧ ė œęą°í•¨", "rename": "ė´ëĻ„ 바꾸기", "repair": "눘ëĻŦ", @@ -1468,16 +1511,17 @@ "reset": "ė´ˆę¸°í™”", "reset_password": "비밀번호 ėžŦ네렕", "reset_people_visibility": "ė¸ëŦŧ í‘œė‹œ ė—Ŧëļ€ ė´ˆę¸°í™”", + "reset_pin_code": "PIN ėŊ”드 ė´ˆę¸°í™”", "reset_to_default": "ę¸°ëŗ¸ę°’ėœŧ로 ëŗĩ뛐", - "resolve_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ í™•ė¸", - "resolved_all_duplicates": "ëš„ėŠˇí•œ 항ëĒŠė„ ëĒ¨ë‘ í™•ė¸í–ˆėŠĩ니다.", + "resolve_duplicates": "뤑ëŗĩ된 항ëĒŠ í™•ė¸", + "resolved_all_duplicates": "뤑ëŗĩ된 항ëĒŠė„ ëĒ¨ë‘ 래ëĻŦ했ėŠĩ니다.", "restore": "ëŗĩ뛐", "restore_all": "ëĒ¨ë‘ ëŗĩ뛐", "restore_user": "ė‚ŦėšŠėž ëŗĩ뛐", "restored_asset": "항ëĒŠė´ ëŗĩė›ë˜ė—ˆėŠĩ니다.", "resume": "ėžŦ氜", "retry_upload": "ë‹¤ė‹œ ė‹œë„", - "review_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ í™•ė¸", + "review_duplicates": "뤑ëŗĩ된 항ëĒŠ í™•ė¸", "role": "ė—­í• ", "role_editor": "íŽ¸ė§‘ėž", "role_viewer": "ëˇ°ė–´", @@ -1496,7 +1540,7 @@ "search_albums": "ė•¨ë˛” ę˛€ėƒ‰", "search_by_context": "ë‚´ėšŠ ę˛€ėƒ‰", "search_by_description": "네ëĒ…ėœŧ로 ę˛€ėƒ‰", - "search_by_description_example": "ė‚ŦíŒŒė—ė„œ ėĻę¸°ëŠ” í•˜ė´í‚š", + "search_by_description_example": "ë™í•´ė•ˆė—ė„œ ë§žė´í•˜ëŠ” ėƒˆí•´ ėŧėļœ", "search_by_filename": "파ėŧëĒ… 또는 확ėžĨėžëĄœ ę˛€ėƒ‰", "search_by_filename_example": "ė˜ˆė‹œ: IMG_1234.JPG or PNG", "search_camera_make": "ėš´ëŠ”ëŧ ė œėĄ°ė‚Ŧ ę˛€ėƒ‰...", @@ -1510,7 +1554,7 @@ "search_filter_date_title": "ë‚ ė§œ ë˛”ėœ„ ė„ íƒ", "search_filter_display_option_not_in_album": "ė•¨ë˛”ė— ė—†ėŒ", "search_filter_display_options": "í‘œė‹œ ė˜ĩė…˜", - "search_filter_filename": "Search by file name", + "search_filter_filename": "파ėŧëĒ…ėœŧ로 ę˛€ėƒ‰", "search_filter_location": "ėœ„ėš˜", "search_filter_location_title": "ėœ„ėš˜ ė„ íƒ", "search_filter_media_type": "ë¯¸ë””ė–´ ėĸ…ëĨ˜", @@ -1518,17 +1562,17 @@ "search_filter_people_title": "ė¸ëŦŧ ė„ íƒ", "search_for": "ę˛€ėƒ‰", "search_for_existing_person": "ėĄ´ėžŦ하는 ė¸ëŦŧ ę˛€ėƒ‰", - "search_no_more_result": "No more results", + "search_no_more_result": "ë”ė´ėƒ 결ęŗŧ ė—†ėŒ", "search_no_people": "ė¸ëŦŧė´ ė—†ėŠĩ니다.", "search_no_people_named": "\"{name}\" ė¸ëŦŧė„ ė°žė„ 눘 ė—†ėŒ", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "ę˛€ėƒ‰ 결ęŗŧ가 ė—†ėŠĩ니다. 다ëĨ¸ ę˛€ėƒ‰ė–´ë‚˜ ėĄ°í•Šėœŧ로 ë‹¤ė‹œ ė‹œë„í•´ ëŗ´ė„¸ėš”.", "search_options": "ę˛€ėƒ‰ ė˜ĩė…˜", "search_page_categories": "ëļ„ëĨ˜", "search_page_motion_photos": "ëĒ¨ė…˜ íŦ토", "search_page_no_objects": "ė‚ŦėšŠ 가ëŠĨ한 ė‚ŦëŦŧ ė •ëŗ´ ė—†ėŒ", "search_page_no_places": "ė‚ŦėšŠ 가ëŠĨ한 ėœ„ėš˜ ė •ëŗ´ ė—†ėŒ", "search_page_screenshots": "늤íŦëϰ냎", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "ė‚Ŧė§„ 및 ë™ė˜ėƒ ę˛€ėƒ‰", "search_page_selfies": "ė…€í”ŧ", "search_page_things": "ė‚ŦëŦŧ", "search_page_view_all_button": "ëĒ¨ë‘ ëŗ´ę¸°", @@ -1540,8 +1584,7 @@ "search_result_page_new_search_hint": "냈 ę˛€ėƒ‰", "search_settings": "네렕 ę˛€ėƒ‰", "search_state": "맀뗭 ę˛€ėƒ‰...", - "search_suggestion_list_smart_search_hint_1": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ ę¸°ëŗ¸ė ėœŧ로 í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ëŠ”íƒ€ë°ė´í„°ëĄœ ę˛€ėƒ‰í•˜ë ¤ëŠ´ ë‹¤ėŒ ęĩŦëŦ¸ė„ ė‚ŦėšŠí•˜ė„¸ėš”.", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", + "search_suggestion_list_smart_search_hint_1": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ ę¸°ëŗ¸ė ėœŧ로 í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ëŠ”íƒ€ë°ė´í„°ëĄœ ę˛€ėƒ‰í•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‚ŦėšŠí•˜ė„¸ėš”. ", "search_tags": "태그로 ę˛€ėƒ‰...", "search_timezone": "ė‹œę°„ëŒ€ ę˛€ėƒ‰...", "search_type": "ę˛€ėƒ‰ ėĸ…ëĨ˜", @@ -1560,14 +1603,15 @@ "select_keep_all": "ëĒ¨ë‘ ėœ ė§€", "select_library_owner": "ëŧė´ë¸ŒëŸŦëĻŦ ė†Œėœ ėž ė„ íƒ", "select_new_face": "냈 ė–ŧęĩ´ ė„ íƒ", + "select_person_to_tag": "태그할 ė¸ëŦŧė„ ė„ íƒí•˜ė„¸ėš”.", "select_photos": "ė‚Ŧė§„ ė„ íƒ", "select_trash_all": "ëĒ¨ë‘ ė‚­ė œ", "select_user_for_sharing_page_err_album": "ė•¨ë˛”ė„ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "selected": "ė„ íƒë¨", - "selected_count": "{count, plural, other {#氜}} 항ëĒŠ ė„ íƒë¨", + "selected_count": "{count, plural, other {#氜}} ė„ íƒë¨", "send_message": "ëŠ”ė‹œė§€ ė „ė†Ą", "send_welcome_email": "í™˜ė˜ ė´ëŠ”ėŧ ė „ė†Ą", - "server_endpoint": "Server Endpoint", + "server_endpoint": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸", "server_info_box_app_version": "ė•ą ë˛„ė „", "server_info_box_server_url": "ė„œë˛„ URL", "server_offline": "ė˜¤í”„ëŧė¸", @@ -1576,7 +1620,7 @@ "server_version": "ė„œë˛„ ë˛„ė „", "set": "네렕", "set_as_album_cover": "ė•¨ë˛” ėģ¤ë˛„ëĄœ 네렕", - "set_as_featured_photo": "ėļ”ė˛œ ė‚Ŧė§„ėœŧ로 네렕", + "set_as_featured_photo": "대표 ė‚Ŧė§„ėœŧ로 네렕", "set_as_profile_picture": "프로필 ė‚Ŧė§„ėœŧ로 네렕", "set_date_of_birth": "ėƒë…„ė›”ėŧ 네렕", "set_profile_picture": "프로필 ė‚Ŧė§„ėœŧ로 네렕", @@ -1588,29 +1632,31 @@ "setting_image_viewer_preview_title": "미ëĻŦ ëŗ´ę¸° ė´ë¯¸ė§€ ëļˆëŸŦ똤揰", "setting_image_viewer_title": "ė´ë¯¸ė§€", "setting_languages_apply": "ė ėšŠ", - "setting_languages_subtitle": "Change the app's language", + "setting_languages_subtitle": "ė•ą 떏떴 ëŗ€ę˛Ŋ", "setting_languages_title": "떏떴", - "setting_notifications_notify_failures_grace_period": "밹꡸ëŧėš´ë“œ ë°ąė—… ė‹¤íŒ¨ ė•ŒëĻŧ: {}", - "setting_notifications_notify_hours": "{}ė‹œę°„ 후", + "setting_notifications_notify_failures_grace_period": "밹꡸ëŧėš´ë“œ ë°ąė—… ė‹¤íŒ¨ ė•ŒëĻŧ: {duration}", + "setting_notifications_notify_hours": "{count}ė‹œę°„", "setting_notifications_notify_immediately": "ėĻ‰ė‹œ", - "setting_notifications_notify_minutes": "{}ëļ„ í›„", + "setting_notifications_notify_minutes": "{count}ëļ„", "setting_notifications_notify_never": "ė•ŒëĻŦė§€ ė•ŠėŒ", - "setting_notifications_notify_seconds": "{}봈", + "setting_notifications_notify_seconds": "{count}봈", "setting_notifications_single_progress_subtitle": "ę°œëŗ„ 항ëĒŠė˜ ėƒė„¸ ė—…ëĄœë“œ ė •ëŗ´ í‘œė‹œ", "setting_notifications_single_progress_title": "밹꡸ëŧėš´ë“œ ë°ąė—… ėƒė„¸ ė§„í–‰ëĨ  í‘œė‹œ", "setting_notifications_subtitle": "ė•ŒëĻŧ ę¸°ëŗ¸ 네렕 ėĄ°ė •", "setting_notifications_total_progress_subtitle": "렄랴 ė—…ëĄœë“œ ė§„í–‰ëĨ  (ė™„ëŖŒ/렄랴)", "setting_notifications_total_progress_title": "밹꡸ëŧėš´ë“œ ë°ąė—… 렄랴 ė§„í–‰ëĨ  í‘œė‹œ", "setting_video_viewer_looping_title": "반ëŗĩ", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_subtitle": "ė„œë˛„ė—ė„œ ë™ė˜ėƒė„ ėŠ¤íŠ¸ëĻŦ밍할 때, íŠ¸ëžœėŠ¤ėŊ”딊된 ë˛„ė „ė´ ėžˆë”ëŧ도 ė›ëŗ¸ė„ ėžŦėƒí•Šë‹ˆë‹¤. ė´ëĄœ ė¸í•´ 버íŧë§ė´ ë°œėƒí•  눘 ėžˆėŠĩ니다. 揰揰뗐 ėžˆëŠ” ë™ė˜ėƒė€ ė´ 네렕ęŗŧ ę´€ęŗ„ė—†ė´ í•­ėƒ ė›ëŗ¸ í™”ė§ˆëĄœ ėžŦėƒëŠë‹ˆë‹¤.", + "setting_video_viewer_original_video_title": "ė›ëŗ¸ ë™ė˜ėƒ ę°•ė œ ė‚ŦėšŠ", "settings": "네렕", "settings_require_restart": "ė„¤ė •ė„ ė ėšŠí•˜ë ¤ëŠ´ ImmichëĨŧ ë‹¤ė‹œ ė‹œėž‘í•˜ė„¸ėš”.", "settings_saved": "ė„¤ė •ė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", + "setup_pin_code": "PIN ėŊ”드 네렕", "share": "ęŗĩ뜠", "share_add_photos": "ė‚Ŧė§„ ėļ”ę°€", - "share_assets_selected": "{}氜 항ëĒŠ ė„ íƒë¨", + "share_assets_selected": "{count}氜 ė„ íƒë¨", "share_dialog_preparing": "ė¤€ëš„ 뤑...", + "share_link": "ęŗĩ뜠 링íŦ", "shared": "ęŗĩėœ ë¨", "shared_album_activities_input_disable": "ëŒ“ę¸€ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다", "shared_album_activity_remove_content": "ė´ ë°˜ė‘ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", @@ -1623,34 +1669,33 @@ "shared_by_user": "{user}ë‹˜ė´ ęŗĩėœ í•¨", "shared_by_you": "내가 ęŗĩėœ í•¨", "shared_from_partner": "{partner}ë‹˜ė˜ ė‚Ŧė§„", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "렄랴 {total}氜 뤑 {current}氜 ė—…ëĄœë“œë¨", "shared_link_app_bar_title": "ęŗĩ뜠 링íŦ", "shared_link_clipboard_copied_massage": "클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧë˜ė—ˆėŠĩ니다.", - "shared_link_clipboard_text": "링íŦ: {}\n비밀번호: {}", + "shared_link_clipboard_text": "링íŦ: {link}\n비밀번호: {password}", "shared_link_create_error": "ęŗĩ뜠 링íŦ ėƒė„ą 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", "shared_link_edit_description_hint": "ęŗĩ뜠 링íŦ 네ëĒ… ėž…ë Ĩ", "shared_link_edit_expire_after_option_day": "1ėŧ", - "shared_link_edit_expire_after_option_days": "{}ėŧ", + "shared_link_edit_expire_after_option_days": "{count}ėŧ", "shared_link_edit_expire_after_option_hour": "1ė‹œę°„", - "shared_link_edit_expire_after_option_hours": "{}ė‹œę°„", + "shared_link_edit_expire_after_option_hours": "{count}ė‹œę°„", "shared_link_edit_expire_after_option_minute": "1ëļ„", - "shared_link_edit_expire_after_option_minutes": "{}ëļ„", - "shared_link_edit_expire_after_option_months": "{}ę°œė›”", - "shared_link_edit_expire_after_option_year": "{}년", + "shared_link_edit_expire_after_option_minutes": "{count}ëļ„", + "shared_link_edit_expire_after_option_months": "{count}ę°œė›”", + "shared_link_edit_expire_after_option_year": "{count}년", "shared_link_edit_password_hint": "ęŗĩ뜠 비밀번호 ėž…ë Ĩ", "shared_link_edit_submit_button": "링íŦ íŽ¸ė§‘", "shared_link_error_server_url_fetch": "ė„œë˛„ URLė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다.", - "shared_link_expires_day": "{}ėŧ 후 ë§ŒëŖŒ", - "shared_link_expires_days": "{}ėŧ 후 ë§ŒëŖŒ", - "shared_link_expires_hour": "{}ė‹œę°„ 후 ë§ŒëŖŒ", - "shared_link_expires_hours": "{}ė‹œę°„ 후 ë§ŒëŖŒ", - "shared_link_expires_minute": "{}ëļ„ í›„ ë§ŒëŖŒ", - "shared_link_expires_minutes": "{}ëļ„ í›„ ë§ŒëŖŒ", + "shared_link_expires_day": "{count}ėŧ 후 ë§ŒëŖŒ", + "shared_link_expires_days": "{count}ėŧ 후 ë§ŒëŖŒ", + "shared_link_expires_hour": "{count}ė‹œę°„ 후 ë§ŒëŖŒ", + "shared_link_expires_hours": "{count}ė‹œę°„ 후 ë§ŒëŖŒ", + "shared_link_expires_minute": "{count}ëļ„ í›„ ë§ŒëŖŒ", + "shared_link_expires_minutes": "{count}ëļ„ í›„ ë§ŒëŖŒ", "shared_link_expires_never": "ë§ŒëŖŒë˜ė§€ ė•ŠėŒ", - "shared_link_expires_second": "{}봈 후 ë§ŒëŖŒ", - "shared_link_expires_seconds": "{}봈 후 ë§ŒëŖŒ", + "shared_link_expires_second": "{count}봈 후 ë§ŒëŖŒ", + "shared_link_expires_seconds": "{count}봈 후 ë§ŒëŖŒ", "shared_link_individual_shared": "ę°œė¸ ęŗĩ뜠", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ęŗĩ뜠 링íŦ 관ëĻŦ", "shared_link_options": "ęŗĩ뜠 링íŦ ė˜ĩė…˜", "shared_links": "ęŗĩ뜠 링íŦ", @@ -1661,7 +1706,7 @@ "sharing": "ęŗĩ뜠", "sharing_enter_password": "ė´ íŽ˜ė´ė§€ëĨŧ ëŗ´ë ¤ëŠ´ 비밀번호ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "sharing_page_album": "ęŗĩ뜠 ė•¨ë˛”", - "sharing_page_description": "ęŗĩ뜠 ė•¨ë˛”ė„ ë§Œë“¤ė–´ ėŖŧëŗ€ ė‚Ŧ람들ęŗŧ ė‚Ŧė§„ 및 ë™ė˜ėƒė„ ęŗĩėœ í•˜ė„¸ėš”.", + "sharing_page_description": "ęŗĩ뜠 ė•¨ë˛”ė„ ë§Œë“¤ė–´ ėŖŧëŗ€ ė‚ŦëžŒë“¤ė—ę˛Œ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ęŗĩėœ í•˜ė„¸ėš”.", "sharing_page_empty_list": "ęŗĩ뜠 ė•¨ë˛” ė—†ėŒ", "sharing_sidebar_description": "ė‚Ŧė´ë“œë°”ė— ęŗĩ뜠 링íŦ í‘œė‹œ", "sharing_silver_appbar_create_shared_album": "ęŗĩ뜠 ė•¨ë˛” ėƒė„ą", @@ -1703,12 +1748,12 @@ "sort_items": "항ëĒŠ 눘", "sort_modified": "ėˆ˜ė •ëœ ë‚ ė§œ", "sort_oldest": "ė˜¤ëž˜ëœ ė‚Ŧė§„", - "sort_people_by_similarity": "뜠ė‚Ŧė„ąė„ 揰뤀ėœŧ로 ė‚Ŧ람 ė •ë Ŧ", + "sort_people_by_similarity": "뜠ė‚Ŧė„ąė„ 揰뤀ėœŧ로 ė¸ëŦŧ ė •ë Ŧ", "sort_recent": "ėĩœęˇŧ ė‚Ŧė§„", "sort_title": "렜ëĒŠ", "source": "ė†ŒėŠ¤", "stack": "ėŠ¤íƒ", - "stack_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ ėŠ¤íƒ", + "stack_duplicates": "뤑ëŗĩ된 항ëĒŠ ėŠ¤íƒ", "stack_select_one_photo": "ėŠ¤íƒė˜ 대표 ė‚Ŧė§„ ė„ íƒ", "stack_selected_photos": "ė„ íƒí•œ ė´ë¯¸ė§€ ėŠ¤íƒ", "stacked_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}} ėŠ¤íƒë¨", @@ -1723,13 +1768,14 @@ "stop_sharing_photos_with_user": "ė´ ė‚ŦėšŠėžė™€ ė‚Ŧė§„ ęŗĩ뜠 ė¤‘ë‹¨", "storage": "ė €ėžĨ ęŗĩ간", "storage_label": "ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”", + "storage_quota": "ėŠ¤í† ëĻŦė§€ 할당량", "storage_usage": "{available} 뤑 {used} ė‚ŦėšŠ", "submit": "í™•ė¸", "suggestions": "ėļ”ė˛œ", - "sunrise_on_the_beach": "ë™í•´ė•ˆė—ė„œ ë§žė´í•˜ëŠ” ėƒˆí•´ ėŧėļœ", + "sunrise_on_the_beach": "ė´ë¯¸ė§€ė— ėĄ´ėžŦ하는 ė‚ŦëŦŧ ę˛€ėƒ‰", "support": "맀뛐", "support_and_feedback": "맀뛐 & ė œė•ˆ", - "support_third_party_description": "Immich가 ė„œë“œíŒŒí‹° íŒ¨í‚¤ė§€ëĄœ ė„¤ėš˜ ë˜ė—ˆėŠĩ니다. 링íŦëĨŧ 눌ëŸŦ ë¨ŧė € íŒ¨í‚¤ė§€ ëŦ¸ė œė¸ė§€ í™•ė¸í•´ ëŗ´ė„¸ėš”.", + "support_third_party_description": "ė„œë“œíŒŒí‹° íŒ¨í‚¤ė§€ëĨŧ ė´ėšŠí•˜ė—Ŧ Immich가 ė„¤ėš˜ëœ 것ėœŧ로 ëŗ´ėž…ë‹ˆë‹¤. 현ėžŦ ë°œėƒí•˜ëŠ” ëŦ¸ė œëŠ” 해당 íŒ¨í‚¤ė§€ę°€ ė›ė¸ėŧ 눘 ėžˆėœŧë¯€ëĄœ, ë¨ŧė € ė•„ëž˜ 링íŦëĨŧ í†ĩ해 íŒ¨í‚¤ė§€ ę°œë°œėžė—ę˛Œ ëŦ¸ė˜í•´ėŖŧė„¸ėš”.", "swap_merge_direction": "ëŗ‘í•Š ë°Ší–Ĩ ëŗ€ę˛Ŋ", "sync": "동기화", "sync_albums": "ė•¨ë˛” 동기화", @@ -1738,9 +1784,9 @@ "tag": "태그", "tag_assets": "항ëĒŠ 태그", "tag_created": "태그 ėƒė„ąë¨: {tag}", - "tag_feature_description": "ė‚Ŧė§„ 및 ë™ė˜ėƒė„ ėŖŧė œëŗ„ ęˇ¸ëŖší™”ëœ 태그로 íƒėƒ‰", + "tag_feature_description": "태그 ėŖŧė œëŗ„ëĄœ ęˇ¸ëŖší™”ëœ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒ íƒėƒ‰", "tag_not_found_question": "태그ëĨŧ ė°žė„ 눘 ė—†ë‚˜ėš”? 냈 태그ëĨŧ ėƒė„ąí•˜ė„¸ėš”.", - "tag_people": "ė‚Ŧ람 태그", + "tag_people": "ė¸ëŦŧ 태그", "tag_updated": "태그 ė—…ë°ė´íŠ¸ë¨: {tag}", "tagged_assets": "항ëĒŠ {count, plural, one {#氜} other {#氜}}뗐 태그ëĨŧ ė ėšŠí•¨", "tags": "태그", @@ -1748,18 +1794,18 @@ "theme": "테마", "theme_selection": "테마 네렕", "theme_selection_description": "브ëŧėš°ė € 및 ė‹œėŠ¤í…œ ę¸°ëŗ¸ 네렕뗐 따ëŧ ëŧė´íŠ¸ ëĒ¨ë“œė™€ 다íŦ ëĒ¨ë“œëĨŧ ėžë™ėœŧ로 네렕", - "theme_setting_asset_list_storage_indicator_title": "항ëĒŠė— ėŠ¤í† ëĻŦė§€ 동기화 ė—Ŧëļ€ í‘œė‹œ", - "theme_setting_asset_list_tiles_per_row_title": "한 뤄뗐 í‘œė‹œí•  항ëĒŠ 눘 ({})", + "theme_setting_asset_list_storage_indicator_title": "타ėŧ뗐 ė„œë˛„ 동기화 ėƒíƒœ í‘œė‹œ", + "theme_setting_asset_list_tiles_per_row_title": "한 뤄뗐 í‘œė‹œí•  항ëĒŠ 눘 ({count})", "theme_setting_colorful_interface_subtitle": "ë°°ę˛Ŋ뗐 대표 ėƒ‰ėƒė„ ė ėšŠí•Šë‹ˆë‹¤.", "theme_setting_colorful_interface_title": "미려한 ė¸í„°íŽ˜ė´ėŠ¤", "theme_setting_image_viewer_quality_subtitle": "ėƒė„¸ ëŗ´ę¸° ė´ë¯¸ė§€ í’ˆė§ˆ ėĄ°ė •", "theme_setting_image_viewer_quality_title": "ė´ë¯¸ė§€ ëŗ´ę¸° í’ˆė§ˆ", - "theme_setting_primary_color_subtitle": "ėŖŧ 기ëŠĨ 및 ę°•ėĄ°ė— ė‚ŦėšŠë˜ëŠ” ėƒ‰ėƒ ė„ íƒ", + "theme_setting_primary_color_subtitle": "ėŖŧėš” 기ëŠĨęŗŧ ę°•ėĄ° ėƒ‰ėƒė— ė ėšŠí•  테마 ėƒ‰ėƒė„ ė„ íƒí•˜ė„¸ėš”.", "theme_setting_primary_color_title": "대표 ėƒ‰ėƒ", "theme_setting_system_primary_color_title": "ė‹œėŠ¤í…œ ėƒ‰ėƒ ė‚ŦėšŠ", "theme_setting_system_theme_switch": "ėžë™ (ė‹œėŠ¤í…œ 네렕)", "theme_setting_theme_subtitle": "ė•ą 테마 ė„ íƒ", - "theme_setting_three_stage_loading_subtitle": "ė´ 기ëŠĨė€ ė•ąė˜ 로드 ė„ąëŠĨė„ í–Ĩėƒė‹œí‚Ŧ 눘 ėžˆė§€ë§Œ 더 ë§Žė€ ë°ė´í„°ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤.", + "theme_setting_three_stage_loading_subtitle": "3ë‹¨ęŗ„ ëĄœë”Šė€ 로드 ė„ąëŠĨė„ í–Ĩėƒė‹œí‚Ŧ 눘 ėžˆėœŧ나, ë„¤íŠ¸ė›ŒíŦ ëļ€í•˜ę°€ íŦ枌 ėĻę°€í•  눘 ėžˆėŠĩ니다.", "theme_setting_three_stage_loading_title": "3ë‹¨ęŗ„ 로드 í™œė„ąí™”", "they_will_be_merged_together": "ė„ íƒí•œ ė¸ëŦŧë“¤ė´ ëŗ‘í•ŠëŠë‹ˆë‹¤.", "third_party_resources": "ė„œë“œ 파티 ëĻŦė†ŒėŠ¤", @@ -1774,7 +1820,7 @@ "to_trash": "ė‚­ė œ", "toggle_settings": "네렕 ëŗ€ę˛Ŋ", "toggle_theme": "다íŦ ëĒ¨ë“œ ė‚ŦėšŠ", - "total": "í•Šęŗ„", + "total": "렄랴", "total_usage": "ė´ ė‚ŦėšŠëŸ‰", "trash": "íœ´ė§€í†ĩ", "trash_all": "ëĒ¨ë‘ ė‚­ė œ", @@ -1784,13 +1830,15 @@ "trash_no_results_message": "ė‚­ė œëœ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė´ ė—Ŧ揰뗐 í‘œė‹œëŠë‹ˆë‹¤.", "trash_page_delete_all": "ëĒ¨ë‘ ė‚­ė œ", "trash_page_empty_trash_dialog_content": "íœ´ė§€í†ĩė„ ëš„ėš°ė‹œę˛ ėŠĩ니까? íœ´ė§€í†ĩ뗐 ėžˆëŠ” ëĒ¨ë“  항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė œęą°ëŠë‹ˆë‹¤.", - "trash_page_info": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė€ {}ėŧ 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "trash_page_info": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė€ {days}ėŧ 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "trash_page_no_assets": "íœ´ė§€í†ĩė´ ëš„ė–´ ėžˆėŒ", "trash_page_restore_all": "ëĒ¨ë‘ ëŗĩ뛐", "trash_page_select_assets_btn": "항ëĒŠ ė„ íƒ", - "trash_page_title": "íœ´ė§€í†ĩ ({})", + "trash_page_title": "íœ´ė§€í†ĩ ({count})", "trashed_items_will_be_permanently_deleted_after": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė€ {days, plural, one {#ėŧ} other {#ėŧ}} 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "type": "í˜•ė‹", + "unable_to_change_pin_code": "PIN ėŊ”드ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŒ", + "unable_to_setup_pin_code": "PIN ėŊ”드ëĨŧ ė„¤ė •í•  눘 ė—†ėŒ", "unarchive": "ëŗ´ę´€í•¨ė—ė„œ ė œęą°", "unarchived_count": "ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠ {count, plural, other {#氜}} ė œęą°ë¨", "unfavorite": "ėĻę˛¨ė°žę¸° í•´ė œ", @@ -1802,7 +1850,7 @@ "unlink_motion_video": "ëĒ¨ė…˜ ëš„ë””ė˜¤ 링íŦ í•´ė œ", "unlink_oauth": "OAuth 뗰枰 í•´ė œ", "unlinked_oauth_account": "OAuth ęŗ„ė • ė—°ę˛°ė´ í•´ė œë˜ė—ˆėŠĩ니다.", - "unmute_memories": "ėļ”ė–ĩ ėŒė†Œęą° í•´ė œ", + "unmute_memories": "ėŒė†Œęą° í•´ė œ", "unnamed_album": "ė´ëĻ„ ė—†ëŠ” ė•¨ë˛”", "unnamed_album_delete_confirmation": "ė„ í…í•œ ė•¨ë˛”ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "unnamed_share": "ė´ëĻ„ ė—†ëŠ” ęŗĩ뜠", @@ -1826,15 +1874,17 @@ "upload_status_errors": "똤ëĨ˜", "upload_status_uploaded": "ė™„ëŖŒ", "upload_success": "ė—…ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다. ė—…ëĄœë“œëœ 항ëĒŠė„ ëŗ´ë ¤ëŠ´ íŽ˜ė´ė§€ëĨŧ ėƒˆëĄœęŗ ėš¨í•˜ė„¸ėš”.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "Immich뗐 ė—…ëĄœë“œ ({count})", + "uploading": "ė—…ëĄœë“œ 뤑", "usage": "ė‚ŦėšŠëŸ‰", - "use_current_connection": "use current connection", + "use_biometric": "ėƒė˛´ ė¸ėĻ ė‚ŦėšŠ", + "use_current_connection": "현ėžŦ ë„¤íŠ¸ė›ŒíŦ ė‚ŦėšŠ", "use_custom_date_range": "ëŒ€ė‹  맞ėļ¤ ę¸°ę°„ ė‚ŦėšŠ", "user": "ė‚ŦėšŠėž", "user_id": "ė‚ŦėšŠėž ID", "user_liked": "{user}ë‹˜ė´ {type, select, photo {ė´ ė‚Ŧė§„ė„} video {ė´ ë™ė˜ėƒė„} asset {ė´ 항ëĒŠė„} other {ė´ 항ëĒŠė„}} ėĸ‹ė•„핊니다.", + "user_pin_code_settings": "PIN ėŊ”드", + "user_pin_code_settings_description": "PIN ėŊ”드 관ëĻŦ", "user_purchase_settings": "ęĩŦ매", "user_purchase_settings_description": "ęĩŦ매 및 ė œí’ˆ 키 관ëĻŦ", "user_role_set": "{user}ë‹˜ė—ę˛Œ {role} ė—­í• ė„ ė„¤ė •í–ˆėŠĩ니다.", @@ -1845,15 +1895,15 @@ "users": "ė‚ŦėšŠėž", "utilities": "도ęĩŦ", "validate": "검ėĻ", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "ėœ íš¨í•œ URLė„ ėž…ë Ĩí•˜ė„¸ėš”.", "variables": "ëŗ€ėˆ˜", "version": "ë˛„ė „", "version_announcement_closing": "ë‹šė‹ ė˜ ėšœęĩŦ, Alex가", "version_announcement_message": "ė•ˆë…•í•˜ė„¸ėš”! 냈 ë˛„ė „ė˜ ImmichëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. ėž˜ëĒģ된 ęĩŦė„ąė„ ë°Šė§€í•˜ęŗ  ImmichëĨŧ ėĩœė‹  ėƒíƒœëĄœ ėœ ė§€í•˜ę¸° ėœ„í•´ ėž ė‹œ ė‹œę°„ė„ ë‚´ė–´ ëĻ´ëĻŦ늤 노트ëĨŧ ėŊė–´ëŗ´ëŠ” ę˛ƒė„ ęļŒėžĨ합니다. 특히 WatchTower ë“ąė˜ ėžë™ ė—…ë°ė´íŠ¸ 기ëŠĨė„ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ė˜ë„í•˜ė§€ ė•Šė€ ë™ėž‘ė„ ë°Šė§€í•˜ę¸° ėœ„í•´ ë”ë”ėšą ęļŒėžĨ됩니다.", "version_announcement_overlay_release_notes": "ëĻ´ëĻŦ늤 노트", "version_announcement_overlay_text_1": "ė•ˆë…•í•˜ė„¸ėš”,", - "version_announcement_overlay_text_2": "냈 ë˛„ė „ė˜ ImmichëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다.", - "version_announcement_overlay_text_3": "WatchTower ë“ąė˜ ėžë™ ė—…ë°ė´íŠ¸ 기ëŠĨė„ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ė˜ë„í•˜ė§€ ė•Šė€ ë™ėž‘ė„ ë°Šė§€í•˜ę¸° ėœ„í•´ docker-compose.yml 및 .env ęĩŦė„ąė´ ėĩœė‹ ė¸ė§€ í™•ė¸í•˜ė„¸ėš”.", + "version_announcement_overlay_text_2": "냈 ë˛„ė „ė˜ ImmichëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. ", + "version_announcement_overlay_text_3": " WatchTower ë“ąė˜ ėžë™ ė—…ë°ė´íŠ¸ 기ëŠĨė„ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ė˜ë„í•˜ė§€ ė•Šė€ ë™ėž‘ė„ ë°Šė§€í•˜ę¸° ėœ„í•´ docker-compose.yml 및 .env ęĩŦė„ąė´ ėĩœė‹ ė¸ė§€ í™•ė¸í•˜ė„¸ėš”.", "version_announcement_overlay_title": "냈 ė„œë˛„ ë˛„ė „ ė‚ŦėšŠ 가ëŠĨ 🎉", "version_history": "ë˛„ė „ 기록", "version_history_item": "{date} ë˛„ė „ {version} ė„¤ėš˜", @@ -1883,11 +1933,12 @@ "week": "ėŖŧ", "welcome": "í™˜ė˜í•Šë‹ˆë‹¤", "welcome_to_immich": "í™˜ė˜í•Šë‹ˆë‹¤", - "wifi_name": "WiFi Name", + "wifi_name": "W-Fi ė´ëĻ„", + "wrong_pin_code": "ėž˜ëĒģ된 PIN ėŊ”드", "year": "년", "years_ago": "{years, plural, one {#년} other {#년}} ė „", "yes": "네", "you_dont_have_any_shared_links": "ėƒė„ąí•œ ęŗĩ뜠 링íŦ가 ė—†ėŠĩ니다.", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„", "zoom_image": "ė´ë¯¸ė§€ 확대" } diff --git a/i18n/lt.json b/i18n/lt.json index 450152bc74..0e11e41676 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -4,7 +4,6 @@ "account_settings": "Paskyros nustatymai", "acknowledge": "Patvirtinti", "action": "Veiksmas", - "action_common_update": "Update", "actions": "Veiksmai", "active": "Vykdoma", "activity": "Veikla", @@ -14,7 +13,6 @@ "add_a_location": "Pridėti vietovę", "add_a_name": "Pridėti vardą", "add_a_title": "Pridėti pavadinimą", - "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Pridėti iÅĄimčiÅŗ ÅĄabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", @@ -24,8 +22,6 @@ "add_photos": "Pridėti nuotraukÅŗ", "add_to": "Pridėti įâ€Ļ", "add_to_album": "Pridėti į albumą", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", "add_to_shared_album": "Pridėti į bendrinamą albumą", "add_url": "Pridėti URL", "added_to_archive": "Pridėta į archyvą", @@ -38,12 +34,13 @@ "authentication_settings_disable_all": "Ar tikrai norite iÅĄjungti visus prisijungimo bÅĢdus? Prisijungimas bus visiÅĄkai iÅĄjungtas.", "authentication_settings_reenable": "Norėdami vėl įjungti, naudokite Serverio komandą.", "background_task_job": "Foninės uÅžduotys", - "backup_database": "DuomenÅŗ bazės atsarginė kopija", - "backup_database_enable_description": "ÄŽgalinti duomenÅŗ bazės atsarginė kopijas", - "backup_keep_last_amount": "IÅĄsaugomÅŗ ankstesniÅŗ atsarginiÅŗ duomenÅŗ bazės kopijÅŗ skaičius", - "backup_settings": "Atsarginės kopijos nustatymai", - "backup_settings_description": "Tvarkyti duomenÅŗ bazės atsarginės kopijos nustatymus", + "backup_database": "Sukurti duomenÅŗ bazės iÅĄklotinę", + "backup_database_enable_description": "ÄŽgalinti duomenÅŗ bazės iÅĄklotinės", + "backup_keep_last_amount": "IÅĄsaugomÅŗ ankstesniÅŗ duomenÅŗ bazės iÅĄklotiniÅŗ skaičius", + "backup_settings": "DuomenÅŗ bazės iÅĄklotiniÅŗ nustatymai", + "backup_settings_description": "Tvarkyti duomenÅŗ bazės iÅĄklotinės nustatymus. Pastaba: Å ie darbai nėra stebimi ir jums nebus praneÅĄta apie nesėkmę.", "check_all": "PaÅžymėti viską", + "cleanup": "Valymas", "cleared_jobs": "IÅĄvalyti darbai: {job}", "config_set_by_file": "KonfigÅĢracija nustatyta pagal konfigÅĢracinį failą", "confirm_delete_library": "Ar tikrai norite iÅĄtrinti {library} biblioteką?", @@ -51,6 +48,7 @@ "confirm_email_below": "Patvirtinimui įveskite \"{email}\" Åžemiau", "confirm_reprocess_all_faces": "Ar tikrai norite iÅĄ naujo apdoroti visus veidus? Tai taip pat iÅĄtrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iÅĄ naujo nustatyti {user} slaptaÅžodį?", + "confirm_user_pin_code_reset": "Ar tikrai norite iÅĄ naujo nustatyti {user} PIN kodą?", "create_job": "Sukurti darbą", "cron_expression": "Cron iÅĄraiÅĄka", "cron_expression_description": "Nustatyti skanavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos ÅžiÅĢrėkite Crontab Guru", @@ -69,9 +67,7 @@ "image_format": "Formatas", "image_format_description": "WebP sukuria maÅžesnius failus nei JPEG, bet lėčiau juos apdoroja.", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą perÅžiÅĢrą", - "image_prefer_embedded_preview_setting_description": "", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", - "image_prefer_wide_gamut_setting_description": "", "image_preview_description": "Vidutinio dydÅžio vaizdas su iÅĄvalytais metaduomenimis, naudojamas kai ÅžiÅĢrimas vienas objektas arba maÅĄininiam mokymuisi", "image_preview_quality_description": "PerÅžiÅĢros kokybė nuo 1-100. AukÅĄtesnės reikÅĄmės yra geriau, bet sukuriami didesni failai gali sumaÅžinti programos reagavimo laiką. MaÅžos vertės nustatymas gali paveikti maÅĄininio mokymo kokybę.", "image_preview_title": "PerÅžiÅĢros nustatymai", @@ -107,22 +103,18 @@ "machine_learning_clip_model": "CLIP modelis", "machine_learning_duplicate_detection": "DublikatÅŗ aptikimas", "machine_learning_duplicate_detection_enabled": "ÄŽjungti dublikatÅŗ aptikimą", - "machine_learning_duplicate_detection_enabled_description": "", "machine_learning_duplicate_detection_setting_description": "Naudoti CLIP įterpimus, norint rasti galimus duplikatus", "machine_learning_enabled": "ÄŽgalinti maÅĄininį mokymąsi", "machine_learning_enabled_description": "Jei iÅĄjungta, visos „ML“ funkcijos bus iÅĄjungtos, nepaisant toliau pateiktÅŗ nustatymÅŗ.", "machine_learning_facial_recognition": "VeidÅŗ atpaÅžinimas", "machine_learning_facial_recognition_description": "Aptikti, atpaÅžinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "VeidÅŗ atpaÅžinimo modelis", - "machine_learning_facial_recognition_model_description": "", "machine_learning_facial_recognition_setting": "ÄŽgalinti veidÅŗ atpaÅžinimą", "machine_learning_facial_recognition_setting_description": "IÅĄjungus, vaizdai nebus uÅžÅĄifruoti veidÅŗ atpaÅžinimui ir nebus naudojami ÅŊmoniÅŗ sekcijoje NarÅĄymo puslapyje.", "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "DidÅžiausias atstumas tarp dviejÅŗ vaizdÅŗ, kad jie bÅĢtÅŗ laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatÅŗ, tačiau gali bÅĢti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpaÅžinimo atstumas", - "machine_learning_max_recognition_distance_description": "", "machine_learning_min_detection_score": "Minimalus aptikimo balas", - "machine_learning_min_detection_score_description": "", "machine_learning_min_recognized_faces": "MaÅžiausias atpaÅžintÅŗ veidÅŗ skaičius", "machine_learning_min_recognized_faces_description": "MaÅžiausias atpaÅžintÅŗ veidÅŗ skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpaÅžinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas Åžmogui nepriskirtas.", "machine_learning_settings": "MaÅĄininio mokymosi nustatymai", @@ -156,7 +148,6 @@ "metadata_settings": "MetaduomenÅŗ nustatymai", "metadata_settings_description": "Tvarkyti metaduomenÅŗ nustatymus", "migration_job": "Migracija", - "migration_job_description": "", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Å ablonas nepridėtas", "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti saugyklos etiketę seniau įkeltiems iÅĄtekliams, paleiskite", @@ -182,26 +173,13 @@ "oauth_auto_register": "Automatinis registravimas", "oauth_auto_register_description": "AutomatiÅĄkai uÅžregistruoti naujus naudotojus po prisijungimo per OAuth", "oauth_button_text": "Mygtuko tekstas", - "oauth_client_id": "Kliento ID", - "oauth_client_secret": "Kliento paslaptis", "oauth_enable_description": "Prisijungti su OAuth", - "oauth_issuer_url": "Teikėjo URL", "oauth_mobile_redirect_uri": "Mobiliojo peradresavimo URI", "oauth_mobile_redirect_uri_override": "Mobiliojo peradresavimo URI pakeitimas", "oauth_mobile_redirect_uri_override_description": "ÄŽjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip '{callback}'", - "oauth_profile_signing_algorithm": "Profilio registracijos algoritmas", - "oauth_profile_signing_algorithm_description": "Algoritmas naudojamas vartotojo profilio registracijai.", - "oauth_scope": "Apimtis", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie ÅĄią funkciją galite paskaityti dokumentacijoje.", - "oauth_signing_algorithm": "", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", - "oauth_storage_quota_default": "", - "oauth_storage_quota_default_description": "", "offline_paths": "Nepasiekiami adresai", "offline_paths_description": "Å ie rezultatai gali bÅĢti dėl rankinio failÅŗ iÅĄtrynimo, kurie nėra iÅĄorinės bibliotekos dalis.", "password_enable_description": "Prisijungti su el. paÅĄtu ir slaptaÅžodÅžiu", @@ -222,93 +200,42 @@ "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo praneÅĄimas", "server_welcome_message_description": "ÅŊinutė, rodoma prisijungimo puslapyje.", - "sidecar_job_description": "", "slideshow_duration_description": "SekundÅžiÅŗ skaičius, kiek viena nuotrauka rodoma", "smart_search_job_description": "Vykdykite maÅĄininį mokymąsi bibliotekos elementÅŗ iÅĄmaniajai paieÅĄkai", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", - "storage_template_migration_job": "", - "storage_template_settings": "", - "storage_template_settings_description": "", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "ÅŊymÅŗ iÅĄvalymas", "theme_custom_css_settings": "Individualizuotas CSS", - "theme_custom_css_settings_description": "", "theme_settings": "Temos nustatymai", - "theme_settings_description": "", "thumbnail_generation_job": "Generuoti miniatiÅĢras", "thumbnail_generation_job_description": "DideliÅŗ, maÅžÅŗ ir neryÅĄkiÅŗ miniatiÅĢrÅŗ generavimas kiekvienam bibliotekos elementui, taip pat miniatiÅĢrÅŗ generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", - "transcoding_acceleration_api_description": "", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", - "transcoding_acceleration_qsv": "", - "transcoding_acceleration_rkmpp": "", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", "transcoding_accepted_containers": "Priimami konteineriai", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", "transcoding_advanced_options_description": "Parinktys, kuriÅŗ daugelis naudotojÅŗ keisti neturėtÅŗ", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukÅĄÄiausios kokybės variantas, tačiau turi maÅžesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įraÅĄai virÅĄija maksimalią leistiną bitÅŗ spartą arba nėra priimtino formato", "transcoding_constant_quality_mode": "Pastovios kokybės reÅžimas", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", - "transcoding_hardware_acceleration_description": "", "transcoding_hardware_decoding": "Aparatinis dekodavimas", - "transcoding_hardware_decoding_setting_description": "", "transcoding_hevc_codec": "HEVC kodekas", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", "transcoding_max_bitrate": "Maksimalus bitÅŗ srautas", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", "transcoding_target_resolution_description": "Didesnės skiriamosios gebos gali iÅĄsaugoti daugiau detaliÅŗ, tačiau jas koduoti uÅžtrunka ilgiau, failÅŗ dydÅžiai yra didesni ir gali sumaŞėti programos jautrumas.", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", "transcoding_video_codec": "Video kodekas", - "transcoding_video_codec_description": "", "trash_enabled_description": "ÄŽgalinti ÅĄiukÅĄliadėŞės funkcijas", "trash_number_of_days": "DienÅŗ skaičius", - "trash_number_of_days_description": "", "trash_settings": "Å iukÅĄliadėŞės nustatymai", "trash_settings_description": "Tvarkyti ÅĄiukÅĄliadėŞės nustatymus", "untracked_files": "Nesekami failai", "untracked_files_description": "Å ie failai aplikacijos nesekami. Jie galėjo atsirasti dėl nepavykusio perkėlimo, nutraukto įkėlimo ar palikti per klaidą", "user_delete_delay_settings": "IÅĄtrynimo delsa", - "user_delete_delay_settings_description": "", "user_management": "NaudotojÅŗ valdymas", "user_password_has_been_reset": "Naudotojo slaptaÅžodis buvo iÅĄ naujo nustatytas:", "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", "user_settings": "Naudotojo nustatymai", "user_settings_description": "Valdyti naudotojo nustatymus", "user_successfully_removed": "Naudotojas {email} sėkmingai paÅĄalintas.", - "version_check_enabled_description": "", "version_check_settings": "Versijos tikrinimas", "version_check_settings_description": "ÄŽjungti/iÅĄjungti naujos versijos praneÅĄimus", "video_conversion_job": "Vaizdo įraÅĄÅŗ konvertavimas", @@ -317,23 +244,10 @@ "admin_email": "Administratoriaus el. paÅĄtas", "admin_password": "Administratoriaus slaptaÅžodis", "administration": "Administravimas", - "advanced": "", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", "album_added": "Albumas pridėtas", "album_added_notification_setting_description": "Gauti el. paÅĄto praneÅĄimą, kai bÅĢsite pridėtas prie bendrinamo albumo", "album_cover_updated": "Albumo virÅĄelis atnaujintas", "album_delete_confirmation": "Ar tikrai norite iÅĄtrinti albumą {album}?", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", "album_info_updated": "Albumo informacija atnaujinta", "album_leave": "Palikti albumą?", "album_leave_confirmation": "Ar tikrai norite palikti albumą {album}?", @@ -342,21 +256,9 @@ "album_remove_user": "PaÅĄalinti naudotoją?", "album_remove_user_confirmation": "Ar tikrai norite paÅĄalinti naudotoją {user}?", "album_share_no_users": "Atrodo, kad bendrinate ÅĄÄ¯ albumą su visais naudotojais, arba neturite naudotojÅŗ, su kuriais galėtumėte bendrinti.", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", "album_updated": "Albumas atnaujintas", "album_updated_setting_description": "Gauti praneÅĄimą el. paÅĄtu, kai bendrinamas albumas turi naujÅŗ elementÅŗ", "album_user_removed": "PaÅĄalintas {user}", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", - "album_viewer_appbar_share_to": "Share To", - "album_viewer_page_share_add_users": "Add users", "album_with_link_access": "Tegul visi, turintys nuorodą, mato ÅĄio albumo nuotraukas ir Åžmones.", "albums": "Albumai", "albums_count": "{count, plural, one {# albumas} few {# albumai} other {# albumÅŗ}}", @@ -371,129 +273,37 @@ "api_key": "API raktas", "api_key_empty": "JÅĢsÅŗ API rakto pavadinimas netÅĢrėtÅŗ bÅĢti tuÅĄÄias", "api_keys": "API raktai", - "app_bar_signout_dialog_content": "Are you sure you want to sign out?", - "app_bar_signout_dialog_ok": "Yes", - "app_bar_signout_dialog_title": "Sign out", "app_settings": "Programos nustatymai", - "appears_in": "", "archive": "Archyvas", "archive_or_unarchive_photo": "Archyvuoti arba iÅĄarchyvuoti nuotrauką", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", "archive_size": "Archyvo dydis", "archive_size_description": "KonfigÅĢruoti archyvo dydį atsisiuntimams (GiB)", - "archived": "Archived", "archived_count": "{count, plural, other {# suarchyvuota}}", "are_these_the_same_person": "Ar tai tas pats asmuo?", "are_you_sure_to_do_this": "Ar tikrai norite tai daryti?", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", "asset_added_to_album": "Pridėta į albumą", "asset_adding_to_album": "Pridedama į albumą...", "asset_description_updated": "Elemento apraÅĄymas buvo atnaujintas", "asset_filename_is_offline": "Elementas {filename} nepasiekiamas", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", "asset_offline": "Elementas nepasiekiamas", "asset_offline_description": "Å is iÅĄorinis elementas neberandamas diske. Dėl pagalbos susisiekite su savo Immich administratoriumi.", - "asset_restored_successfully": "Asset restored successfully", "asset_uploaded": "ÄŽkelta", "asset_uploading": "ÄŽkeliama...", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", "assets": "Elementai", "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementÅŗ}}", "assets_added_to_album_count": "ÄŽ albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementÅŗ}}", "assets_added_to_name_count": "ÄŽ {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementÅŗ}}", "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementÅŗ perkelta}} į ÅĄiukÅĄliadėŞę", "assets_permanently_deleted_count": "{count, plural, one {# elementas iÅĄtrintas} few {# elementai iÅĄtrinti} other {# elementÅŗ iÅĄtrinta}} visam laikui", "assets_removed_count": "{count, plural, one {PaÅĄalintas # elementas} few {PaÅĄalinti # elementai} other {PaÅĄalinta # elementÅŗ}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", "assets_restore_confirmation": "Ar tikrai norite atkurti visus ÅĄiukÅĄliadėŞėje esančius perkeltus elementus? Å io veiksmo atÅĄaukti negalėsite! Pastaba: nepasiekiami elementai tokiu bÅĢdu atkurti nebus.", "assets_restored_count": "{count, plural, one {Atkurtas # elementas} few {Atkurti # elementai} other {Atkurta # elementÅŗ}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}} jau prieÅĄ tai buvo albume", "authorized_devices": "Autorizuoti įrenginiai", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "Atgal", "back_close_deselect": "Atgal, uÅždaryti arba atÅžymėti", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", - "backup_controller_page_background_app_refresh_enable_button_text": "Go to settings", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "backup_controller_page_turn_on": "Turn on foreground backup", - "backup_controller_page_uploading_file_info": "Uploading file info", - "backup_err_only_album": "Cannot remove the only album", - "backup_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", - "backward": "", "birthdate_saved": "Sėkmingai iÅĄsaugota gimimo data", "blurred_background": "NeryÅĄkus fonas", "bugs_and_feature_requests": "KlaidÅŗ ir funkcijÅŗ uÅžklausos", @@ -501,65 +311,28 @@ "bulk_keep_duplicates_confirmation": "Ar tikrai norite palikti visus {count, plural, one {# besidubliuojantį elementą} few {# besidubliuojančius elementus} other {# besidubliuojančiÅŗ elementÅŗ}}? Tokiu bÅĢdu nieko netrinant bus sutvarkytos visos dublikatÅŗ grupės.", "bulk_trash_duplicates_confirmation": "Ar tikrai norite perkelti į ÅĄiukÅĄliadėŞę visus {count, plural, one {# besidubliuojantį elementą} few {# besidubliuojančius elementus} other {# besidubliuojančiÅŗ elementÅŗ}}? Bus paliktas didÅžiausias kiekvienos grupės elementas ir į ÅĄiukÅĄliadėŞę perkelti kiti besidubliuojantys elementai.", "buy": "ÄŽsigyti Immich", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", - "cache_settings_title": "Caching Settings", "camera": "Fotoaparatas", "camera_brand": "Fotoaparato prekės Åženklas", "camera_model": "Fotoaparato modelis", "cancel": "AtÅĄaukti", "cancel_search": "AtÅĄaukti paieÅĄką", - "canceled": "Canceled", "cannot_merge_people": "Negalima sujungti asmenÅŗ", "cannot_update_the_description": "Negalima atnaujinti apraÅĄymo", "change_date": "Pakeisti datą", - "change_display_order": "Change display order", "change_expiration_time": "Pakeisti galiojimo trukmę", "change_location": "Pakeisti vietovę", "change_name": "Pakeisti vardą", - "change_name_successfully": "", "change_password": "Pakeisti slaptaÅžodį", "change_password_description": "Tai arba pirmas kartas, kai jungiatės prie sistemos, arba buvo pateikta uÅžklausa pakeisti jÅĢsÅŗ slaptaÅžodį. PraÅĄome įvesti naują slaptaÅžodį Åžemiau.", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", "change_your_password": "Pakeisti slaptaÅžodį", "changed_visibility_successfully": "Matomumas pakeistas sėkmingai", "check_all": "ÅŊymėti viską", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "Tikrinti Åžurnalus", "city": "Miestas", "clear": "IÅĄvalyti", "clear_all": "IÅĄvalyti viską", "clear_message": "IÅĄvalyti praneÅĄimą", "clear_value": "IÅĄvalyti reikÅĄmę", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "close": "UÅždaryti", "collapse": "Suskleisti", "collapse_all": "Suskleisti viską", @@ -568,25 +341,12 @@ "comment_options": "KomentarÅŗ parinktys", "comments_and_likes": "Komentarai ir patiktukai", "comments_are_disabled": "Komentarai yra iÅĄjungti", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", "confirm": "Patvirtinti", "confirm_admin_password": "Patvirtinti administratoriaus slaptaÅžodį", "confirm_delete_shared_link": "Ar tikrai norite iÅĄtrinti ÅĄią bendrinimo nuorodą?", "confirm_password": "Patvirtinti slaptaÅžodį", - "contain": "", "context": "Kontekstas", "continue": "Tęsti", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", "copied_image_to_clipboard": "Nuotrauka nukopijuota į iÅĄkarpinę.", "copied_to_clipboard": "Nukopijuota į iÅĄkapinę!", "copy_error": "Kopijavimo klaida", @@ -597,72 +357,45 @@ "copy_password": "Kopijuoti slaptaÅžodį", "copy_to_clipboard": "Kopijuoti į iÅĄkarpinę", "country": "Å alis", - "cover": "", - "covers": "", "create": "Sukurti", "create_album": "Sukurti albumą", - "create_album_page_untitled": "Untitled", "create_library": "Sukurti biblioteką", "create_link": "Sukurti nuorodą", "create_link_to_share": "Sukurti bendrinimo nuorodą", "create_link_to_share_description": "Leisti bet kam su nuoroda matyti paÅžymėtą(-as) nuotrauką(-as)", - "create_new": "CREATE NEW", "create_new_person": "Sukurti naują ÅžmogÅŗ", "create_new_person_hint": "Priskirti pasirinktus elementus naujam Åžmogui", "create_new_user": "Sukurti naują varotoją", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", "create_tag": "Sukurti Åžymą", "create_tag_description": "Sukurti naują Åžymą. ÄŽdėtinėms Åžymoms įveskite pilną kelią, įskaitant pasviruosius brÅĢkÅĄnius.", "create_user": "Sukurti naudotoją", "created": "Sukurta", - "crop": "Crop", - "curated_object_page_title": "Things", "current_device": "Dabartinis įrenginys", - "current_server_address": "Current server address", - "custom_locale": "", "custom_locale_description": "Formatuoti datas ir skaičius pagal kalbą ir regioną", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", - "dark": "", "date_after": "Data po", "date_and_time": "Data ir laikas", "date_before": "Data prieÅĄ", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Gimimo data sėkmingai iÅĄsaugota", - "date_range": "", "day": "Diena", "deduplicate_all": "Å alinti visus dublikatus", "deduplication_criteria_1": "Failo dydis baitais", "deduplication_criteria_2": "EXIF metaduomenÅŗ įraÅĄÅŗ skaičius", "deduplication_info": "DublikatÅŗ ÅĄalinimo informacija", "deduplication_info_description": "Automatinis elementÅŗ parinkimas ir masinis dublikatÅŗ ÅĄalinimas atliekamas atsiÅžvelgiant į:", - "default_locale": "", "default_locale_description": "Formatuoti datas ir skaičius pagal jÅĢsÅŗ narÅĄyklės lokalę", "delete": "IÅĄtrinti", "delete_album": "IÅĄtrinti albumą", "delete_api_key_prompt": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ API raktą?", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", "delete_duplicates_confirmation": "Ar tikrai norite visam laikui iÅĄtrinti ÅĄiuos dublikatus?", "delete_key": "IÅĄtrinti raktą", "delete_library": "IÅĄtrinti biblioteką", "delete_link": "IÅĄtrinti nuorodą", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", "delete_shared_link": "IÅĄtrinti bendrinimo nuorodą", - "delete_shared_link_dialog_title": "Delete Shared Link", "delete_tag": "IÅĄtrinti Åžymą", "delete_tag_confirmation_prompt": "Ar tikrai norite iÅĄtrinti Åžymą {tagName}?", "delete_user": "IÅĄtrinti naudotoją", "deleted_shared_link": "Bendrinimo nuoroda iÅĄtrinta", "description": "ApraÅĄymas", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", "details": "Detalės", "direction": "Kryptis", "disabled": "IÅĄjungta", @@ -670,30 +403,13 @@ "discover": "Atrasti", "dismiss_all_errors": "Nepaisyti visÅŗ klaidÅŗ", "dismiss_error": "Nepaisyti klaidos", - "display_options": "", "display_order": "Atvaizdavimo tvarka", "display_original_photos": "Rodyti originalias nuotraukas", - "display_original_photos_setting_description": "", "do_not_show_again": "Daugiau nerodyti ÅĄio praneÅĄimo", "documentation": "Dokumentacija", - "done": "", "download": "AtsisiÅŗsti", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_notfound": "Download not found", - "download_paused": "Download paused", "download_settings": "AtsisiÅŗsti", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", "downloading": "Siunčiama", - "downloading_media": "Downloading media", "duplicates": "Dublikatai", "duplicates_description": "Sutvarkykite kiekvieną elementÅŗ grupę nurodydami elementus, kurie yra dublikatai (jei tokiÅŗ yra)", "duration": "Trukmė", @@ -709,26 +425,20 @@ "edit_key": "Redaguoti raktą", "edit_link": "Redaguoti nuorodą", "edit_location": "Redaguoti vietovę", - "edit_location_dialog_title": "Location", "edit_name": "Redaguoti vardą", "edit_people": "Redaguoti Åžmones", "edit_tag": "Redaguoti Åžymą", "edit_title": "Redaguoti antraÅĄtę", "edit_user": "Redaguoti naudotoją", "edited": "Redaguota", - "editor": "", "email": "El. paÅĄtas", - "empty_folder": "This folder is empty", "empty_trash": "IÅĄtuÅĄtinti ÅĄiukÅĄliadėŞę", "enable": "ÄŽgalinti", "enabled": "ÄŽgalintas", "end_date": "Pabaigos data", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "Klaida", - "error_change_sort_album": "Failed to change album sort order", "error_loading_image": "Klaida įkeliant vaizdą", - "error_saving_image": "Error: {}", "error_title": "Klaida - KaÅžkas nutiko ne taip", "errors": { "cant_apply_changes": "Negalima taikyti pakeitimÅŗ", @@ -768,75 +478,41 @@ "unable_to_create_library": "Nepavyko sukurti bibliotekos", "unable_to_create_user": "Nepavyko sukurti naudotojo", "unable_to_delete_album": "Nepavyksta iÅĄtrinti albumo", - "unable_to_delete_asset": "", "unable_to_delete_exclusion_pattern": "Nepavyksta iÅĄtrinti iÅĄimčiÅŗ ÅĄablono", "unable_to_delete_import_path": "Nepavyksta iÅĄtrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyko iÅĄtrinti bendrinimo nuorodos", "unable_to_delete_user": "Nepavyksta iÅĄtrinti naudotojo", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti iÅĄimčiÅŗ ÅĄablono", "unable_to_edit_import_path": "Nepavyksta redaguoti iÅĄimčiÅŗ kelio", - "unable_to_empty_trash": "", "unable_to_enter_fullscreen": "Nepavyksta pereiti į viso ekrano reÅžimą", "unable_to_exit_fullscreen": "Nepavyksta iÅĄeiti iÅĄ viso ekrano reÅžimo", "unable_to_get_shared_link": "Nepavyko gauti bendrinimo nuorodos", "unable_to_hide_person": "Nepavyksta paslėpti Åžmogaus", "unable_to_link_oauth_account": "Nepavyko susieti su OAuth paskyra", "unable_to_load_album": "Nepavyksta uÅžkrauti albumo", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", "unable_to_log_out_all_devices": "Nepavyksta atjungti visÅŗ įrenginiÅŗ", "unable_to_log_out_device": "Nepavyksta atjungti įrenginio", "unable_to_login_with_oauth": "Nepavyko prisijungti su OAuth", "unable_to_play_video": "Nepavyksta paleisti vaizdo įraÅĄo", "unable_to_refresh_user": "Nepavyksta atnaujinti naudotojo", - "unable_to_remove_album_users": "", "unable_to_remove_api_key": "Nepavyko paÅĄalinti API rakto", "unable_to_remove_assets_from_shared_link": "Nepavyko iÅĄ bendrinimo nuorodos paÅĄalinti elementÅŗ", "unable_to_remove_deleted_assets": "Nepavyko paÅĄalinti nepasiekiamÅŗ elementÅŗ", "unable_to_remove_library": "Nepavyksta paÅĄalinti bibliotekos", "unable_to_remove_partner": "Nepavyksta paÅĄalinti partnerio", "unable_to_remove_reaction": "Nepavyksta paÅĄalinti reakcijos", - "unable_to_repair_items": "", - "unable_to_reset_password": "", "unable_to_resolve_duplicate": "Nepavyko sutvarkyti dublikatÅŗ", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_name": "", "unable_to_save_profile": "Nepavyko iÅĄsaugoti profilio", "unable_to_save_settings": "Nepavyksta iÅĄsaugoti nustatymÅŗ", "unable_to_scan_libraries": "Nepavyksta nuskaityti bibliotekÅŗ", "unable_to_scan_library": "Nepavyksta nuskaityti bibliotekos", "unable_to_set_feature_photo": "Nepavyksta nustatyti mėgstamiausios nuotraukos", "unable_to_set_profile_picture": "Nepavyksta nustatyti profilio nuotraukos", - "unable_to_submit_job": "", "unable_to_trash_asset": "Nepavyko perkelti į ÅĄiukÅĄliadėŞę", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_user": "", "unable_to_upload_file": "Nepavyksta įkelti failo" }, - "exif": "Exif", - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "IÅĄeiti iÅĄ skaidriÅŗ perÅžiÅĢros", "expand_all": "IÅĄskleisti viską", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", - "experimental_settings_title": "Experimental", - "expire_after": "", "expired": "Nebegalioja", "expires_date": "Nebegalios uÅž {date}", "explore": "NarÅĄyti", @@ -845,51 +521,25 @@ "extension": "Plėtinys", "external": "IÅĄorinis", "external_libraries": "IÅĄorinės bibliotekos", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "Nepriskirta", - "failed": "Failed", - "failed_to_load_assets": "Failed to load assets", - "failed_to_load_folder": "Failed to load folder", "favorite": "Mėgstamiausias", "favorite_or_unfavorite_photo": "ÄŽtraukti prie arba paÅĄalinti iÅĄ mėgstamiausiÅŗ", "favorites": "Mėgstamiausi", - "favorites_page_no_favorites": "No favorite assets found", - "feature_photo_updated": "", "features": "Funkcijos", "features_setting_description": "Valdyti aplikacijos funkcijas", "file_name": "Failo pavadinimas", "file_name_or_extension": "Failo pavadinimas arba plėtinys", - "filename": "", "filetype": "Failo tipas", - "filter": "Filter", "filter_people": "Filtruoti Åžmones", - "fix_incorrect_match": "", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Aplankai", "folders_feature_description": "PerÅžiÅĢrėkite failÅŗ sistemoje esančias nuotraukas ir vaizdo įraÅĄus aplankÅŗ rodinyje", - "forward": "", - "general": "", "get_help": "Gauti pagalbos", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "grant_permission": "Grant permission", "group_albums_by": "Grupuoti albumus pagal...", "group_no": "Negrupuoti", "group_owner": "Grupuoti pagal savininką", "group_year": "Grupuoti pagal metus", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", "has_quota": "Turi kvotą", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "Labas {name} ({email})", "hide_all_people": "Slėpti visus asmenis", "hide_gallery": "Slėpti galeriją", @@ -897,29 +547,9 @@ "hide_password": "Slėpti slaptaÅžodį", "hide_person": "Slėpti asmenį", "hide_unnamed_people": "Slėpti neįvardintus asmenis", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", - "home_page_building_timeline": "Building the timeline", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", - "host": "", "hour": "Valanda", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Nuotrauka", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", "immich_logo": "Immich logotipas", "import_from_json": "Importuoti iÅĄ JSON", "import_path": "Importavimo kelias", @@ -927,16 +557,12 @@ "include_archived": "ÄŽtraukti archyvuotus", "include_shared_albums": "ÄŽtraukti bendrinamus albumus", "include_shared_partner_assets": "ÄŽtraukti partnerio pasidalintus elementus", - "individual_share": "", "info": "Informacija", "interval": { "day_at_onepm": "Kiekvieną dieną 13:00", - "hours": "", "night_at_midnight": "Kiekvieną vidurnaktį", "night_at_twoam": "Kiekvieną naktį 02:00" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "Kviesti Åžmones", "invite_to_album": "Pakviesti į albumą", "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}}", @@ -954,60 +580,22 @@ "level": "Lygis", "library": "Biblioteka", "library_options": "Bibliotekos pasirinktys", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", - "light": "", "link_options": "NuorodÅŗ parinktys", "link_to_oauth": "Susieti su OAuth", "linked_oauth_account": "Susieta OAuth paskyra", "list": "SąraÅĄas", "loading": "Kraunama", "loading_search_results_failed": "Nepavyko uÅžkrauti paieÅĄkos rezultatÅŗ", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", "log_out": "Atsijungti", "log_out_all_devices": "Atsijungti iÅĄ visÅŗ įrenginiÅŗ", "logged_out_all_devices": "Atsijungta iÅĄ visÅŗ įrenginiÅŗ", "login": "Prisijungti", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", "login_has_been_disabled": "Prisijungimas iÅĄjungtas.", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", "logout_all_device_confirmation": "Ar tikrai norite atsijungti iÅĄ visÅŗ įrenginiÅŗ?", "logout_this_device_confirmation": "Ar tikrai norite atsijungti iÅĄ ÅĄio prietaiso?", "longitude": "Ilguma", - "look": "", "loop_videos": "Kartoti vaizdo įraÅĄus", - "loop_videos_description": "", "make": "Gamintojas", "manage_shared_links": "Bendrinimo nuorodÅŗ tvarkymas", "manage_sharing_with_partners": "Valdyti dalijimąsi su partneriais", @@ -1017,39 +605,11 @@ "manage_your_devices": "Valdyti prijungtus įrenginius", "manage_your_oauth_connection": "Tvarkyti OAuth prisijungimą", "map": "ÅŊemėlapis", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", - "map_marker_with_image": "", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", "map_settings": "ÅŊemėlapio nustatymai", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", "matches": "Atitikmenys", "media_type": "Laikmenos tipas", "memories": "Atsiminimai", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", "memories_setting_description": "Valdyti tai, ką matote savo prisiminimuose", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Atmintis", "menu": "Meniu", "merge": "Sujungti", @@ -1063,16 +623,11 @@ "missing": "TrÅĢkstami", "model": "Modelis", "month": "Mėnesis", - "monthly_title_text_date_format": "MMMM y", "more": "Daugiau", "moved_to_trash": "Perkelta į ÅĄiukÅĄliadėŞę", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", "my_albums": "Mano albumai", "name": "Vardas", "name_or_nickname": "Vardas arba slapyvardis", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "Niekada", "new_album": "Naujas albumas", "new_api_key": "Naujas API raktas", @@ -1089,34 +644,21 @@ "no_albums_yet": "Atrodo, kad dar neturite albumÅŗ.", "no_archived_assets_message": "Suarchyvuokite nuotraukas ir vaizdo įraÅĄus, kad jie nebÅĢtÅŗ rodomi nuotraukÅŗ rodinyje", "no_assets_message": "SPUSTELĖKITE NORĖDAMI ÄŽKELTI PIRMĄJĄ NUOTRAUKĄ", - "no_assets_to_show": "No assets to show", "no_duplicates_found": "DublikatÅŗ nerasta.", - "no_exif_info_available": "", "no_explore_results_message": "ÄŽkelkite daugiau nuotraukÅŗ ir tyrinėkite savo kolekciją.", - "no_favorites_message": "", "no_libraries_message": "Sukurkite iÅĄorinę biblioteką nuotraukoms ir vaizdo įraÅĄams perÅžiÅĢrėti", "no_name": "Be vardo", - "no_places": "", "no_results": "Nerasta", "no_results_description": "Pabandykite sinonimą arba bendresnį raktaÅžodį", - "no_shared_albums_message": "", "not_in_any_album": "Nė viename albume", - "not_selected": "Not selected", "notes": "Pastabos", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", - "notification_permission_list_tile_content": "Grant permission to enable notifications.", - "notification_permission_list_tile_enable_button": "Enable Notifications", - "notification_permission_list_tile_title": "Notification Permission", "notification_toggle_setting_description": "ÄŽjungti el. paÅĄto praneÅĄimus", "notifications": "PraneÅĄimai", "notifications_setting_description": "Tvarkyti praneÅĄimus", - "oauth": "OAuth", "official_immich_resources": "OficialÅĢs Immich iÅĄtekliai", "offline": "Neprisijungęs", "offline_paths": "Nepasiekiami adresai", - "ok": "Ok", "oldest_first": "Seniausias pirmas", - "on_this_device": "On this device", "onboarding_welcome_user": "Sveiki atvykę, {user}", "online": "Prisijungęs", "only_favorites": "Tik mėgstamiausi", @@ -1125,7 +667,6 @@ "or": "arba", "organize_your_library": "Tvarkykite savo biblioteką", "original": "Originalas", - "other": "", "other_devices": "Kiti įrenginiai", "other_variables": "Kiti kintamieji", "owned": "Nuosavi", @@ -1134,27 +675,12 @@ "partner_can_access": "{partner} gali naudotis", "partner_can_access_assets": "Visos jÅĢsÅŗ nuotraukos ir vaizdo įraÅĄai, iÅĄskyrus archyvuotus ir iÅĄtrintus", "partner_can_access_location": "Vieta, kurioje darytos nuotraukos", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", - "partner_sharing": "", "partners": "Partneriai", "password": "SlaptaÅžodis", "password_does_not_match": "SlaptaÅžodis nesutampa", "password_required": "Reikalingas slaptaÅžodis", "password_reset_success": "SlaptaÅžodis sėkmingai atkurtas", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, "path": "Kelias", - "pattern": "", "pause": "Sustabdyti", "pause_memories": "Pristabdyti atsiminimus", "paused": "Sustabdyta", @@ -1163,54 +689,22 @@ "people_edits_count": "{count, plural, one {Redaguotas # asmuo} few {Redaguoti # asmenys} other {Redaguota # asmenÅŗ}}", "people_feature_description": "PerÅžiÅĢrėkite nuotraukas ir vaizdo įraÅĄus sugrupuotus pagal asmenis", "people_sidebar_description": "Rodyti asmenÅŗ rodinio nuorodą ÅĄoninėje juostoje", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", "permanently_delete": "IÅĄtrinti visam laikui", "permanently_delete_assets_count": "Visam laikui iÅĄtrinti {count, plural, one {# elementą} few {# elementus} other {# elementÅŗ}}", - "permanently_deleted_asset": "", "permanently_deleted_assets_count": "Visam laikui {count, plural, one {iÅĄtrintas # elementas} few {iÅĄtrinti # elementai} other {iÅĄtrinta # elementÅŗ}}", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", "photos": "Nuotraukos", "photos_and_videos": "Nuotraukos ir vaizdo įraÅĄai", "photos_count": "{count, plural, one {{count, number} nuotrauka} few {{count, number} nuotraukos} other {{count, number} nuotraukÅŗ}}", "photos_from_previous_years": "AnkstesniÅŗ metÅŗ nuotraukos", - "pick_a_location": "", "place": "Vieta", "places": "Vietos", - "play": "", "play_memories": "Leisti atsiminimus", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", "profile_image_of_user": "{user} profilio nuotrauka", "profile_picture_set": "Profilio nuotrauka nustatyta.", "public_album": "VieÅĄas albumas", - "public_share": "", "purchase_account_info": "Rėmėjas", "purchase_activated_subtitle": "Dėkojame, kad remiate Immich ir atviro kodo programinę įrangą", - "purchase_activated_time": "Suaktyvinta {date, date}", + "purchase_activated_time": "Suaktyvinta {date}", "purchase_activated_title": "JÅĢsÅŗ raktas sėkmingai aktyvuotas", "purchase_button_activate": "Aktyvuoti", "purchase_button_buy": "Pirkti", @@ -1242,12 +736,6 @@ "rating": "ÄŽvertinimas ÅžvaigÅždutėmis", "rating_count": "{count, plural, one {# įvertinimas} few {# įvertinimai} other {# įvertinimÅŗ}}", "rating_description": "Rodyti EXIF įvertinimus informacijos skydelyje", - "reaction_options": "", - "read_changelog": "", - "recent": "", - "recent_searches": "", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", "refresh": "Atnaujinti", "refresh_encoded_videos": "Perkrauti apdorotus vaizdo įraÅĄus", "refresh_faces": "Perkrauti veidus", @@ -1260,7 +748,6 @@ "refreshing_metadata": "Perkraunami metaduomenys", "remove": "PaÅĄalinti", "remove_assets_shared_link_confirmation": "Ar tikrai norite paÅĄalinti {count, plural, one {# elementą} few {# elementus} other {# elementÅŗ}} iÅĄ ÅĄios bendrinimo nuorodos?", - "remove_deleted_assets": "", "remove_from_album": "PaÅĄalinti iÅĄ albumo", "remove_from_favorites": "PaÅĄalinti iÅĄ mėgstamiausiÅŗ", "remove_from_shared_link": "PaÅĄalinti iÅĄ bendrinimo nuorodos", @@ -1278,205 +765,75 @@ "replace_with_upload": "Pakeisti naujai įkeltu failu", "require_password": "Reikalauti slaptaÅžodÅžio", "reset": "Atstatyti", - "reset_password": "", - "reset_people_visibility": "", "resolve_duplicates": "Sutvarkyti dublikatus", "resolved_all_duplicates": "Sutvarkyti visi dublikatai", "restore": "Atkurti", "restore_all": "Atkurti visus", "restore_user": "Atkurti naudotoją", - "retry_upload": "", "review_duplicates": "PerÅžiÅĢrėti dublikatus", - "role": "", "save": "IÅĄsaugoti", - "save_to_gallery": "Save to gallery", "saved_api_key": "IÅĄsaugotas API raktas", "saved_profile": "IÅĄsaugotas profilis", "saved_settings": "IÅĄsaugoti nustatymai", "say_something": "Ką nors pasakykite", - "scaffold_body_error_occurred": "Error occurred", "scan_all_libraries": "Skenuoti visas bibliotekas", "scan_library": "Skenuoti", "scan_settings": "Skenavimo nustatymai", "search": "IeÅĄkoti", - "search_albums": "", "search_by_context": "IeÅĄkoti pagal kontekstą", "search_by_description_example": "ÅŊygio diena Sapoje", "search_by_filename": "IeÅĄkoti pagal failo pavadinimą arba plėtinį", "search_by_filename_example": "pvz. IMG_1234.JPG arba PNG", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", "search_country": "IeÅĄkoti ÅĄalies...", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", - "search_for_existing_person": "", - "search_no_more_result": "No more results", "search_no_people_named": "Nėra ÅžmoniÅŗ vardu „{name}“", - "search_no_result": "No results found, try a different search term or combination", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", "search_people": "IeÅĄkoti ÅžmoniÅŗ", "search_places": "IeÅĄkoti vietÅŗ", - "search_result_page_new_search_hint": "New Search", "search_settings": "IeÅĄkoti nustatymÅŗ", - "search_state": "", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", "search_tags": "IeÅĄkoti ÅžymÅŗ...", - "search_timezone": "", "search_type": "PaieÅĄkos tipas", "search_your_photos": "IeÅĄkoti nuotraukÅŗ", - "searching_locales": "", - "second": "", - "select_album_cover": "", - "select_all": "", "select_all_duplicates": "Pasirinkti visus dublikatus", "select_avatar_color": "Pasirinkti avataro spalvą", "select_face": "Pasirinkti veidą", "select_featured_photo": "Pasirinkti rodomą nuotrauką", "select_keep_all": "Visus paÅžymėti \"Palikti\"", "select_library_owner": "Pasirinkti bibliotekos savininką", - "select_new_face": "", - "select_photos": "", "select_trash_all": "Visus paÅžymėti \"IÅĄmesti\"", - "select_user_for_sharing_page_err_album": "Failed to create album", "selected": "Pasirinkta", "selected_count": "{count, plural, one {# pasirinktas} few {# pasirinkti} other {# pasirinktÅŗ}}", "send_message": "SiÅŗsti Åžinutę", "send_welcome_email": "SiÅŗsti sveikinimo el. laiÅĄką", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", - "server_info_box_server_url": "Server URL", "server_offline": "Serveris nepasiekiamas", "server_online": "Serveris pasiekiamas", "server_stats": "Serverio statistika", "server_version": "Serverio versija", "set": "Nustatyti", - "set_as_album_cover": "", "set_as_profile_picture": "Nustatyti kaip profilio nuotrauką", "set_date_of_birth": "Nustatyti gimimo datą", "set_profile_picture": "Nustatyti profilio nuotrauką", "set_slideshow_to_fullscreen": "Nustatyti skaidriÅŗ perÅžiÅĢrą per visą ekraną", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "setting_notifications_subtitle": "Adjust your notification preferences", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Nustatymai", - "settings_require_restart": "Please restart Immich to apply this setting", - "settings_saved": "", "share": "Dalintis", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", "shared": "Bendrinami", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", - "shared_by": "", - "shared_by_you": "", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", "shared_link_options": "Bendrinimo nuorodos parametrai", "shared_links": "Bendrinimo nuorodos", "shared_photos_and_videos_count": "{assetCount, plural, one {# bendrinama nuotrauka ir vaizdo įraÅĄas} few {# bendrinamos nuotraukos ir vaizdo įraÅĄai} other {# bendrinamÅŗ nuotraukÅŗ ir vaizdo įraÅĄÅŗ}}", - "shared_with_me": "Shared with me", "shared_with_partner": "Pasidalinta su {partner}", "sharing": "Dalijimasis", "sharing_enter_password": "Norėdami perÅžiÅĢrėti ÅĄÄ¯ puslapį, įveskite slaptaÅžodį.", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", "sharing_sidebar_description": "Rodyti bendrinimo rodinio nuorodą ÅĄoninėje juostoje", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", "show_album_options": "Rodyti albumo parinktis", "show_file_location": "Rodyti rinkmenos vietą", "show_gallery": "Rodyti galeriją", - "show_hidden_people": "", "show_in_timeline": "Rodyti laiko skalėje", "show_in_timeline_setting_description": "Rodyti ÅĄio naudotojo nuotraukas ir vaizdo įraÅĄus mano laiko skalėje", - "show_keyboard_shortcuts": "", "show_metadata": "Rodyti metaduomenis", "show_or_hide_info": "Rodyti arba slėpti informaciją", "show_password": "Rodyti slaptaÅžodį", - "show_person_options": "", - "show_progress_bar": "", "show_search_options": "Rodyti paieÅĄkos parinktis", "show_slideshow_transition": "Rodyti perėjimą tarp skaidriÅŗ", "show_supporter_badge": "Rėmėjo Åženklelis", "show_supporter_badge_description": "Rodyti rėmėjo Åženklelį", - "shuffle": "", "sidebar": "Å oninė juosta", "sidebar_display_description": "Rodyti rodinio nuorodą ÅĄoninėje juostoje", "sign_out": "Atsijungti", @@ -1485,7 +842,6 @@ "skip_to_content": "Pereiti prie turinio", "slideshow": "SkaidriÅŗ perÅžiÅĢra", "slideshow_settings": "SkaidriÅŗ perÅžiÅĢros nustatymai", - "sort_albums_by": "", "sort_created": "SukÅĢrimo data", "sort_modified": "Keitimo data", "sort_oldest": "Seniausia nuotrauka", @@ -1497,24 +853,15 @@ "stack_select_one_photo": "Pasirinkti pagrindinę grupės nuotrauką", "stack_selected_photos": "Grupuoti pasirinktas nuotraukas", "stacked_assets_count": "{count, plural, one {Sugrupuotas # elementas} few {Sugrupuoti # elementai} other {Sugrupuota # elementÅŗ}}", - "stacktrace": "", "start": "Pradėti", "start_date": "PradÅžios data", - "state": "", "status": "Statusas", - "stop_motion_photo": "", "storage": "Saugykla", - "storage_label": "", "storage_usage": "Naudojama {used} iÅĄ {available}", "submit": "Pateikti", - "suggestions": "", "sunrise_on_the_beach": "Saulėtekis paplÅĢdimyje", "support_and_feedback": "Palaikymas ir atsiliepimai", - "swap_merge_direction": "", "sync": "Sinchronizuoti", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "ÅŊyma", "tag_created": "Sukurta Åžyma: {tag}", "tag_feature_description": "PerÅžiÅĢrėkite nuotraukas ir vaizdo įraÅĄus sugrupuotus pagal suÅžymėtas temas", @@ -1524,21 +871,6 @@ "tags": "ÅŊymos", "template": "Å ablonas", "theme": "Tema", - "theme_selection": "", - "theme_selection_description": "", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", "time_based_memories": "Atsiminimai pagal laiką", "timeline": "Laiko skalė", "timezone": "Laiko juosta", @@ -1546,74 +878,56 @@ "to_change_password": "Pakeisti slaptaÅžodį", "to_favorite": "ÄŽtraukti prie mėgstamiausiÅŗ", "to_trash": "IÅĄmesti", - "toggle_settings": "", - "toggle_theme": "", - "total_usage": "", "trash": "Å iukÅĄliadėŞė", "trash_all": "Perkelti visus į ÅĄiukÅĄliadėŞę", "trash_count": "Perkelti {count, number} į ÅĄiukÅĄliadėŞę", - "trash_emptied": "Emptied trash", "trash_no_results_message": "ÄŽ ÅĄiukÅĄliadėŞę perkeltos nuotraukos ir vaizdo įraÅĄai bus rodomi čia.", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", "trashed_items_will_be_permanently_deleted_after": "ÄŽ ÅĄiukÅĄliadėŞę perkelti elementai bus visam laikui iÅĄtrinti po {days, plural, one {# dienos} other {# dienÅŗ}}.", "type": "Tipas", "unarchive": "IÅĄarchyvuoti", "unarchived_count": "{count, plural, other {# iÅĄarchyvuota}}", "unfavorite": "PaÅĄalinti iÅĄ mėgstamiausiÅŗ", - "unhide_person": "", - "unknown": "", "unknown_year": "NeÅžinomi metai", "unlink_oauth": "Atsieti OAuth", "unlinked_oauth_account": "Atsieta OAuth paskyra", "unnamed_album_delete_confirmation": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ albumą?", "unsaved_change": "NeiÅĄsaugoti pakeitimai", - "unselect_all": "", + "unselect_all": "AtÅĄaukti visÅŗ pasirinkimą", "unselect_all_duplicates": "AtÅžymėti visus dublikatus", "unstack": "IÅĄgrupuoti", "unstacked_assets_count": "{count, plural, one {IÅĄgrupuotas # elementas} few {IÅĄgrupuoti # elementai} other {IÅĄgrupuota # elementÅŗ}}", "untracked_files": "Nesekami failai", "untracked_files_decription": "Å ie failai aplikacijos nesekami. Jie galėjo atsirasti dėl nepavykusio perkėlimo, nutraukto įkėlimo ar palikti per klaidą", - "up_next": "", + "updated_at": "Atnaujintas", "updated_password": "SlaptaÅžodis atnaujintas", "upload": "ÄŽkelti", - "upload_concurrency": "", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", + "upload_concurrency": "ÄŽkėlimo lygiagretumas", + "upload_dialog_info": "Ar norite sukurti pasirinkto(-Åŗ) turinio(-Åŗ) atsarginę kopiją serveryje?", + "upload_dialog_title": "ÄŽkelti turinį", "upload_errors": "ÄŽkėlimas įvyko su {count, plural, one {# klaida} few {# klaidomis} other {# klaidÅŗ}}, norėdami pamatyti naujai įkeltus elementus perkraukite puslapį.", "upload_progress": "Liko {remaining, number} - Apdorota {processed, number}/{total, number}", "upload_status_duplicates": "Dublikatai", "upload_status_errors": "Klaidos", "upload_status_uploaded": "ÄŽkelta", "upload_success": "ÄŽkėlimas pavyko, norėdami pamatyti naujai įkeltus elementus perkraukite puslapį.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", - "usage": "", - "use_current_connection": "use current connection", + "upload_to_immich": "ÄŽkelti į Immich ({count})", + "uploading": "ÄŽkeliama", + "usage": "Naudojymas", + "use_current_connection": "naudoti dabartinį ryÅĄÄ¯", "user": "Naudotojas", + "user_has_been_deleted": "Å is naudotojas buvo iÅĄtrintas.", "user_id": "Naudotojo ID", - "user_usage_detail": "", + "user_pin_code_settings": "PIN kodas", + "user_pin_code_settings_description": "Tvarkykite savo PIN kodą", "user_usage_stats": "Paskyros naudojimo statistika", "user_usage_stats_description": "ÅŊiÅĢrėti paskyros naudojimo statistiką", "username": "Naudotojo vardas", "users": "Naudotojai", "utilities": "ÄŽrankiai", "validate": "Validuoti", - "validate_endpoint_error": "Please enter a valid URL", "variables": "Kintamieji", "version": "Versija", "version_announcement_closing": "Tavo draugas, Alex", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available 🎉", "version_history": "VersijÅŗ istorija", "version_history_item": "Versija {version} įdiegta {date}", "video": "Vaizdo įraÅĄas", @@ -1626,12 +940,7 @@ "view_all_users": "PerÅžiÅĢrėti visus naudotojus", "view_in_timeline": "ÅŊiÅĢrėti laiko skalėje", "view_links": "ÅŊiÅĢrėti nuorodas", - "view_next_asset": "", - "view_previous_asset": "", "view_stack": "PerÅžiÅĢrėti grupę", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", "waiting": "Laukiama", "warning": "ÄŽspėjimas", "week": "Savaitė", diff --git a/i18n/lv.json b/i18n/lv.json index fbefc9a521..6b54ee3196 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -8,13 +8,13 @@ "actions": "DarbÄĢbas", "active": "AktÄĢvs", "activity": "Aktivitāte", - "activity_changed": "Aktivitāte ir", + "activity_changed": "Aktivitāte ir {enabled, select, true {iespējota} other {atspējota}}", "add": "Pievienot", "add_a_description": "Pievienot aprakstu", "add_a_location": "Pievienot atraÅĄanās vietu", "add_a_name": "Pievienot vārdu", "add_a_title": "Pievienot virsrakstu", - "add_endpoint": "Add endpoint", + "add_endpoint": "Pievienot galapunktu", "add_exclusion_pattern": "Pievienot izslēgÅĄanas ÅĄablonu", "add_import_path": "Pievienot importa ceÄŧu", "add_location": "Pievienot lokāciju", @@ -56,10 +56,6 @@ "face_detection": "Seju noteikÅĄana", "image_format": "Formāts", "image_format_description": "WebP veido mazākus failus nekā JPEG, taču to kodÄ“ÅĄana ir lēnāka.", - "image_prefer_embedded_preview": "", - "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", - "image_prefer_wide_gamut_setting_description": "", "image_quality": "Kvalitāte", "image_resolution": "IzÅĄÄˇirtspēja", "image_settings": "Attēla IestatÄĢjumi", @@ -70,105 +66,43 @@ "job_settings_description": "Uzdevumu izpildes vienlaicÄĢguma pārvaldÄĢba", "job_status": "Uzdevumu statuss", "library_deleted": "Bibliotēka dzēsta", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", "library_settings_description": "Ārējo bibliotēku iestatÄĢjumu pārvaldÄĢba", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", "machine_learning_clip_model": "CLIP modelis", "machine_learning_duplicate_detection": "Dublikātu noteikÅĄana", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", - "machine_learning_enabled_description": "", "machine_learning_facial_recognition": "Seju atpazÄĢÅĄana", - "machine_learning_facial_recognition_description": "", "machine_learning_facial_recognition_model": "Seju atpazÄĢÅĄanas modelis", - "machine_learning_facial_recognition_model_description": "", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", "machine_learning_settings": "MaÅĄÄĢnmācÄĢÅĄanās iestatÄĢjumi", "machine_learning_settings_description": "MaÅĄÄĢnmācÄĢÅĄanās funkciju un iestatÄĢjumu pārvaldÄĢba", "machine_learning_smart_search": "Viedā meklÄ“ÅĄana", - "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled_description": "", "machine_learning_url_description": "MaÅĄÄĢnmācÄĢÅĄanās servera URL", "manage_concurrency": "VienlaicÄĢgas darbÄĢbas pārvaldÄĢba", "manage_log_settings": "ÅŊurnāla iestatÄĢjumu pārvaldÄĢba", - "map_dark_style": "", - "map_enable_description": "", "map_gps_settings": "Kartes un GPS iestatÄĢjumi", "map_gps_settings_description": "KarÅĄu un GPS (apgrieztās ÄŖeokodÄ“ÅĄanas) iestatÄĢjumu pārvaldÄĢba", - "map_light_style": "", "map_manage_reverse_geocoding_settings": "Reversās ÄŖeokodÄ“ÅĄanas iestatÄĢjumu pārvaldÄĢba", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", "map_settings": "Karte", "map_settings_description": "Kartes iestatÄĢjumu pārvaldÄĢba", - "map_style_description": "", "metadata_extraction_job": "Metadatu iegÅĢÅĄana", - "metadata_extraction_job_description": "", "metadata_settings": "Metadatu iestatÄĢjumi", "metadata_settings_description": "Metadatu iestatÄĢjumu pārvaldÄĢba", "migration_job": "Migrācija", - "migration_job_description": "", "no_paths_added": "Nav pievienots neviens ceÄŧÅĄ", "no_pattern_added": "Nav pievienots neviens izslēgÅĄanas ÅĄablons", "note_cannot_be_changed_later": "PIEZÄĒME: Vēlāk to vairs nevar mainÄĢt!", "notification_email_from_address": "No adreses", "notification_email_from_address_description": "SÅĢtÄĢtāja e-pasta adrese, piemēram: “Immich foto serveris ”", - "notification_email_host_description": "", "notification_email_ignore_certificate_errors": "Ignorēt sertifikātu kÄŧÅĢdas", "notification_email_ignore_certificate_errors_description": "Ignorēt TLS sertifikāta apstiprinÄÅĄanas kÄŧÅĢdas (nav ieteicams)", - "notification_email_password_description": "", "notification_email_port_description": "e-pasta servera ports (piemēram, 25, 465 vai 587)", "notification_email_sent_test_email_button": "NosÅĢtÄĢt testa e-pastu un saglabāt", - "notification_email_setting_description": "", "notification_email_test_email": "NosÅĢtÄĢt testa e-pastu", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", "notification_settings": "Paziņojumu iestatÄĢjumi", "notification_settings_description": "Paziņojumu iestatÄĢjumu, tostarp e-pasta, pārvaldÄĢba", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", "oauth_button_text": "Pogas teksts", - "oauth_client_id": "Klienta ID", - "oauth_client_secret": "Klienta noslēpums", "oauth_enable_description": "Pieslēgties ar OAuth", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", - "oauth_mobile_redirect_uri_override": "", - "oauth_mobile_redirect_uri_override_description": "", - "oauth_profile_signing_algorithm": "Profila parakstÄĢÅĄanas algoritms", - "oauth_profile_signing_algorithm_description": "Lietotāja profila parakstÄĢÅĄanai izmantotais algoritms.", - "oauth_scope": "", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth pieteikÅĄanās iestatÄĢjumu pārvaldÄĢba", - "oauth_signing_algorithm": "ParakstÄĢÅĄanas algoritms", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", "oauth_storage_quota_default": "Noklusējuma krātuves kvota (GiB)", - "oauth_storage_quota_default_description": "", "password_enable_description": "PieteikÅĄanās ar e-pasta adresi un paroli", "password_settings": "PieteikÅĄanās ar paroli", "password_settings_description": "PieteikÅĄanās ar paroli iestatÄĢjumu pārvaldÄĢba", @@ -178,133 +112,73 @@ "repair_all": "Salabot visu", "require_password_change_on_login": "PieprasÄĢt lietotājam mainÄĢt paroli pēc pirmās pieteikÅĄanās", "scanning_library": "Skenē bibliotēku", - "search_jobs": "Meklēt uzdevumus...", - "server_external_domain_settings": "", - "server_external_domain_settings_description": "", + "search_jobs": "Meklēt uzdevumusâ€Ļ", "server_settings": "Servera iestatÄĢjumi", "server_settings_description": "Servera iestatÄĢjumu pārvaldÄĢba", - "server_welcome_message": "", - "server_welcome_message_description": "", - "sidecar_job_description": "", - "slideshow_duration_description": "", - "smart_search_job_description": "", + "server_welcome_message": "Sveiciena ziņa", + "server_welcome_message_description": "Ziņojums, kas tiek parādÄĢts pieslēgÅĄanās lapā.", "storage_template_date_time_sample": "Laika paraugs {date}", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", "storage_template_migration": "Krātuves veidņu migrācija", "storage_template_migration_job": "Krātuves veidņu migrācijas uzdevums", + "storage_template_path_length": "Aptuvenais ceÄŧa garuma ierobeÅžojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", - "storage_template_settings_description": "", "system_settings": "Sistēmas iestatÄĢjumi", + "template_email_preview": "PriekÅĄskatÄĢjums", "template_email_settings_description": "Pielāgotu e-pasta paziņojumu veidņu pārvaldÄĢba", "template_settings_description": "Pielāgotu paziņojumu veidņu pārvaldÄĢba", "theme_custom_css_settings": "Pielāgots CSS", - "theme_custom_css_settings_description": "", - "theme_settings": "", + "theme_custom_css_settings_description": "Cascading Style Sheets Äŧauj pielāgot Immich izskatu.", "theme_settings_description": "Immich tÄĢmekÄŧa saskarnes pielāgojumu pārvaldÄĢba", - "thumbnail_generation_job_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", + "thumbnail_generation_job": "SÄĢktēlu ÄŖenerÄ“ÅĄana", + "transcoding_acceleration_api": "PaātrinÄÅĄanas API", "transcoding_acceleration_nvenc": "NVENC (nepiecieÅĄams NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (nepiecieÅĄams 7. paaudzes vai jaunāks Intel procesors)", "transcoding_acceleration_rkmpp": "RKMPP (tikai Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", + "transcoding_advanced_options_description": "Lielākajai daÄŧai lietotāju nevajadzētu mainÄĢt ÅĄÄĢs opcijas", "transcoding_audio_codec": "Audio kodeks", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", - "transcoding_constant_quality_mode": "", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", - "transcoding_hardware_acceleration_description": "", - "transcoding_hardware_decoding": "", - "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", + "transcoding_codecs_learn_more": "Lai uzzinātu vairāk par ÅĄeit lietoto terminoloÄŖiju, skatiet FFmpeg dokumentāciju par H.264 kodeku, HEVC kodeku un VP9 kodeku.", "transcoding_threads": "Pavedieni", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", "transcoding_video_codec": "Video kodeks", - "transcoding_video_codec_description": "", - "trash_enabled_description": "", "trash_number_of_days": "Dienu skaits", - "trash_number_of_days_description": "", - "trash_settings": "", "trash_settings_description": "Atkritnes iestatÄĢjumu pārvaldÄĢba", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", "user_management": "Lietotāju pārvaldÄĢba", "user_password_has_been_reset": "Lietotāja parole ir atiestatÄĢta:", "user_restore_description": "{user} konts tiks atjaunots.", - "user_settings": "", "user_settings_description": "Lietotāju iestatÄĢjumu pārvaldÄĢba", "version_check_enabled_description": "Ieslēgt versijas pārbaudi", "version_check_implications": "Versiju pārbaudes funkcija ir atkarÄĢga no periodiskas saziņas ar github.com", - "version_check_settings": "Versijas pārbaude", - "version_check_settings_description": "", - "video_conversion_job_description": "" + "version_check_settings": "Versijas pārbaude" }, "admin_email": "Administratora e-pasts", "admin_password": "Administratora parole", "administration": "AdministrÄ“ÅĄana", "advanced": "Papildu", - "advanced_settings_log_level_title": "ÅŊurnalÄ“ÅĄanas lÄĢmenis: {}", + "advanced_settings_log_level_title": "ÅŊurnalÄ“ÅĄanas lÄĢmenis: {level}", "advanced_settings_prefer_remote_subtitle": "DaŞās ierÄĢcēs sÄĢktēli no ierÄĢcē esoÅĄajiem resursiem tiek ielādēti Äŧoti lēni. Aktivizējiet ÅĄo iestatÄĢjumu, lai tā vietā ielādētu attālus attēlus.", "advanced_settings_prefer_remote_title": "Dot priekÅĄroku attāliem attēliem", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_proxy_headers_title": "Starpniekservera galvenes", "advanced_settings_self_signed_ssl_subtitle": "IzlaiÅž servera galapunkta SSL sertifikātu verifikāciju. NepiecieÅĄams paÅĄparakstÄĢtajiem sertifikātiem.", "advanced_settings_self_signed_ssl_title": "AtÄŧaut paÅĄparakstÄĢtus SSL sertifikātus", "advanced_settings_tile_subtitle": "Lietotāja papildu iestatÄĢjumi", "advanced_settings_troubleshooting_subtitle": "Iespējot papildu aktÄĢvus problēmu novērÅĄanai", "advanced_settings_troubleshooting_title": "Problēmas novērÅĄana", + "age_months": "Vecums {months, plural, zero {# mēneÅĄu} one {# mēnesis} other {# mēneÅĄi}}", + "age_year_months": "Vecums 1 gads, {months, plural, zero {# mēneÅĄu} one {# mēnesis} other {# mēneÅĄi}}", + "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", - "album_added_notification_setting_description": "", "album_cover_updated": "Albuma attēls atjaunināts", "album_info_card_backup_album_excluded": "NEIEKÄģAUTS", "album_info_card_backup_album_included": "IEKÄģAUTS", "album_info_updated": "Albuma informācija atjaunināta", "album_leave": "Pamest albumu?", "album_name": "Albuma nosaukums", - "album_options": "", "album_remove_user": "Noņemt lietotāju?", "album_thumbnail_card_item": "1 vienums", - "album_thumbnail_card_items": "{} vienumi", - "album_thumbnail_card_shared": "¡ Koplietots", - "album_thumbnail_shared_by": "KopÄĢgoja {}", + "album_thumbnail_card_items": "{count} vienumi", + "album_thumbnail_card_shared": " ¡ KopÄĢgots", + "album_thumbnail_shared_by": "KopÄĢgoja {user}", "album_updated": "Albums atjaunināts", - "album_updated_setting_description": "", "album_user_left": "Pameta {album}", "album_user_removed": "Noņēma {user}", "album_viewer_appbar_delete_confirm": "Vai tieÅĄÄm vēlaties dzēst ÅĄo albumu no sava konta?", @@ -331,18 +205,15 @@ "app_bar_signout_dialog_content": "Vai tieÅĄÄm vēlaties izrakstÄĢties?", "app_bar_signout_dialog_ok": "Jā", "app_bar_signout_dialog_title": "IzrakstÄĢties", - "app_settings": "", - "appears_in": "", "archive": "ArhÄĢvs", - "archive_or_unarchive_photo": "", "archive_page_no_archived_assets": "Nav atrasts neviens arhivēts aktÄĢvs", - "archive_page_title": "ArhÄĢvs ({})", + "archive_page_title": "ArhÄĢvs ({count})", "archive_size": "ArhÄĢva izmērs", - "archived": "Archived", + "archived": "Arhivēts", "are_these_the_same_person": "Vai ÅĄÄĢ ir tā pati persona?", "asset_action_delete_err_read_only": "Nevar dzēst read only aktÄĢvu(-s), notiek izlaiÅĄana", "asset_action_share_err_offline": "Nevar iegÅĢt bezsaistes aktÄĢvu(-s), notiek izlaiÅĄana", - "asset_adding_to_album": "Pievieno albumam...", + "asset_adding_to_album": "Pievieno albumamâ€Ļ", "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", @@ -351,25 +222,13 @@ "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "FotoreÅžÄŖa izkārtojuma iestatÄĢjumi", "asset_list_settings_title": "FotoreÅžÄŖis", - "asset_offline": "", - "asset_restored_successfully": "Asset restored successfully", - "asset_uploading": "AugÅĄupielādē...", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", + "asset_uploading": "AugÅĄupielādēâ€Ļ", "asset_viewer_settings_title": "AktÄĢvu SkatÄĢtājs", "assets": "aktÄĢvi", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "authorized_devices": "Autorizētās ierÄĢces", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_title": "Automātiska URL pārslēgÅĄana", "back": "AtpakaÄŧ", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albumi ierÄĢcē ({})", + "backup_album_selection_page_albums_device": "Albumi ierÄĢcē ({count})", "backup_album_selection_page_albums_tap": "Pieskarieties, lai iekÄŧautu, veiciet dubultskārienu, lai izslēgtu", "backup_album_selection_page_assets_scatter": "AktÄĢvi var bÅĢt izmētāti pa vairākiem albumiem. Tādējādi dublÄ“ÅĄanas procesā albumus var iekÄŧaut vai neiekÄŧaut.", "backup_album_selection_page_select_albums": "AtlasÄĢt albumus", @@ -378,22 +237,21 @@ "backup_all": "Viss", "backup_background_service_backup_failed_message": "Neizdevās dublēt lÄĢdzekÄŧus. Notiek atkārtota mēĪinÄÅĄanaâ€Ļ", "backup_background_service_connection_failed_message": "Neizdevās izveidot savienojumu ar serveri. Notiek atkārtota mēĪinÄÅĄanaâ€Ļ", - "backup_background_service_current_upload_notification": "Notiek {} augÅĄupielāde", + "backup_background_service_current_upload_notification": "Notiek {filename} augÅĄupielāde", "backup_background_service_default_notification": "Notiek jaunu aktÄĢvu meklÄ“ÅĄanaâ€Ļ", "backup_background_service_error_title": "DublÄ“ÅĄanas kÄŧÅĢda", "backup_background_service_in_progress_notification": "Notiek aktÄĢvu dublÄ“ÅĄanaâ€Ļ", - "backup_background_service_upload_failure_notification": "Neizdevās augÅĄupielādēt {}", + "backup_background_service_upload_failure_notification": "Neizdevās augÅĄupielādēt {filename}", "backup_controller_page_albums": "Dublējuma Albumi", "backup_controller_page_background_app_refresh_disabled_content": "Iespējojiet fona aplikācijas atsvaidzinÄÅĄanu sadaÄŧā IestatÄĢjumi > VispārÄĢgi > Fona Aplikācijas AtsvaidzinÄÅĄana, lai izmantotu fona dublÄ“ÅĄanu.", "backup_controller_page_background_app_refresh_disabled_title": "Fona aplikācijas atsvaidzinÄÅĄana atspējota", "backup_controller_page_background_app_refresh_enable_button_text": "Doties uz iestatÄĢjumiem", "backup_controller_page_background_battery_info_link": "ParādÄĢt, kā", "backup_controller_page_background_battery_info_message": "Lai iegÅĢtu vislabāko fona dublÄ“ÅĄanas pieredzi, lÅĢdzu, atspējojiet visas akumulatora optimizācijas, kas ierobeÅžo Immich fona aktivitāti.\n\nTā kā katrai ierÄĢcei iestatÄĢjumi ir citādāki, lÅĢdzu, meklējiet nepiecieÅĄamo informāciju pie ierÄĢces raÅžotāja.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Akumulatora optimizācija", "backup_controller_page_background_charging": "Tikai uzlādes laikā", "backup_controller_page_background_configure_error": "Neizdevās konfigurēt fona pakalpojumu", - "backup_controller_page_background_delay": "Aizkavēt jaunu lÄĢdzekÄŧu dublÄ“ÅĄanu: {}", + "backup_controller_page_background_delay": "Aizkavēt jaunu lÄĢdzekÄŧu dublÄ“ÅĄanu: {duration}", "backup_controller_page_background_description": "Ieslēdziet fona pakalpojumu, lai automātiski dublētu visus jaunos aktÄĢvus, neatverot programmu", "backup_controller_page_background_is_off": "Automātiskā fona dublÄ“ÅĄana ir izslēgta", "backup_controller_page_background_is_on": "Automātiskā fona dublÄ“ÅĄana ir ieslēgta", @@ -401,14 +259,13 @@ "backup_controller_page_background_turn_on": "Ieslēgt fona pakalpojumu", "backup_controller_page_background_wifi": "Tikai WiFi tÄĢklā", "backup_controller_page_backup": "DublÄ“ÅĄana", - "backup_controller_page_backup_selected": "AtlasÄĢts:", + "backup_controller_page_backup_selected": "AtlasÄĢts: ", "backup_controller_page_backup_sub": "Dublētie Fotoattēli un videoklipi", - "backup_controller_page_created": "Izveidots: {}", + "backup_controller_page_created": "Izveidots: {date}", "backup_controller_page_desc_backup": "Ieslēdziet priekÅĄplāna dublÄ“ÅĄanu, lai, atverot programmu, serverÄĢ automātiski augÅĄupielādētu jaunus aktÄĢvus.", - "backup_controller_page_excluded": "Izņemot:", - "backup_controller_page_failed": "Neizdevās ({})", - "backup_controller_page_filename": "Faila nosaukums: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Izņemot: ", + "backup_controller_page_failed": "Neizdevās ({count})", + "backup_controller_page_filename": "Faila nosaukums: {filename} [{size}]", "backup_controller_page_info": "Dublējuma Informācija", "backup_controller_page_none_selected": "Neviens nav atlasÄĢts", "backup_controller_page_remainder": "Atlikums", @@ -417,7 +274,7 @@ "backup_controller_page_start_backup": "Sākt DublÄ“ÅĄanu", "backup_controller_page_status_off": "Automātiskā priekÅĄplāna dublÄ“ÅĄana ir izslēgta", "backup_controller_page_status_on": "Automātiskā priekÅĄplāna dublÄ“ÅĄana ir ieslēgta", - "backup_controller_page_storage_format": "{} no {} tiek izmantots", + "backup_controller_page_storage_format": "{used} no {total} tiek izmantots", "backup_controller_page_to_backup": "Dublējamie albumi", "backup_controller_page_total_sub": "Visi unikālie fotoattēli un videoklipi no izvēlētajiem albumiem", "backup_controller_page_turn_off": "Izslēgt priekÅĄplāna dublÄ“ÅĄanu", @@ -430,42 +287,32 @@ "backup_manual_success": "VeiksmÄĢgi", "backup_manual_title": "AugÅĄupielādes statuss", "backup_options_page_title": "DublÄ“ÅĄanas iestatÄĢjumi", - "backup_setting_subtitle": "Manage background and foreground upload settings", - "backward": "", "birthdate_saved": "DzimÅĄanas datums veiksmÄĢgi saglabāts", "birthdate_set_description": "DzimÅĄanas datums tiek izmantots, lai aprēķinātu ÅĄÄĢs personas vecumu fotogrāfijas uzņemÅĄanas brÄĢdÄĢ.", - "blurred_background": "", "bugs_and_feature_requests": "KÄŧÅĢdas un funkciju pieprasÄĢjumi", "build": "BÅĢvējums", "build_image": "BÅĢvējuma attēls", - "cache_settings_album_thumbnails": "Bibliotēkas lapu sÄĢktēli ({} aktÄĢvi)", + "cache_settings_album_thumbnails": "Bibliotēkas lapu sÄĢktēli ({count} faili)", "cache_settings_clear_cache_button": "IztÄĢrÄĢt keÅĄatmiņu", "cache_settings_clear_cache_button_title": "IztÄĢra aplikācijas keÅĄatmiņu. Tas bÅĢtiski ietekmēs lietotnes veiktspēju, lÄĢdz keÅĄatmiņa bÅĢs pārbÅĢvēta.", "cache_settings_duplicated_assets_clear_button": "NOTÄĒRÄĒT", "cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekÄŧāvusi melnajā sarakstā", - "cache_settings_duplicated_assets_title": "Dublicētie AktÄĢvi ({})", - "cache_settings_image_cache_size": "Attēlu keÅĄatmiņas lielums ({} aktÄĢvi)", + "cache_settings_duplicated_assets_title": "Dublicētie faili ({count})", + "cache_settings_image_cache_size": "Attēlu keÅĄatmiņas lielums ({count} faili)", "cache_settings_statistics_album": "Bibliotēkas sÄĢktēli", - "cache_settings_statistics_assets": "{} aktÄĢvi ({})", + "cache_settings_statistics_assets": "{count} faili ({size})", "cache_settings_statistics_full": "Pilni attēli", "cache_settings_statistics_shared": "Koplietojamo albumu sÄĢktēli", "cache_settings_statistics_thumbnail": "SÄĢktēli", "cache_settings_statistics_title": "KeÅĄatmiņas lietojums", "cache_settings_subtitle": "Kontrolēt Immich mobilās lietotnes keÅĄdarbi", - "cache_settings_thumbnail_size": "SÄĢktēlu keÅĄa lielums ({} aktÄĢvi)", + "cache_settings_thumbnail_size": "SÄĢktēlu keÅĄatmiņas izmērs ({count} faili)", "cache_settings_tile_subtitle": "Kontrolēt lokālās krātuves uzvedÄĢbu", "cache_settings_tile_title": "Lokālā Krātuve", "cache_settings_title": "KeÅĄdarbes iestatÄĢjumi", - "camera": "", - "camera_brand": "", - "camera_model": "", "cancel": "Atcelt", - "cancel_search": "", - "canceled": "Canceled", "cannot_merge_people": "Nevar apvienot cilvēkus", - "cannot_update_the_description": "", "change_date": "MainÄĢt datumu", - "change_display_order": "Change display order", "change_expiration_time": "IzmainÄĢt derÄĢguma termiņu", "change_location": "MainÄĢt atraÅĄanās vietu", "change_name": "MainÄĢt nosaukumu", @@ -476,26 +323,12 @@ "change_password_form_new_password": "Jauna Parole", "change_password_form_password_mismatch": "Paroles nesakrÄĢt", "change_password_form_reenter_new_password": "Atkārtoti ievadÄĢt jaunu paroli", - "change_your_password": "", - "changed_visibility_successfully": "", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", - "check_logs": "", + "change_pin_code": "NomainÄĢt PIN kodu", "choose_matching_people_to_merge": "Izvēlies atbilstoÅĄus cilvēkus apvienoÅĄanai", "city": "Pilsēta", "clear": "NotÄĢrÄĢt", "clear_all": "NotÄĢrÄĢt visu", - "clear_message": "", "clear_value": "NotÄĢrÄĢt vērtÄĢbu", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "clockwise": "PulksteņrādÄĢtāja virzienā", "close": "Aizvērt", "collapse": "SakÄŧaut", @@ -503,68 +336,45 @@ "color": "Krāsa", "color_theme": "Krāsu tēma", "comment_deleted": "Komentārs dzēsts", - "comment_options": "", - "comments_are_disabled": "", "common_create_new_album": "Izveidot jaunu albumu", "common_server_error": "LÅĢdzu, pārbaudiet tÄĢkla savienojumu, pārliecinieties, vai serveris ir sasniedzams un aplikācijas/servera versijas ir saderÄĢgas.", - "completed": "Completed", "confirm": "Apstiprināt", - "confirm_admin_password": "", + "confirm_new_pin_code": "Apstiprināt jauno PIN kodu", "confirm_password": "Apstiprināt paroli", - "contain": "", "context": "Konteksts", "continue": "Turpināt", - "control_bottom_app_bar_album_info_shared": "{} vienumi ¡ Koplietoti", + "control_bottom_app_bar_album_info_shared": "{count} vienumi ¡ Koplietoti", "control_bottom_app_bar_create_new_album": "Izveidot jaunu albumu", "control_bottom_app_bar_delete_from_immich": "Dzēst no Immich", "control_bottom_app_bar_delete_from_local": "Dzēst no ierÄĢces", "control_bottom_app_bar_edit_location": "RediÄŖÄ“t AtraÅĄanās Vietu", "control_bottom_app_bar_edit_time": "RediÄŖÄ“t Datumu un Laiku", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "KopÄĢgot Uz", "control_bottom_app_bar_trash_from_immich": "Pārvietot uz Atkritni", - "copied_image_to_clipboard": "", - "copy_error": "", - "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", + "copy_error": "KopÄ“ÅĄanas kÄŧÅĢda", "country": "Valsts", - "cover": "", - "covers": "", "create": "Izveidot", "create_album": "Izveidot albumu", "create_album_page_untitled": "Bez nosaukuma", - "create_library": "", + "create_library": "Izveidot bibliotēku", "create_link": "Izveidot saiti", "create_link_to_share": "Izveidot kopÄĢgoÅĄanas saiti", - "create_new": "CREATE NEW", - "create_new_person": "", + "create_new_person": "Izveidot jaunu personu", "create_new_user": "Izveidot jaunu lietotāju", "create_shared_album_page_share_add_assets": "PIEVIENOT AKTÄĒVUS", "create_shared_album_page_share_select_photos": "Fotoattēlu Izvēle", "create_user": "Izveidot lietotāju", - "created": "", - "crop": "Crop", "curated_object_page_title": "Lietas", - "current_device": "", - "current_server_address": "Current server address", - "custom_locale": "", - "custom_locale_description": "", - "daily_title_text_date": "E, MMM dd", + "current_pin_code": "EsoÅĄais PIN kods", "daily_title_text_date_year": "E, MMM dd, gggg", - "dark": "", - "date_after": "", + "date_after": "Datums pēc", "date_and_time": "Datums un Laiks", - "date_before": "", + "date_before": "Datums pirms", "date_format": "E, LLL d, g â€ĸ h:mm a", "date_of_birth_saved": "DzimÅĄanas datums veiksmÄĢgi saglabāts", "date_range": "Datumu diapazons", - "day": "", - "default_locale": "", - "default_locale_description": "", + "day": "Diena", + "deduplication_criteria_1": "Attēla izmērs baitos", "delete": "Dzēst", "delete_album": "Dzēst albumu", "delete_dialog_alert": "Å ie vienumi tiks neatgriezeniski dzēsti no Immich un jÅĢsu ierÄĢces", @@ -573,145 +383,75 @@ "delete_dialog_alert_remote": "Å ie vienumi tiks neatgriezeniski dzēsti no Immich servera.", "delete_dialog_ok_force": "Tā pat dzēst", "delete_dialog_title": "Neatgriezeniski Dzēst", - "delete_key": "", - "delete_library": "", - "delete_link": "", + "delete_face": "Dzēst seju", + "delete_key": "Dzēst atslēgu", + "delete_library": "Dzēst bibliotēku", + "delete_link": "Dzēst saiti", "delete_local_dialog_ok_backed_up_only": "Dzēst tikai Dublētos", "delete_local_dialog_ok_force": "Tā pat dzēst", + "delete_others": "Dzēst citus", "delete_shared_link": "Dzēst KopÄĢgoÅĄanas saiti", "delete_shared_link_dialog_title": "Dzēst KopÄĢgoÅĄanas saiti", "delete_user": "Dzēst lietotāju", - "deleted_shared_link": "", + "deleted_shared_link": "Dzēst kopÄĢgoto saiti", "description": "Apraksts", "description_input_hint_text": "Pievienot aprakstu...", "description_input_submit_error": "Atjauninot aprakstu, radās kÄŧÅĢda; papildinformāciju skatiet Åžurnālā", "details": "INFORMĀCIJA", "direction": "Virziens", - "disallow_edits": "", - "discover": "", - "dismiss_all_errors": "", - "dismiss_error": "", - "display_options": "", - "display_order": "", - "display_original_photos": "", - "display_original_photos_setting_description": "", + "display_order": "AttēloÅĄanas secÄĢba", "documentation": "Dokumentācija", "done": "Gatavs", "download": "Lejupielādēt", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_notfound": "Download not found", - "download_paused": "Download paused", + "download_filename": "fails: {filename}", "download_settings": "Lejupielāde", "download_settings_description": "Ar failu lejupielādi saistÄĢto iestatÄĢjumu pārvaldÄĢba", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", - "downloading": "", - "downloading_media": "Downloading media", + "downloading": "Lejupielādē", + "downloading_asset_filename": "Lejupielādē failu {filename}", "duplicates": "Dublikāti", - "duration": "", - "edit_album": "", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", - "edit_key": "", + "edit": "Labot", + "edit_album": "Labot albumu", + "edit_date": "Labot datumu", + "edit_date_and_time": "Labot datumu un laiku", + "edit_faces": "Labot sejas", + "edit_import_path": "Labot importa ceÄŧu", + "edit_import_paths": "Labot importa ceÄŧus", + "edit_key": "Labot atslēgu", "edit_link": "RediÄŖÄ“t saiti", "edit_location": "RediÄŖÄ“t AtraÅĄanās Vietu", "edit_location_dialog_title": "AtraÅĄanās vieta", "edit_name": "RediÄŖÄ“t vārdu", - "edit_people": "", - "edit_title": "", + "edit_people": "Labot profilu", + "edit_title": "Labot nosaukumu", "edit_user": "Labot lietotāju", - "edited": "", - "editor": "", + "edited": "Labots", + "editor": "Redaktors", "editor_close_without_save_prompt": "Izmaiņas netiks saglabātas", "editor_close_without_save_title": "Aizvērt redaktoru?", "email": "E-pasts", - "empty_folder": "This folder is empty", + "email_notifications": "E-pasta paziņojumi", "empty_trash": "IztukÅĄot atkritni", - "enable": "", - "enabled": "", - "end_date": "", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", - "error": "", - "error_change_sort_album": "Failed to change album sort order", - "error_loading_image": "", - "error_saving_image": "Error: {}", + "error_saving_image": "KÄŧÅĢda: {error}", "errors": { "cant_get_faces": "Nevar iegÅĢt sejas", "cant_search_people": "Neizdevās veikt peronu meklÄ“ÅĄanu", "failed_to_create_album": "Neizdevās izveidot albumu", - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_create_admin_account": "", - "unable_to_create_library": "", "unable_to_create_user": "Neizdevās izveidot lietotāju", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", "unable_to_delete_user": "Neizdevās dzēst lietotāju", - "unable_to_empty_trash": "", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", "unable_to_hide_person": "Neizdevās paslēpt personu", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimÅĄanas datumu", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_user": "" + "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimÅĄanas datumu" }, "exif_bottom_sheet_description": "Pievienot Aprakstu...", "exif_bottom_sheet_details": "INFORMĀCIJA", "exif_bottom_sheet_location": "ATRAÅ ANĀS VIETA", "exif_bottom_sheet_people": "CILVĒKI", "exif_bottom_sheet_person_add_person": "Pievienot vārdu", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Vecums {age}", + "exif_bottom_sheet_person_age_months": "Vecums {months} mēneÅĄi", + "exif_bottom_sheet_person_age_year_months": "Vecums 1 gads, {months} mēneÅĄi", + "exif_bottom_sheet_person_age_years": "Vecums {years}", "exit_slideshow": "Iziet no slÄĢdrādes", - "expand_all": "", "experimental_settings_new_asset_list_subtitle": "Izstrādes posmā", "experimental_settings_new_asset_list_title": "Iespējot eksperimentālo fotoreÅžÄŖi", "experimental_settings_subtitle": "Izmanto uzņemoties risku!", @@ -719,50 +459,16 @@ "expire_after": "DerÄĢguma termiÅ†ÅĄ beidzas pēc", "expired": "DerÄĢguma termiÅ†ÅĄ beidzās", "explore": "IzpētÄĢt", - "extension": "", - "external_libraries": "", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", - "failed": "Failed", - "failed_to_load_assets": "Failed to load assets", - "failed_to_load_folder": "Failed to load folder", "favorite": "Izlase", - "favorite_or_unfavorite_photo": "", "favorites": "Izlase", "favorites_page_no_favorites": "Nav atrasti iecienÄĢtākie aktÄĢvi", - "feature_photo_updated": "", "features_setting_description": "Lietotnes funkciju pārvaldÄĢba", - "file_name": "", - "file_name_or_extension": "", - "filename": "", - "filetype": "", - "filter": "Filter", - "filter_people": "", - "fix_incorrect_match": "", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Mapes", - "forward": "", - "general": "", - "get_help": "", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "grant_permission": "Grant permission", - "group_albums_by": "", "haptic_feedback_switch": "IestatÄĢt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", - "hide_gallery": "", "hide_named_person": "Paslēpt personu {name}", - "hide_password": "", "hide_person": "Paslēpt personu", "home_page_add_to_album_conflicts": "Pievienoja {added} aktÄĢvus albumam {album}. {failed} aktÄĢvi jau ir albumā.", "home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktÄĢvus, notiek izlaiÅĄana", @@ -778,12 +484,7 @@ "home_page_first_time_notice": "Ja ÅĄÄĢ ir pirmā reize, kad izmantojat aplikāciju, lÅĢdzu, izvēlieties dublējuma albumu(s), lai laika skala varētu aizpildÄĢt fotoattēlus un videoklipus albumā(os).", "home_page_share_err_local": "Caur saiti nevarēja kopÄĢgot lokālos aktÄĢvus, notiek izlaiÅĄana", "home_page_upload_err_limit": "Vienlaikus var augÅĄupielādēt ne vairāk kā 30 aktÄĢvus, notiek izlaiÅĄana", - "host": "", - "hour": "", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Attēls", - "image_saved_successfully": "Image saved", "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde Izdevās", "image_viewer_page_state_provider_share_error": "KopÄĢgoÅĄanas KÄŧÅĢda", @@ -794,17 +495,12 @@ "in_archive": "ArhÄĢvā", "include_archived": "IekÄŧaut arhivētos", "include_shared_albums": "IekÄŧaut koplietotos albumus", - "include_shared_partner_assets": "", - "individual_share": "", "info": "Informācija", "interval": { "day_at_onepm": "Katru dienu 13.00", - "hours": "", "night_at_midnight": "Katru dienu pusnaktÄĢ", "night_at_twoam": "Katru dienu 2.00 naktÄĢ" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "IelÅĢgt cilvēkus", "invite_to_album": "Uzaicināt albumā", "jobs": "Uzdevumi", @@ -820,23 +516,14 @@ "let_others_respond": "Äģaut citiem atbildēt", "level": "LÄĢmenis", "library": "Bibliotēka", - "library_options": "", "library_page_device_albums": "Albumi ierÄĢcē", "library_page_new_album": "Jauns albums", "library_page_sort_asset_count": "Daudzums ar aktÄĢviem", "library_page_sort_created": "Jaunākais izveidotais", "library_page_sort_last_modified": "Pēdējo reizi modificēts", "library_page_sort_title": "Albuma virsraksts", - "light": "", - "link_options": "", - "link_to_oauth": "", - "linked_oauth_account": "", "list": "Saraksts", "loading": "Ielādē", - "loading_search_results_failed": "", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", "location_picker_choose_on_map": "Izvēlēties uz kartes", "location_picker_latitude_error": "Ievadiet korektu ÄŖeogrāfisko platumu", @@ -844,7 +531,6 @@ "location_picker_longitude_error": "Ievadiet korektu ÄŖeogrāfisko garumu", "location_picker_longitude_hint": "Ievadiet savu ÄŖeogrāfisko garumu ÅĄeit", "log_out": "IzrakstÄĢties", - "log_out_all_devices": "", "login_disabled": "PieslēgÅĄanās ir atslēgta", "login_form_api_exception": "API izņēmums. LÅĢdzu, pārbaudiet servera URL un mēĪiniet vēlreiz.", "login_form_back_button_text": "AtpakaÄŧ", @@ -864,12 +550,10 @@ "login_form_save_login": "Palikt pieteiktam", "login_form_server_empty": "Ieraksties servera URL.", "login_form_server_error": "Nevarēja izveidot savienojumu ar serveri.", - "login_has_been_disabled": "", "login_password_changed_error": "Atjaunojot paroli radās kÄŧÅĢda", "login_password_changed_success": "Parole veiksmÄĢgi atjaunota", "longitude": "Äĸeogrāfiskais garums", "look": "Izskats", - "loop_videos": "", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaÄŧu skatÄĢtājā.", "make": "Firma", "manage_shared_links": "KopÄĢgoto saiÅĄu pārvaldÄĢba", @@ -880,8 +564,8 @@ "manage_your_devices": "Pieslēgto ierÄĢču pārvaldÄĢba", "manage_your_oauth_connection": "OAuth savienojumu pārvaldÄĢba", "map": "Karte", - "map_assets_in_bound": "{} fotoattēls", - "map_assets_in_bounds": "{} fotoattēli", + "map_assets_in_bound": "{count} fotoattēls", + "map_assets_in_bounds": "{count} fotoattēli", "map_cannot_get_user_location": "Nevar iegÅĢt lietotāja atraÅĄanās vietu", "map_location_dialog_yes": "Jā", "map_location_picker_page_use_location": "Izvēlēties ÅĄo atraÅĄanās vietu", @@ -895,9 +579,9 @@ "map_settings": "Kartes IestatÄĢjumi", "map_settings_dark_mode": "TumÅĄais reÅžÄĢms", "map_settings_date_range_option_day": "Pēdējās 24 stundas", - "map_settings_date_range_option_days": "Pēdējās {} dienas", + "map_settings_date_range_option_days": "Pēdējās {days} dienas", "map_settings_date_range_option_year": "Pēdējo gadu", - "map_settings_date_range_option_years": "Pēdējos {} gadus", + "map_settings_date_range_option_years": "Pēdējie {years} gadi", "map_settings_dialog_title": "Kartes IestatÄĢjumi", "map_settings_include_show_archived": "IekÄŧaut Arhivētos", "map_settings_include_show_partners": "IekÄŧaut Partnerus", @@ -909,11 +593,8 @@ "memories": "Atmiņas", "memories_all_caught_up": "Å obrÄĢd, tas arÄĢ viss", "memories_check_back_tomorrow": "PriekÅĄ vairāk atmiņām atgriezieties rÄĢtdien.", - "memories_setting_description": "", "memories_start_over": "Sākt no jauna", "memories_swipe_to_close": "Pavelciet uz augÅĄu, lai aizvērtu", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Atmiņa", "menu": "Izvēlne", "merge": "Apvienot", @@ -928,41 +609,35 @@ "month": "Mēnesis", "monthly_title_text_date_format": "MMMM g", "more": "Vairāk", + "moved_to_library": "Pārvietoja {count, plural, one {# failu} other {# failus}} uz bibliotēku", "moved_to_trash": "Pārvietots uz atkritni", "multiselect_grid_edit_date_time_err_read_only": "Nevar rediÄŖÄ“t read only aktÄĢva(-u) datumu, notiek izlaiÅĄana", "multiselect_grid_edit_gps_err_read_only": "Nevar rediÄŖÄ“t atraÅĄanās vietu read only aktÄĢva(-u) datumu, notiek izlaiÅĄana", "my_albums": "Mani albumi", "name": "Vārds", "name_or_nickname": "Vārds vai iesauka", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "nekad", "new_album": "Jauns albums", "new_api_key": "Jauna API atslēga", "new_password": "Jaunā parole", "new_person": "Jauna persona", + "new_pin_code": "Jaunais PIN kods", "new_user_created": "Izveidots jauns lietotājs", "new_version_available": "PIEEJAMA JAUNA VERSIJA", - "newest_first": "", "next": "NākoÅĄais", "next_memory": "Nākamā atmiņa", "no": "Nē", - "no_albums_message": "", - "no_archived_assets_message": "", + "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", "no_assets_message": "NOKLIKÅ ÄļINIET, LAI AUGÅ UPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktÄĢvu", "no_duplicates_found": "Dublikāti netika atrasti.", "no_exif_info_available": "Nav pieejama exif informācija", - "no_explore_results_message": "", - "no_favorites_message": "", - "no_libraries_message": "", "no_name": "Nav nosaukuma", + "no_notifications": "Nav paziņojumu", "no_places": "Nav atraÅĄanās vietu", "no_results": "Nav rezultātu", "no_results_description": "IzmēĪiniet sinonÄĢmu vai vispārÄĢgāku atslēgvārdu", - "no_shared_albums_message": "", "not_in_any_album": "Nav nevienā albumā", - "not_selected": "Not selected", "notes": "PiezÄĢmes", "notification_permission_dialog_content": "Lai iespējotu paziņojumus, atveriet IestatÄĢjumi un atlasiet AtÄŧaut.", "notification_permission_list_tile_content": "PieÅĄÄˇirt atÄŧauju, lai iespējotu paziņojumus.", @@ -971,12 +646,9 @@ "notification_toggle_setting_description": "Ieslēgt e-pasta paziņojumus", "notifications": "Paziņojumi", "notifications_setting_description": "Paziņojumu pārvaldÄĢba", - "oauth": "OAuth", "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", "ok": "Labi", - "oldest_first": "", - "on_this_device": "On this device", "online": "TieÅĄsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -984,12 +656,13 @@ "open_the_search_filters": "Atvērt meklÄ“ÅĄanas filtrus", "options": "IestatÄĢjumi", "or": "vai", - "organize_your_library": "", + "original": "oriÄŖināls", "other": "Citi", "other_devices": "Citas ierÄĢces", "other_variables": "Citi mainÄĢgie", "owned": "ÄĒpaÅĄumā", "owner": "ÄĒpaÅĄnieks", + "partner_can_access": "{partner} var piekÄŧÅĢt", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "ApskatÄĢt visu", "partner_page_empty_message": "JÅĢsu fotogrāfijas pagaidām nav kopÄĢgotas ar nevienu partneri.", @@ -997,30 +670,12 @@ "partner_page_partner_add_failed": "Neizdevās pievienot partneri", "partner_page_select_partner": "Izvēlēties partneri", "partner_page_shared_to_title": "KopÄĢgots uz", - "partner_page_stop_sharing_content": "{} vairs nevarēs piekÄŧÅĢt jÅĢsu fotoattēliem.", - "partner_sharing": "", - "partners": "", + "partner_page_stop_sharing_content": "{partner} vairs nevarēs piekÄŧÅĢt jÅĢsu fotoattēliem.", + "partners": "Partneri", "password": "Parole", "password_does_not_match": "Parole nesakrÄĢt", - "password_required": "", - "password_reset_success": "", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, - "path": "", - "pattern": "", - "pause": "", - "pause_memories": "", - "paused": "", - "pending": "", + "path": "CeÄŧÅĄ", "people": "Cilvēki", - "people_sidebar_description": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", - "permanently_delete": "", - "permanently_deleted_asset": "", "permission_onboarding_back": "AtpakaÄŧ", "permission_onboarding_continue_anyway": "Tomēr turpināt", "permission_onboarding_get_started": "Darba sākÅĄana", @@ -1029,33 +684,21 @@ "permission_onboarding_permission_granted": "AtÄŧauja pieÅĄÄˇirta! JÅĢs esat gatavi darbam.", "permission_onboarding_permission_limited": "AtÄŧauja ierobeÅžota. Lai atÄŧautu Immich dublÄ“ÅĄanu un varētu pārvaldÄĢt visu galeriju kolekciju, sadaÄŧā IestatÄĢjumi pieÅĄÄˇiriet fotoattēlu un video atÄŧaujas.", "permission_onboarding_request": "Immich nepiecieÅĄama atÄŧauja skatÄĢt jÅĢsu fotoattēlus un videoklipus.", + "person": "Persona", "photos": "Fotoattēli", - "photos_from_previous_years": "", - "pick_a_location": "", - "place": "", + "photos_from_previous_years": "Fotogrāfijas no iepriekÅĄÄ“jiem gadiem", "places": "Vietas", - "play": "", - "play_memories": "", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preferences_settings_subtitle": "Manage the app's preferences", + "port": "Ports", "preferences_settings_title": "IestatÄĢjumi", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", + "preview": "PriekÅĄskatÄĢjums", + "privacy": "Privātums", + "profile": "Profils", "profile_drawer_app_logs": "ÅŊurnāli", "profile_drawer_client_out_of_date_major": "Mobilā Aplikācija ir novecojusi. LÅĢdzu atjaunojiet to uz jaunāko lielo versiju", "profile_drawer_client_out_of_date_minor": "Mobilā Aplikācija ir novecojusi. LÅĢdzu atjaunojiet to uz jaunāko mazo versiju", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serveris ir novecojis. LÅĢdzu atjaunojiet to uz jaunāko lielo versiju", "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. LÅĢdzu atjaunojiet to uz jaunāko mazo versiju", - "profile_picture_set": "", - "public_share": "", "purchase_button_never_show_again": "Nekad vairs nerādÄĢt", "purchase_button_reminder": "Atgādināt man pēc 30 dienām", "purchase_button_remove_key": "Noņemt atslēgu", @@ -1073,33 +716,19 @@ "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", "rating_clear": "Noņemt vērtējumu", - "reaction_options": "", "read_changelog": "LasÄĢt izmaiņu sarakstu", - "recent": "", - "recent_searches": "", - "recently_added": "Recently added", "recently_added_page_title": "Nesen Pievienotais", - "refresh": "", - "refreshed": "", - "refreshes_every_file": "", "remove": "Noņemt", - "remove_deleted_assets": "", "remove_from_album": "Noņemt no albuma", "remove_from_favorites": "Noņemt no izlases", - "remove_from_shared_link": "", "remove_user": "Noņemt lietotāju", "removed_api_key": "Noņēma API atslēgu: {name}", "removed_from_archive": "Noņēma no arhÄĢva", "removed_from_favorites": "Noņēma no izlases", "rename": "Pārsaukt", "repair": "Remonts", - "repair_no_results_message": "", "replace_with_upload": "Aizstāt ar augÅĄupielādi", - "require_password": "", "require_user_to_change_password_on_first_login": "PieprasÄĢt lietotājam mainÄĢt paroli pēc pirmās pieteikÅĄanās", - "reset": "", - "reset_password": "", - "reset_people_visibility": "", "resolve_duplicates": "Atrisināt dublÄ“ÅĄanās gadÄĢjumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", @@ -1112,115 +741,70 @@ "role_editor": "Redaktors", "role_viewer": "SkatÄĢtājs", "save": "Saglabāt", - "save_to_gallery": "Save to gallery", "saved_api_key": "API atslēga saglabāta", "saved_profile": "Profils saglabāts", "saved_settings": "IestatÄĢjumi saglabāti", "say_something": "Teikt kaut ko", "scaffold_body_error_occurred": "Radās kÄŧÅĢda", - "scan_all_libraries": "", - "scan_settings": "", "search": "Meklēt", "search_albums": "Meklēt albumus", - "search_by_context": "", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", "search_filter_apply": "Lietot filtru", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", "search_filter_display_option_not_in_album": "Nav albumā", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", - "search_for_existing_person": "", - "search_no_more_result": "No more results", "search_no_people": "Nav cilvēku", "search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_page_categories": "Kategorijas", "search_page_motion_photos": "KustÄĢbu Fotoattēli", "search_page_no_objects": "Informācija par Objektiem nav pieejama", "search_page_no_places": "Nav pieejama Informācija par Vietām", "search_page_screenshots": "Ekrānuzņēmumi", - "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": "Selfiji", "search_page_things": "Lietas", "search_page_view_all_button": "ApskatÄĢt visu", "search_page_your_activity": "JÅĢsu aktivitāte", "search_page_your_map": "JÅĢsu Karte", "search_people": "Meklēt cilvēkus", - "search_places": "", "search_result_page_new_search_hint": "Jauns Meklējums", - "search_state": "", "search_suggestion_list_smart_search_hint_1": "Viedā meklÄ“ÅĄana ir iespējota pēc noklusējuma, lai meklētu metadatus, izmantojiet sintaksi", "search_suggestion_list_smart_search_hint_2": "m:jÅĢsu-meklÄ“ÅĄanas-frāze", - "search_timezone": "", - "search_type": "", "search_your_photos": "Meklēt JÅĢsu fotoattēlus", - "searching_locales": "", "second": "Sekunde", "select_album_cover": "Izvēlieties albuma vāciņu", - "select_all": "", "select_all_duplicates": "AtlasÄĢt visus dublikātus", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_library_owner": "", - "select_new_face": "", "select_photos": "Fotoattēlu Izvēle", "select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu", - "selected": "", - "send_message": "", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "Aplikācijas Versija", "server_info_box_server_url": "Servera URL", "server_online": "Serveris tieÅĄsaistē", "server_stats": "Servera statistika", "server_version": "Servera versija", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", "set_date_of_birth": "IestatÄĢt dzimÅĄanas datumu", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", "setting_image_viewer_help": "DetaÄŧu skatÄĢtājs vispirms ielādē mazo sÄĢktēlu, pēc tam ielādē vidēja lieluma priekÅĄskatÄĢjumu (ja iespējots), visbeidzot ielādē oriÄŖinālu (ja iespējots).", - "setting_image_viewer_original_subtitle": "Iespējojiet sākotnējā pilnas izÅĄÄˇirtspējas attēla (liels!) ielādi. Atspējot lai samazinātu datu lietojumu (gan tÄĢklā, gan ierÄĢces keÅĄatmiņā).", + "setting_image_viewer_original_subtitle": "Iespējot sākotnējā pilnas izÅĄÄˇirtspējas attēla (liels!) ielādi. Atspējot, lai samazinātu datu lietojumu (gan tÄĢklā, gan ierÄĢces keÅĄatmiņā).", "setting_image_viewer_original_title": "Ielādēt oriÄŖinālo attēlu", "setting_image_viewer_preview_subtitle": "Iespējojiet vidējas izÅĄÄˇirtspējas attēla ielādÄ“ÅĄanu. Atspējojiet vai nu tieÅĄu oriÄŖināla ielādi, vai izmantojiet tikai sÄĢktēlu.", "setting_image_viewer_preview_title": "Ielādēt priekÅĄskatÄĢjuma attēlu", "setting_image_viewer_title": "Attēli", "setting_languages_apply": "Lietot", - "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "Valodas", - "setting_notifications_notify_failures_grace_period": "Paziņot par fona dublÄ“ÅĄanas kÄŧÅĢmēm: {}", - "setting_notifications_notify_hours": "{} stundas", + "setting_notifications_notify_failures_grace_period": "Paziņot par fona dublÄ“ÅĄanas kÄŧÅĢmēm: {duration}", + "setting_notifications_notify_hours": "{count} stundas", "setting_notifications_notify_immediately": "nekavējoties", - "setting_notifications_notify_minutes": "{} minÅĢtes", + "setting_notifications_notify_minutes": "{count} minÅĢtes", "setting_notifications_notify_never": "nekad", - "setting_notifications_notify_seconds": "{} sekundes", + "setting_notifications_notify_seconds": "{count} sekundes", "setting_notifications_single_progress_subtitle": "Detalizēta augÅĄupielādes progresa informācija par katru aktÄĢvu", "setting_notifications_single_progress_title": "RādÄĢt fona dublējuma detalizēto progresu", "setting_notifications_subtitle": "Paziņojumu preferenču pielāgoÅĄana", "setting_notifications_total_progress_subtitle": "Kopējais augÅĄupielādes progress (pabeigti/kopējie aktÄĢvi)", "setting_notifications_total_progress_title": "RādÄĢt fona dublējuma kopējo progresu", "setting_video_viewer_looping_title": "Cikliski", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "IestatÄĢjumi", "settings_require_restart": "LÅĢdzu, restartējiet Immich, lai lietotu ÅĄo iestatÄĢjumu", - "settings_saved": "", + "setup_pin_code": "UzstādÄĢt PIN kodu", "share": "KopÄĢgot", "share_add_photos": "Pievienot fotoattēlus", - "share_assets_selected": "{} izvēlēti", + "share_assets_selected": "{count} izvēlēti", "share_dialog_preparing": "Notiek sagatavoÅĄana...", "shared": "KopÄĢgots", "shared_album_activities_input_disable": "Komentāri atslēgti", @@ -1230,44 +814,41 @@ "shared_album_section_people_action_leave": "Noņemt lietotāju no albuma", "shared_album_section_people_action_remove_user": "Noņemt lietotāju no albuma", "shared_album_section_people_title": "CILVĒKI", - "shared_by": "", - "shared_by_you": "", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "AugÅĄupielādēti {current} / {total}", "shared_link_app_bar_title": "KopÄĢgotas Saites", "shared_link_clipboard_copied_massage": "Ievietots starpliktuvē", - "shared_link_clipboard_text": "Saite: {}\nParole: {}", + "shared_link_clipboard_text": "Saite: {link}\nParole: {password}", "shared_link_create_error": "KÄŧÅĢda izveidojot kopÄĢgoÅĄanas saiti", "shared_link_edit_description_hint": "Ievadiet kopÄĢgojuma aprakstu", "shared_link_edit_expire_after_option_day": "1 diena", - "shared_link_edit_expire_after_option_days": "{} dienas", + "shared_link_edit_expire_after_option_days": "{count} dienas", "shared_link_edit_expire_after_option_hour": "1 stunda", - "shared_link_edit_expire_after_option_hours": "{} stundas", + "shared_link_edit_expire_after_option_hours": "{count} stundas", "shared_link_edit_expire_after_option_minute": "1 minÅĢte", - "shared_link_edit_expire_after_option_minutes": "{} minÅĢtes", - "shared_link_edit_expire_after_option_months": "{} mēneÅĄi", - "shared_link_edit_expire_after_option_year": "{} gads", + "shared_link_edit_expire_after_option_minutes": "{count} minÅĢtes", + "shared_link_edit_expire_after_option_months": "{count} mēneÅĄi", + "shared_link_edit_expire_after_option_year": "{count} gads", "shared_link_edit_password_hint": "IerakstÄĢt kopÄĢgojuma paroli", "shared_link_edit_submit_button": "Atjaunināt saiti", "shared_link_error_server_url_fetch": "Nevarēja ienest servera URL", - "shared_link_expires_day": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} dienas", - "shared_link_expires_days": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} dienām", - "shared_link_expires_hour": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} stundas", - "shared_link_expires_hours": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} stundām", - "shared_link_expires_minute": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} minÅĢtes", - "shared_link_expires_minutes": "DerÄĢguma termiÅ†ÅĄ beidzas pēc {} minÅĢtēm", + "shared_link_expires_day": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} dienas", + "shared_link_expires_days": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} dienām", + "shared_link_expires_hour": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} stundas", + "shared_link_expires_hours": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} stundām", + "shared_link_expires_minute": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} minÅĢtes", + "shared_link_expires_minutes": "DerÄĢguma termiÅ†ÅĄ beidzas pēc {count} minÅĢtēm", "shared_link_expires_never": "DerÄĢguma termiÅ†ÅĄ beigsies ∞", - "shared_link_expires_second": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {} sekundes", - "shared_link_expires_seconds": "DerÄĢguma termiÅ†ÅĄ beidzas pēc {} sekundēm", + "shared_link_expires_second": "DerÄĢguma termiÅ†ÅĄ beigsies pēc {count} sekundes", + "shared_link_expires_seconds": "DerÄĢguma termiÅ†ÅĄ beidzas pēc {count} sekundēm", "shared_link_individual_shared": "Individuāli kopÄĢgots", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "PārvaldÄĢt KopÄĢgotās saites", "shared_links": "KopÄĢgotās saites", - "shared_with_me": "Shared with me", + "shared_with_partner": "KopÄĢgots ar {partner}", "sharing": "KopÄĢgoÅĄana", + "sharing_enter_password": "LÅĢdzu, ievadi paroli, lai apskatÄĢtu ÅĄo lapu.", "sharing_page_album": "KopÄĢgotie albumi", "sharing_page_description": "Izveidojiet koplietojamus albumus, lai kopÄĢgotu fotoattēlus un videoklipus ar JÅĢsu tÄĢkla lietotājiem.", "sharing_page_empty_list": "TUKÅ S SARAKSTS", - "sharing_sidebar_description": "", "sharing_silver_appbar_create_shared_album": "Izveidot kopÄĢgotu albumu", "sharing_silver_appbar_share_partner": "DalÄĢties ar partneri", "show_album_options": "RādÄĢt albuma iespējas", @@ -1277,21 +858,10 @@ "show_file_location": "RādÄĢt faila atraÅĄanās vietu", "show_gallery": "RādÄĢt galeriju", "show_hidden_people": "RādÄĢt paslēptos cilvēkus", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", "show_metadata": "RādÄĢt metadatus", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", "show_supporter_badge": "AtbalstÄĢtāja nozÄĢmÄĢte", "show_supporter_badge_description": "RādÄĢt atbalstÄĢtāja nozÄĢmÄĢti", - "shuffle": "", - "sign_up": "", "size": "Izmērs", - "skip_to_content": "", "slideshow": "SlÄĢdrāde", "slideshow_settings": "SlÄĢdrādes iestatÄĢjumi", "sort_albums_by": "Kārtot albumus pēc...", @@ -1299,128 +869,104 @@ "sort_items": "VienÄĢbu skaits", "sort_modified": "Izmaiņu datums", "sort_oldest": "Vecākā fotogrāfija", + "sort_people_by_similarity": "Sakārtot cilvēkus pēc lÄĢdzÄĢbas", "sort_recent": "Nesenākā fotogrāfija", "sort_title": "Nosaukums", "source": "Pirmkods", "stack": "Apvienot kaudzē", - "stack_selected_photos": "", - "stacktrace": "", - "start_date": "", + "start_date": "Sākuma datums", "state": "Å tats", "status": "Statuss", - "stop_motion_photo": "", "stop_photo_sharing": "Beigt kopÄĢgot jÅĢsu fotogrāfijas?", + "stop_photo_sharing_description": "{partner} vairs nevarēs piekÄŧÅĢt tavām fotogrāfijām.", + "stop_sharing_photos_with_user": "Pārtraukt dalÄĢties ar fotogrāfijām ar ÅĄo lietotāju", "storage": "Vieta krātuvē", - "storage_label": "", "storage_usage": "{used} no {available} izmantoti", "submit": "Iesniegt", "suggestions": "Ieteikumi", "sunrise_on_the_beach": "Saullēkts pludmalē", "support": "Atbalsts", "support_and_feedback": "Atbalsts un atsauksmes", - "swap_merge_direction": "", "sync": "Sinhronizēt", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "template": "", "theme": "Dizains", - "theme_selection": "", - "theme_selection_description": "", "theme_setting_asset_list_storage_indicator_title": "RādÄĢt krātuves indikatoru uz aktÄĢvu elementiem", - "theme_setting_asset_list_tiles_per_row_title": "AktÄĢvu skaits rindā ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Failu skaits rindā ({count})", "theme_setting_image_viewer_quality_subtitle": "Attēlu skatÄĢtāja detaÄŧu kvalitātes pielāgoÅĄana", "theme_setting_image_viewer_quality_title": "Attēlu skatÄĢtāja kvalitāte", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "Automātisks (sekot sistēmas iestatÄĢjumiem)", "theme_setting_theme_subtitle": "Izvēlieties programmas dizaina iestatÄĢjumu", "theme_setting_three_stage_loading_subtitle": "TrÄĢspakāpju ielāde var palielināt ielādÄ“ÅĄanas veiktspēju, bet izraisa ievērojami lielāku tÄĢkla noslodzi", "theme_setting_three_stage_loading_title": "Iespējot trÄĢspakāpju ielādi", "they_will_be_merged_together": "Tās tiks apvienotas", - "time_based_memories": "", + "third_party_resources": "TreÅĄo puÅĄu resursi", "timezone": "Laika zona", "to_archive": "Arhivēt", "to_change_password": "MainÄĢt paroli", "toggle_settings": "Pārslēgt iestatÄĢjumus", - "toggle_theme": "", + "total": "Kopā", "total_usage": "Kopējais lietojums", "trash": "Atkritne", "trash_all": "Dzēst Visu", - "trash_emptied": "Emptied trash", - "trash_no_results_message": "", "trash_page_delete_all": "Dzēst Visu", "trash_page_empty_trash_dialog_content": "Vai vēlaties iztukÅĄot savus izmestos aktÄĢvus? Tie tiks neatgriezeniski izņemti no Immich", - "trash_page_info": "Atkritnes vienumi tiks neatgriezeniski dzēsti pēc {} dienām", + "trash_page_info": "Atkritnes vienumi tiks neatgriezeniski dzēsti pēc {days} dienām", "trash_page_no_assets": "Atkritnē nav aktÄĢvu", "trash_page_restore_all": "Atjaunot Visu", "trash_page_select_assets_btn": "AtlasÄĢt aktÄĢvus", - "trash_page_title": "Atkritne ({})", - "type": "", + "trash_page_title": "Atkritne ({count})", + "type": "Veids", + "unable_to_change_pin_code": "Neizdevās nomainÄĢt PIN kodu", + "unable_to_setup_pin_code": "Neizdevās uzstādÄĢt PIN kodu", "unarchive": "Atarhivēt", "unfavorite": "Noņemt no izlases", "unhide_person": "Atcelt personas slēpÅĄanu", - "unknown": "", + "unknown": "Nezināms", "unknown_country": "Nezināma Valsts", "unknown_year": "Nezināms gads", "unlimited": "NeierobeÅžots", - "unlink_oauth": "", - "unlinked_oauth_account": "", "unnamed_album": "Albums bez nosaukuma", "unsaved_change": "Nesaglabāta izmaiņa", - "unselect_all": "", "unstack": "At-Stekot", - "up_next": "", "updated_password": "Parole ir atjaunināta", "upload": "AugÅĄupielādēt", - "upload_concurrency": "", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktÄĢva(-u) dublējumu uz servera?", "upload_dialog_title": "AugÅĄupielādēt AktÄĢvu", "upload_status_duplicates": "Dublikāti", "upload_status_errors": "KÄŧÅĢdas", "upload_status_uploaded": "AugÅĄupielādēts", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "", + "upload_to_immich": "AugÅĄupielādēt Immich ({count})", "usage": "Lietojums", - "use_current_connection": "use current connection", "user": "Lietotājs", + "user_has_been_deleted": "Å is lietotājs ir dzēsts.", "user_id": "Lietotāja ID", + "user_pin_code_settings": "PIN kods", + "user_purchase_settings": "Iegādāties", "user_purchase_settings_description": "Pirkuma pārvaldÄĢba", "user_usage_detail": "Informācija par lietotāju lietojumu", "username": "Lietotājvārds", "users": "Lietotāji", "utilities": "RÄĢki", - "validate": "", - "validate_endpoint_error": "Please enter a valid URL", - "variables": "", + "variables": "MainÄĢgie", "version": "Versija", + "version_announcement_closing": "Tavs draugs, Alekss", "version_announcement_message": "Sveiki! Ir pieejama jauna Immich versija. LÅĢdzu, veltiet laiku, lai izlasÄĢtu laidiena piezÄĢmes un pārliecinātos, ka jÅĢsu iestatÄĢjumi ir atjaunināti, lai novērstu jebkādu nepareizu konfigurāciju, jo ÄĢpaÅĄi, ja izmantojat WatchTower vai citu mehānismu, kas automātiski atjaunina jÅĢsu Immich instanci.", "version_announcement_overlay_release_notes": "informācija par laidienu", "version_announcement_overlay_text_1": "Sveiks draugs, ir jauns izlaidums no", - "version_announcement_overlay_text_2": "lÅĢdzu, veltiet laiku, lai apmeklētu", + "version_announcement_overlay_text_2": "lÅĢdzu, veltiet laiku, lai apmeklētu ", "version_announcement_overlay_text_3": " un pārliecinieties, vai docker-compose un .env iestatÄĢjumi ir atjaunināti, lai novērstu jebkādas nepareizas konfigurācijas, ÄĢpaÅĄi, ja izmantojat WatchTower vai mehānismu, kas automātiski veic servera lietojumprogrammas atjauninÄÅĄanu.", "version_announcement_overlay_title": "Pieejama jauna servera versija 🎉", "version_history": "Versiju vēsture", "version_history_item": "{version} uzstādÄĢta {date}", "video": "Videoklips", - "video_hover_setting_description": "", "videos": "Videoklipi", "view_album": "SkatÄĢt Albumu", "view_all": "ApskatÄĢt visu", "view_all_users": "SkatÄĢt visus lietotājus", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", "viewer_remove_from_stack": "Noņemt no Steka", "viewer_stack_use_as_main_asset": "Izmantot kā Galveno AktÄĢvu", "viewer_unstack": "At-Stekot", "waiting": "Gaida", "week": "NedēÄŧa", - "welcome_to_immich": "", "wifi_name": "WiFi Name", "year": "Gads", "years_ago": "Pirms {years, plural, one {# gada} other {# gadiem}}", diff --git a/i18n/mk.json b/i18n/mk.json index 658ab9453e..e70108cd63 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -71,12 +71,7 @@ "oauth_auto_launch": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž СаĐŋĐžŅ‡ĐŊŅƒĐ˛Đ°ŅšĐĩ", "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚ŅĐēа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ°", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐŊа ĐēĐžĐŋ҇Đĩ", - "oauth_client_id": "КĐģиĐĩĐŊ҂ҁĐēи ID", - "oauth_client_secret": "КĐģиĐĩĐŊ҂ҁĐēа Ņ‚Đ°Ņ˜ĐŊа", - "oauth_issuer_url": "URL ĐŊа Đ¸ĐˇĐ´Đ°Đ˛Đ°Ņ‡", - "oauth_scope": "ОĐŋҁĐĩĐŗ", "oauth_settings": "OAuth", - "oauth_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đ°Đŧ Са ĐŋĐžŅ‚ĐŋĐ¸ŅˆŅƒĐ˛Đ°ŅšĐĩ", "offline_paths": "ĐžŅ„ĐģĐ°Ņ˜ĐŊ ĐŋĐ°Ņ‚ĐĩĐēи", "password_settings": "ĐĐ°Ņ˜Đ°Đ˛Đ° ŅĐž ĐģОСиĐŊĐēа", "repair_all": "ПоĐŋŅ€Đ°Đ˛Đ¸ ĐŗĐ¸ ŅĐ¸Ņ‚Đĩ", @@ -171,7 +166,6 @@ "enabled": "ОвозĐŧĐžĐļĐĩĐŊĐž", "end_date": "ĐšŅ€Đ°ĐĩĐŊ Đ´Đ°Ņ‚ŅƒĐŧ", "error": "Đ“Ņ€Đĩ҈Đēа", - "exif": "Exif", "expand_all": "ĐŸŅ€ĐžŅˆĐ¸Ņ€Đ¸ ĐŗĐ¸ ŅĐ¸Ņ‚Đĩ", "expire_after": "Да Đ¸ŅŅ‚Đĩ҇Đĩ ĐŋĐžŅĐģĐĩ", "expired": "Đ˜ŅŅ‚Đĩ҇ĐĩĐŊĐž", @@ -240,7 +234,6 @@ "no_results": "НĐĩĐŧа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸", "notes": "БĐĩĐģĐĩ҈Đēи", "notifications": "ĐĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", - "oauth": "OAuth", "offline": "ĐžŅ„ĐģĐ°Ņ˜ĐŊ", "ok": "ОĐē", "online": "ОĐŊĐģĐ°Ņ˜ĐŊ", diff --git a/i18n/ml.json b/i18n/ml.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/i18n/ml.json @@ -0,0 +1 @@ +{} diff --git a/i18n/mn.json b/i18n/mn.json index c91a670846..23fe574c6a 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -4,7 +4,6 @@ "account_settings": "Đ‘Ō¯Ņ€Ņ‚ĐŗŅĐģиКĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "acknowledge": "ОйĐģĐŗĐžĐģОО", "action": "ŌŽĐšĐģĐ´ŅĐģ", - "action_common_update": "Update", "actions": "ŌŽĐšĐģĐ´ĐģŌ¯Ō¯Đ´", "active": "Đ˜Đ´ŅĐ˛Ņ…Ņ‚ŅĐš", "activity": "ŌŽĐšĐģĐ´ĐģиКĐŊ ĐąŌ¯Ņ€Ņ‚ĐŗŅĐģ", @@ -14,18 +13,11 @@ "add_a_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĐŊŅĐŧŅŅ…", "add_a_name": "ĐŅŅ€ ĶŠĐŗĶŠŅ…", "add_a_title": "Đ“Đ°Ņ€Ņ‡Đ¸Đŗ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", - "add_endpoint": "Add endpoint", - "add_exclusion_pattern": "", - "add_import_path": "", "add_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "add_more_users": "Ķ¨ĶŠŅ€ Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸Đ´ ĐŊŅĐŧŅŅ…", "add_partner": "ĐĨаĐŧŅ‚Ņ€Đ°ĐŗŅ‡ ĐŊŅĐŧŅŅ…", - "add_path": "", "add_photos": "Đ—ŅƒŅ€Đ°Đŗ ĐŊŅĐŧŅŅ…", - "add_to": "", "add_to_album": "ĐĻĐžĐŧĐžĐŗŅ‚ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", "add_to_shared_album": "ĐŅŅĐģŅ‚Ņ‚ŅĐš аĐģĐąŅƒĐŧĐ´ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "added_to_archive": "ĐŅ€Ņ…Đ¸Đ˛Đ´ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "added_to_favorites": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€ĐŗĐ°ĐŊĐ´ ĐŊŅĐŧŅŅ…", @@ -35,36 +27,10 @@ "authentication_settings_description": "ĐŅƒŅƒŅ† Ō¯ĐŗĐ¸ĐšĐŊ ŅƒĐ´Đ¸Ņ€Đ´ĐģĐ°ĐŗĐ°, OAuth йОĐģĐžĐŊ ĐąŅƒŅĐ°Đ´ Ņ‚Đ°ĐŊиĐŊ ĐŊŅĐ˛Ņ‚Ņ€ŅĐģŅ‚Đ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "authentication_settings_disable_all": "Đ‘Ō¯Ņ… ĐŊŅĐ˛Ņ‚Ņ€ŅŅ… Đ°Ņ€ĐŗŅƒŅƒĐ´Ņ‹Đŗ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŗĐžŅ…Đ´ĐžĐž Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃? ĐŅĐ˛Ņ‚Ņ€ŅŅ… Ō¯ĐšĐģĐ´ŅĐģ ĐąŌ¯Ņ€ŅĐŊ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŊĐž.", "check_all": "Đ‘Ō¯ĐŗĐ´Đ¸ĐšĐŗ ŅĐžĐŊĐŗĐžŅ…", - "disable_login": "", - "duplicate_detection_job_description": "", "face_detection": "ĐŌ¯Ō¯Ņ€ иĐģŅ€Ō¯Ō¯ĐģŅŅ…", - "image_format_description": "", - "image_prefer_embedded_preview": "", - "image_prefer_embedded_preview_setting_description": "", - "image_prefer_wide_gamut": "", - "image_prefer_wide_gamut_setting_description": "", "image_quality": "ЧаĐŊĐ°Ņ€", - "image_settings": "", - "image_settings_description": "", "job_settings": "АĐļĐģŅ‹ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", - "job_settings_description": "", "job_status": "АĐļĐģŅ‹ĐŊ Ņ‚ĶŠĐģĶŠĐ˛", - "library_scanning": "", - "library_scanning_description": "", - "library_scanning_enable_description": "", - "library_settings": "", - "library_settings_description": "", - "library_tasks_description": "", - "library_watching_enable_description": "", - "library_watching_settings": "", - "library_watching_settings_description": "", - "logging_enable_description": "", - "logging_level_description": "", - "logging_settings": "", - "machine_learning_clip_model": "", - "machine_learning_duplicate_detection": "", - "machine_learning_duplicate_detection_enabled_description": "", - "machine_learning_duplicate_detection_setting_description": "", "machine_learning_enabled": "ĐœĐ°ŅˆĐ¸ĐŊ ŅŅƒŅ€ĐŗĐ°ĐģŅ‚ Đ¸Đ´ŅĐ˛Ņ…ĐļŌ¯Ō¯ĐģŅŅ…", "machine_learning_enabled_description": "Đ˜Đ´ŅĐ˛Ņ…ĐŗŌ¯Đš йОĐģĐŗĐžŅĐžĐŊ Ō¯ĐĩĐ´ Đ´ĐžĐžŅ€Ņ… Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐžĐŊĐžĐžŅ Ņ…Đ°ĐŧĐ°Đ°Ņ€Đ°Ņ…ĐŗŌ¯ĐšĐŗŅŅŅ€ ĐąŌ¯Ņ… ĐŧĐ°ŅˆĐ¸ĐŊ ŅŅƒŅ€ĐŗĐ°Đģ҂ҋĐŊ йОĐģĐžĐŧĐļ Đ¸Đ´ŅĐ˛Ņ…ĐŗŌ¯Đš йОĐģĐŊĐž.", "machine_learning_facial_recognition": "ĐŌ¯Ō¯Ņ€ Ņ‚Đ°ĐŊиĐģŅ‚", @@ -72,182 +38,19 @@ "machine_learning_facial_recognition_model": "ĐŌ¯Ō¯Ņ€ Ņ‚Đ°ĐŊиĐģ҂ҋĐŊ ĐˇĐ°ĐŗĐ˛Đ°Ņ€", "machine_learning_facial_recognition_model_description": "Đ—Đ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´ Ņ…ŅĐŧĐļŅŅ ĐŊҌ ĐąŅƒŅƒŅ€Đ°Ņ… ŅŅ€ŅĐŧĐąŅŅŅ€ ĐļĐ°ĐŗŅŅĐ°ĐŊ. ĐĸĐžĐŧ ĐˇĐ°ĐŗĐ˛Đ°Ņ€ŅƒŅƒĐ´ ŅƒĐ´Đ°Đ°ĐŊ, иĐģŌ¯Ō¯ Đ¸Ņ… ŅĐ°ĐŊĐ°Ņ… ОК Ņ…ŅŅ€ŅĐŗĐģŅŅ… йОĐģĐžĐ˛Ņ‡ Ņ…Đ°Ņ€ŅŒŅ†Đ°ĐŊĐŗŅƒĐš Ņ‡Đ°ĐŊĐ°Ņ€Ņ‚Đ°Đš ԝҀ Đ´Ō¯ĐŊ Ō¯ĐˇŌ¯Ō¯ĐģĐŊŅ. Đ—Đ°ĐŗĐ˛Đ°Ņ€ ĶŠĶŠŅ€Ņ‡Đ¸ĐģŅĶŠĐŊ Ņ‚ĐžŅ…Đ¸ĐžĐģĐ´ĐžĐģĐ´ ĐŊԝԝҀ иĐģŅ€Ō¯Ō¯ĐģŅĐģŅ‚Đ¸ĐšĐŊ аĐļĐģŅ‹Đŗ Đ´Đ°Ņ…Đ¸ĐŊ ŅŅ…ĐģŌ¯Ō¯ĐģŅŅ… ŅˆĐ°Đ°Ņ€Đ´ĐģĐ°ĐŗĐ°Ņ‚Đ°ĐšĐŗ ŅĐ°ĐŊĐ°Đ°Ņ€Đ°Đš.", "machine_learning_facial_recognition_setting": "ĐŌ¯Ō¯Ņ€ Ņ‚Đ°ĐŊиĐģŅ‚ Đ¸Đ´ŅĐ˛Ņ…ĐļŌ¯Ō¯ĐģŅŅ…", - "machine_learning_facial_recognition_setting_description": "", - "machine_learning_max_detection_distance": "", - "machine_learning_max_detection_distance_description": "", - "machine_learning_max_recognition_distance": "", - "machine_learning_max_recognition_distance_description": "", - "machine_learning_min_detection_score": "", - "machine_learning_min_detection_score_description": "", - "machine_learning_min_recognized_faces": "", - "machine_learning_min_recognized_faces_description": "", - "machine_learning_settings": "", - "machine_learning_settings_description": "", - "machine_learning_smart_search": "", - "machine_learning_smart_search_description": "", - "machine_learning_smart_search_enabled_description": "", - "machine_learning_url_description": "", - "manage_log_settings": "", - "map_dark_style": "", - "map_enable_description": "", - "map_light_style": "", - "map_reverse_geocoding": "", - "map_reverse_geocoding_enable_description": "", - "map_reverse_geocoding_settings": "", "map_settings": "Đ“Đ°ĐˇŅ€Ņ‹ĐŊ ĐˇŅƒŅ€Đ°Đŗ", - "map_settings_description": "", - "map_style_description": "", - "metadata_extraction_job_description": "", - "migration_job_description": "", - "notification_email_from_address": "", - "notification_email_from_address_description": "", - "notification_email_host_description": "", - "notification_email_ignore_certificate_errors": "", - "notification_email_ignore_certificate_errors_description": "", - "notification_email_password_description": "", - "notification_email_port_description": "", - "notification_email_sent_test_email_button": "", - "notification_email_setting_description": "", - "notification_email_test_email_failed": "", - "notification_email_test_email_sent": "", - "notification_email_username_description": "", - "notification_enable_email_notifications": "", - "notification_settings": "", - "notification_settings_description": "", - "oauth_auto_launch": "", - "oauth_auto_launch_description": "", - "oauth_auto_register": "", - "oauth_auto_register_description": "", - "oauth_button_text": "", - "oauth_client_id": "", - "oauth_client_secret": "", - "oauth_enable_description": "", - "oauth_issuer_url": "", - "oauth_mobile_redirect_uri": "", - "oauth_mobile_redirect_uri_override": "", - "oauth_mobile_redirect_uri_override_description": "", - "oauth_scope": "", - "oauth_settings": "", - "oauth_settings_description": "", - "oauth_signing_algorithm": "", - "oauth_storage_label_claim": "", - "oauth_storage_label_claim_description": "", - "oauth_storage_quota_claim": "", - "oauth_storage_quota_claim_description": "", - "oauth_storage_quota_default": "", - "oauth_storage_quota_default_description": "", - "password_enable_description": "", - "password_settings": "", - "password_settings_description": "", - "server_external_domain_settings": "", - "server_external_domain_settings_description": "", - "server_settings": "", - "server_settings_description": "", - "server_welcome_message": "", - "server_welcome_message_description": "", - "sidecar_job_description": "", - "slideshow_duration_description": "", - "smart_search_job_description": "", - "storage_template_enable_description": "", - "storage_template_hash_verification_enabled": "", - "storage_template_hash_verification_enabled_description": "", - "storage_template_migration_job": "", - "storage_template_settings": "", - "storage_template_settings_description": "", - "theme_custom_css_settings": "", - "theme_custom_css_settings_description": "", - "theme_settings": "", - "theme_settings_description": "", - "thumbnail_generation_job_description": "", - "transcoding_acceleration_api": "", - "transcoding_acceleration_api_description": "", - "transcoding_acceleration_nvenc": "", - "transcoding_acceleration_qsv": "", - "transcoding_acceleration_rkmpp": "", - "transcoding_acceleration_vaapi": "", - "transcoding_accepted_audio_codecs": "", - "transcoding_accepted_audio_codecs_description": "", - "transcoding_accepted_video_codecs": "", - "transcoding_accepted_video_codecs_description": "", - "transcoding_advanced_options_description": "", - "transcoding_audio_codec": "", - "transcoding_audio_codec_description": "", - "transcoding_bitrate_description": "", - "transcoding_constant_quality_mode": "", - "transcoding_constant_quality_mode_description": "", - "transcoding_constant_rate_factor": "", - "transcoding_constant_rate_factor_description": "", - "transcoding_disabled_description": "", - "transcoding_hardware_acceleration": "", - "transcoding_hardware_acceleration_description": "", - "transcoding_hardware_decoding": "", - "transcoding_hardware_decoding_setting_description": "", - "transcoding_hevc_codec": "", - "transcoding_max_b_frames": "", - "transcoding_max_b_frames_description": "", - "transcoding_max_bitrate": "", - "transcoding_max_bitrate_description": "", - "transcoding_max_keyframe_interval": "", - "transcoding_max_keyframe_interval_description": "", - "transcoding_optimal_description": "", - "transcoding_preferred_hardware_device": "", - "transcoding_preferred_hardware_device_description": "", - "transcoding_preset_preset": "", - "transcoding_preset_preset_description": "", - "transcoding_reference_frames": "", - "transcoding_reference_frames_description": "", - "transcoding_required_description": "", - "transcoding_settings": "", - "transcoding_settings_description": "", - "transcoding_target_resolution": "", - "transcoding_target_resolution_description": "", - "transcoding_temporal_aq": "", - "transcoding_temporal_aq_description": "", - "transcoding_threads": "", - "transcoding_threads_description": "", - "transcoding_tone_mapping": "", - "transcoding_tone_mapping_description": "", - "transcoding_transcode_policy": "", - "transcoding_two_pass_encoding": "", - "transcoding_two_pass_encoding_setting_description": "", - "transcoding_video_codec": "", - "transcoding_video_codec_description": "", "trash_enabled_description": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ Đ¸Đ´ŅĐ˛Ņ…ĐļŌ¯Ō¯ĐģŅŅ…", "trash_number_of_days": "ĐĨĐžĐŊĐžĐŗĐ¸ĐšĐŊ Ņ‚ĐžĐž", "trash_number_of_days_description": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛Đ°ĐŊĐ´ Ņ…ŅĐ´ Ņ…ĐžĐŊĐžĐŗ Ņ…Đ°Đ´ĐŗĐ°Đģаад ĐąŌ¯Ņ€ ĐŧĶŠŅĶŠĐŊ ŅƒŅŅ‚ĐŗĐ°Ņ… Đ˛Ņ", "trash_settings": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ĐŊŅ‹ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "trash_settings_description": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ĐŊŅ‹ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐžĐŗ ĶŠĶŠŅ€Ņ‡ĐģĶŠŅ…", - "user_delete_delay_settings": "", - "user_delete_delay_settings_description": "", "user_management": "ĐĨŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŊ ŅƒĐ´Đ¸Ņ€Đ´ĐģĐ°ĐŗĐ°", "user_password_has_been_reset": "ĐĨŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŊ ĐŊŅƒŅƒŅ† Ō¯Đŗ ŅˆĐ¸ĐŊŅŅŅ€ Ņ‚ĐžŅ…Đ¸Ņ€ŅƒŅƒĐģĐ°ĐŗĐ´Đģаа:", "user_restore_description": "{user}-ĐŊ ĐąŌ¯Ņ€Ņ‚ĐŗŅĐģ ŅŅŅ€ĐŗŅĐŊŅ.", - "user_settings": "ĐĨŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", - "user_settings_description": "", - "version_check_enabled_description": "", - "version_check_settings": "", - "version_check_settings_description": "", - "video_conversion_job_description": "" + "user_settings": "ĐĨŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž" }, - "admin_email": "", - "admin_password": "", "administration": "АдĐŧиĐŊ", - "advanced": "", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", "album_added": "ĐĻĐžĐŧĐžĐŗ ĐŊŅĐŧŅĐŗĐ´ĐģŅŅ", - "album_added_notification_setting_description": "", - "album_cover_updated": "", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", "album_info_updated": "ĐĻĐžĐŧĐŗĐ¸ĐšĐŊ ĐŧŅĐģŅŅĐģŅĐģ ŅˆĐ¸ĐŊŅŅ‡ĐģŅĐŗĐ´ĐģŅŅ", "album_leave": "ĐĻĐžĐŧĐŗĐžĐžŅ ĐŗĐ°Ņ€Đ°Ņ… ҃҃?", "album_leave_confirmation": "Đĸа {album} Ņ†ĐžĐŧĐŗĐžĐžŅ ĐŗĐ°Ņ€Đ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃?", @@ -255,20 +58,6 @@ "album_options": "ĐĻĐžĐŧĐŗĐ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "album_remove_user": "ĐĨŅŅ€ŅĐŗĐģŅĐŗŅ‡ Ņ…Đ°ŅĐ°Ņ… ҃҃?", "album_remove_user_confirmation": "{user} Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŗ Ņ…Đ°ŅĐ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃?", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", - "album_updated": "", - "album_updated_setting_description": "", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", - "album_viewer_appbar_share_to": "Share To", - "album_viewer_page_share_add_users": "Add users", "albums": "ĐĻĐžĐŧĐŗŅƒŅƒĐ´", "all": "Đ‘Ō¯ĐŗĐ´", "all_albums": "Đ‘Ō¯Ņ… Ņ†ĐžĐŧĐžĐŗ", @@ -280,993 +69,63 @@ "api_key_description": "Đ­ĐŊŅ ŅƒŅ‚ĐŗĐ° ĐˇĶŠĐ˛Ņ…ĶŠĐŊ ĐŗĐ°ĐŊ҆ Đģ ŅƒĐ´Đ°Đ° Ņ…Đ°Ņ€Đ°ĐŗĐ´Đ°ĐŊа. ĐĻĐžĐŊŅ…ĐžĐž Ņ…Đ°Đ°Ņ…Đ°Đ°Ņ ĶŠĐŧĐŊĶŠ Ņ…ŅƒŅƒĐģĐļ Đ°Đ˛Đ°Đ°Ņ€Đ°Đš.", "api_key_empty": "ĐĸаĐŊŅ‹ API ҂ԝĐģŅ…Ō¯Ō¯Ņ€Đ¸ĐšĐŊ ĐŊŅŅ€ Ņ…ĐžĐžŅĐžĐŊ йаКĐļ йОĐģĐžŅ…ĐŗŌ¯Đš", "api_keys": "API ҂ԝĐģŅ…Ō¯Ō¯Ņ€Ō¯Ō¯Đ´", - "app_bar_signout_dialog_content": "Are you sure you want to sign out?", - "app_bar_signout_dialog_ok": "Yes", - "app_bar_signout_dialog_title": "Sign out", "app_settings": "АĐŋĐŋ-ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", - "appears_in": "", "archive": "ĐŅ€Ņ…Đ¸Đ˛", "archive_or_unarchive_photo": "Đ—ŅƒŅ€ĐŗĐ¸ĐšĐŗ Đ°Ņ€Ņ…Đ¸Đ˛Ņ‚ Ņ…Đ¸ĐšŅ… ŅŅĐ˛ŅĐģ ĐŗĐ°Ņ€ĐŗĐ°Ņ…", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", "archive_size": "ĐŅ€Ņ…Đ¸Đ˛Ņ‹ĐŊ Ņ…ŅĐŧĐļŅŅ", "archive_size_description": "ĐĸĐ°Ņ‚Đ°Ņ… Ō¯ĐĩиКĐŊ Đ°Ņ€Ņ…Đ¸Đ˛Ņ‹ĐŊ Ņ…ŅĐŧĐļŅŅĐŗ Ņ‚ĐžŅ…Đ¸Ņ€ŅƒŅƒĐģĐ°Ņ… (GiB-Ņ€)", - "archived": "Archived", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", "asset_added_to_album": "ĐĻĐžĐŧĐžĐŗŅ‚ ĐŊŅĐŧŅŅĐŊ", "asset_adding_to_album": "ĐĻĐžĐŧĐžĐŗŅ‚ ĐŊŅĐŧĐļ йаКĐŊа...", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", - "asset_offline": "", - "asset_restored_successfully": "Asset restored successfully", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", - "assets": "", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", - "authorized_devices": "", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", - "back": "", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", "backup_controller_page_background_app_refresh_disabled_content": "АĐŋĐŋ ĐŊŅŅĐŗŅŅĐŗŌ¯Đš ĐąĐ°ĐšŅ… Ō¯ĐĩĐ´ ĐŊĶŠĶŠŅ†ĐģĶŠĐģŅ‚ Ņ…Đ¸ĐšŅ… йОĐģ Settings > General > Background App Refresh Ņ…Đ°ĐŊдаĐļ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐļŌ¯Ō¯ĐģĐŊŅ Ō¯Ō¯.", "backup_controller_page_background_app_refresh_disabled_title": "АĐŋĐŋ ĐŊŅŅĐŗŅŅĐŗŌ¯Đš ĐąĐ°ĐšŅ… Ō¯ĐĩĐ´ ĐŊĶŠĶŠŅ†ĐģĶŠĐģŅ‚ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš.", "backup_controller_page_background_app_refresh_enable_button_text": "ĐĸĐžŅ…Đ¸Ņ€ĐŗĐžĐž Ņ…ŅŅŅĐŗŅ‚ ĐžŅ‡Đ¸Ņ…", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", - "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "backup_controller_page_turn_on": "Turn on foreground backup", - "backup_controller_page_uploading_file_info": "Uploading file info", - "backup_err_only_album": "Cannot remove the only album", - "backup_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", - "backward": "", - "blurred_background": "", "buy": "Immich Ņ…ŅƒĐ´Đ°ĐģдаĐļ Đ°Đ˛Đ°Ņ…", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", - "cache_settings_title": "Caching Settings", "camera": "КаĐŧĐĩŅ€", "camera_brand": "КаĐŧĐĩҀҋĐŊ Ō¯ĐšĐģĐ´Đ˛ŅŅ€", "camera_model": "КаĐŧĐĩҀҋĐŊ ĐˇĐ°ĐŗĐ˛Đ°Ņ€", "cancel": "ĐĻŅƒŅ†ĐģĐ°Ņ…", "cancel_search": "ĐĨаКĐģŅ‚ Ņ†ŅƒŅ†ĐģĐ°Ņ…", - "canceled": "Canceled", - "cannot_merge_people": "", - "cannot_update_the_description": "", "change_date": "ĐžĐŗĐŊОО ĶŠĶŠŅ€Ņ‡ĐģĶŠŅ…", - "change_display_order": "Change display order", - "change_expiration_time": "", "change_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĶŠĶŠŅ€Ņ‡ĐģĶŠŅ…", "change_name": "ĐŅŅ€ ĶŠĶŠŅ€Ņ‡ĐģĶŠŅ…", "change_name_successfully": "ĐŅŅ€ аĐŧĐļиĐģŅ‚Ņ‚Đ°Đš ĶŠĶŠŅ€Ņ‡ĐģĶŠĐŗĐ´ĐģĶŠĶŠ", "change_password": "ĐŅƒŅƒŅ† Ō¯Đŗ ĶŠĶŠŅ€Ņ‡ĐģĶŠŅ…", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", - "change_your_password": "", - "changed_visibility_successfully": "", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", - "check_logs": "", "city": "ĐĨĐžŅ‚", "clear": "ĐĻŅĐ˛ŅŅ€ĐģŅŅ…", "clear_all": "Đ‘Ō¯ĐŗĐ´Đ¸ĐšĐŗ Ņ†ŅĐ˛ŅŅ€ĐģŅŅ…", - "clear_message": "", - "clear_value": "", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", - "close": "", - "collapse_all": "", - "color_theme": "", - "comment_options": "", - "comments_are_disabled": "", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", - "confirm": "", - "confirm_admin_password": "", - "confirm_password": "", - "contain": "", - "context": "", - "continue": "", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", - "copied_image_to_clipboard": "", - "copy_error": "", - "copy_file_path": "", - "copy_image": "", - "copy_link": "", - "copy_link_to_clipboard": "", - "copy_password": "", - "copy_to_clipboard": "", - "country": "", - "cover": "", - "covers": "", - "create": "", - "create_album": "", - "create_album_page_untitled": "Untitled", - "create_library": "", - "create_link": "", - "create_link_to_share": "", - "create_new": "CREATE NEW", - "create_new_person": "", - "create_new_user": "", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", - "create_user": "", - "created": "", - "crop": "Crop", - "curated_object_page_title": "Things", - "current_device": "", - "current_server_address": "Current server address", - "custom_locale": "", - "custom_locale_description": "", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", - "dark": "", - "date_after": "", - "date_and_time": "", - "date_before": "", - "date_format": "E, LLL d, y â€ĸ h:mm a", - "date_range": "", - "day": "", - "default_locale": "", - "default_locale_description": "", - "delete": "", - "delete_album": "", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", - "delete_key": "", - "delete_library": "", - "delete_link": "", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", - "delete_shared_link": "", - "delete_shared_link_dialog_title": "Delete Shared Link", - "delete_user": "", - "deleted_shared_link": "", - "description": "", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", - "details": "", - "direction": "", - "disallow_edits": "", - "discover": "", - "dismiss_all_errors": "", - "dismiss_error": "", - "display_options": "", - "display_order": "", - "display_original_photos": "", - "display_original_photos_setting_description": "", - "done": "", - "download": "", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", - "download_notfound": "Download not found", - "download_paused": "Download paused", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", - "downloading": "", - "downloading_media": "Downloading media", - "duration": "", - "edit_album": "", - "edit_avatar": "", - "edit_date": "", - "edit_date_and_time": "", - "edit_exclusion_pattern": "", - "edit_faces": "", - "edit_import_path": "", - "edit_import_paths": "", - "edit_key": "", - "edit_link": "", - "edit_location": "", - "edit_location_dialog_title": "Location", - "edit_name": "", - "edit_people": "", - "edit_title": "", - "edit_user": "", - "edited": "", - "editor": "", - "email": "", - "empty_folder": "This folder is empty", "empty_trash": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ Ņ…ĐžĐžŅĐģĐžŅ…", - "enable": "", - "enabled": "", - "end_date": "", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", - "error": "", - "error_change_sort_album": "Failed to change album sort order", - "error_loading_image": "", - "error_saving_image": "Error: {}", "errors": { - "unable_to_add_album_users": "", - "unable_to_add_comment": "", - "unable_to_add_partners": "", - "unable_to_change_album_user_role": "", - "unable_to_change_date": "", - "unable_to_change_location": "", - "unable_to_create_admin_account": "", - "unable_to_create_library": "", - "unable_to_create_user": "", - "unable_to_delete_album": "", - "unable_to_delete_asset": "", - "unable_to_delete_user": "", "unable_to_empty_trash": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛Ņ‹Đŗ Ņ…ĐžĐžŅĐģĐžĐļ Ņ‡Đ°Đ´ŅĐ°ĐŊĐŗŌ¯Đš", - "unable_to_enter_fullscreen": "", - "unable_to_exit_fullscreen": "", - "unable_to_hide_person": "", - "unable_to_load_album": "", - "unable_to_load_asset_activity": "", - "unable_to_load_items": "", - "unable_to_load_liked_status": "", - "unable_to_play_video": "", - "unable_to_refresh_user": "", - "unable_to_remove_album_users": "", - "unable_to_remove_library": "", - "unable_to_remove_partner": "", - "unable_to_remove_reaction": "", - "unable_to_repair_items": "", - "unable_to_reset_password": "", - "unable_to_resolve_duplicate": "", - "unable_to_restore_assets": "", - "unable_to_restore_trash": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ĐŊĐ°Đ°Ņ ĐŗĐ°Ņ€ĐŗĐ°Đļ Ņ‡Đ°Đ´ŅĐ°ĐŊĐŗŌ¯Đš", - "unable_to_restore_user": "", - "unable_to_save_album": "", - "unable_to_save_name": "", - "unable_to_save_profile": "", - "unable_to_save_settings": "", - "unable_to_scan_libraries": "", - "unable_to_scan_library": "", - "unable_to_set_profile_picture": "", - "unable_to_submit_job": "", - "unable_to_trash_asset": "", - "unable_to_unlink_account": "", - "unable_to_update_library": "", - "unable_to_update_location": "", - "unable_to_update_settings": "", - "unable_to_update_user": "" + "unable_to_restore_trash": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛ĐŊĐ°Đ°Ņ ĐŗĐ°Ņ€ĐŗĐ°Đļ Ņ‡Đ°Đ´ŅĐ°ĐŊĐŗŌ¯Đš" }, - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", - "exit_slideshow": "", - "expand_all": "", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", - "experimental_settings_title": "Experimental", - "expire_after": "", - "expired": "", "explore": "Đ­Ņ€Đļ ĐžĐģĐžŅ…", - "extension": "", - "external_libraries": "", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", - "failed": "Failed", - "failed_to_load_assets": "Failed to load assets", - "failed_to_load_folder": "Failed to load folder", - "favorite": "", - "favorite_or_unfavorite_photo": "", "favorites": "Đ”ŅƒŅ€Ņ‚Đ°Đš", - "favorites_page_no_favorites": "No favorite assets found", - "feature_photo_updated": "", - "file_name": "", - "file_name_or_extension": "", - "filename": "", - "filetype": "", - "filter": "Filter", - "filter_people": "", - "fix_incorrect_match": "", - "folder": "Folder", - "folder_not_found": "Folder not found", - "folders": "Folders", - "forward": "", - "general": "", - "get_help": "", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", - "getting_started": "", - "go_back": "", - "go_to_search": "", - "grant_permission": "Grant permission", - "group_albums_by": "", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", - "has_quota": "", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", - "hide_gallery": "", - "hide_password": "", - "hide_person": "", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", - "home_page_building_timeline": "Building the timeline", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", - "host": "", - "hour": "", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", - "image": "", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", - "immich_logo": "", - "import_path": "", - "in_archive": "", - "include_archived": "", - "include_shared_albums": "", - "include_shared_partner_assets": "", - "individual_share": "", - "info": "", - "interval": { - "day_at_onepm": "", - "hours": "", - "night_at_midnight": "", - "night_at_twoam": "" - }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "ĐĨŌ¯Đŧԝԝҁ ŅƒŅ€Đ¸Ņ…", - "invite_to_album": "", - "jobs": "", - "keep": "", - "keyboard_shortcuts": "", - "language": "", - "language_setting_description": "", - "last_seen": "", - "leave": "", - "let_others_respond": "", - "level": "", "library": "Đ—ŅƒŅ€ĐŗĐ¸ĐšĐŊ ŅĐ°ĐŊ", - "library_options": "", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", - "light": "", - "link_options": "", - "link_to_oauth": "", - "linked_oauth_account": "", - "list": "", - "loading": "", - "loading_search_results_failed": "", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", - "log_out": "", - "log_out_all_devices": "", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", - "login_has_been_disabled": "", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", - "look": "", - "loop_videos": "", - "loop_videos_description": "", - "make": "", - "manage_shared_links": "", - "manage_sharing_with_partners": "", - "manage_the_app_settings": "", - "manage_your_account": "", - "manage_your_api_keys": "", - "manage_your_devices": "", - "manage_your_oauth_connection": "", - "map": "", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", - "map_marker_with_image": "", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", - "map_settings": "", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", - "media_type": "", - "memories": "", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", - "memories_setting_description": "", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", - "menu": "", - "merge": "", - "merge_people": "", - "merge_people_successfully": "", - "minimize": "", - "minute": "", - "missing": "", - "model": "", - "month": "", - "monthly_title_text_date_format": "MMMM y", - "more": "", - "moved_to_trash": "", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", - "my_albums": "", - "name": "", - "name_or_nickname": "", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", - "never": "", - "new_api_key": "", - "new_password": "", - "new_person": "", - "new_user_created": "", - "newest_first": "", - "next": "", - "next_memory": "", - "no": "", - "no_albums_message": "", - "no_archived_assets_message": "", "no_assets_message": "Đ­ĐŊĐ´ Đ´Đ°Ņ€Đļ Ņ‚Đ° ŅŅ…ĐŊиК ĐˇŅƒŅ€ĐŗĐ°Đ° Ņ…ŅƒŅƒĐģĐļ Ō¯ĐˇŅŅ… Ō¯Ō¯", - "no_assets_to_show": "No assets to show", - "no_exif_info_available": "", "no_explore_results_message": "Đ—ŅƒŅ€Đ°Đŗ Ņ…ŅƒŅƒĐģĐļ ĐžŅ€ŅƒŅƒĐģŅĐ°ĐŊŅ‹ Đ´Đ°Ņ€Đ°Đ° Đ°ŅˆĐ¸ĐŗĐģĐ°Ņ… йОĐģĐžĐŧĐļŅ‚ĐžĐš йОĐģĐŊĐž.", - "no_favorites_message": "", - "no_libraries_message": "", - "no_name": "", - "no_places": "", - "no_results": "", - "no_shared_albums_message": "", - "not_in_any_album": "", - "not_selected": "Not selected", - "notes": "", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", "notification_permission_list_tile_content": "ĐœŅĐ´ŅĐŗĐ´ŅĐģ ĐŊŅŅŅ… ŅŅ€Ņ… ĶŠĐŗĐŊĶŠ Ō¯Ō¯.\n", "notification_permission_list_tile_enable_button": "ĐœŅĐ´ŅĐŗĐ´ŅĐģ ĐŊŅŅŅ…", "notification_permission_list_tile_title": "ĐœŅĐ´ŅĐŗĐ´ĐģиКĐŊ ŅŅ€Ņ…", - "notification_toggle_setting_description": "", - "notifications": "", - "notifications_setting_description": "", - "oauth": "", - "offline": "", - "ok": "", - "oldest_first": "", - "on_this_device": "On this device", - "online": "", "only_favorites": "Đ—ĶŠĐ˛Ņ…ĶŠĐŊ Đ´ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´", - "open_the_search_filters": "", - "options": "", - "organize_your_library": "", - "other": "", - "other_devices": "", - "other_variables": "", - "owned": "", - "owner": "", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", - "partner_sharing": "", - "partners": "", - "password": "", - "password_does_not_match": "", - "password_required": "", - "password_reset_success": "", - "past_durations": { - "days": "", - "hours": "", - "years": "" - }, - "path": "", - "pattern": "", - "pause": "", - "pause_memories": "", - "paused": "", - "pending": "", "people": "ĐĨŌ¯Đŧԝԝҁ", - "people_sidebar_description": "", - "permanent_deletion_warning": "", - "permanent_deletion_warning_setting_description": "", - "permanently_delete": "", - "permanently_deleted_asset": "", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", - "photos": "", - "photos_from_previous_years": "", - "pick_a_location": "", - "place": "", "places": "Đ‘Đ°ĐšŅ€ŅˆĐ¸ĐģŅƒŅƒĐ´", - "play": "", - "play_memories": "", - "play_motion_photo": "", - "play_or_pause_video": "", - "port": "", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", - "preset": "", - "preview": "", - "previous": "", - "previous_memory": "", - "previous_or_next_photo": "", - "primary": "", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", - "profile_picture_set": "", - "public_share": "", - "reaction_options": "", - "read_changelog": "", - "recent": "", - "recent_searches": "", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", - "refresh": "", - "refreshed": "", - "refreshes_every_file": "", - "remove": "", - "remove_deleted_assets": "", - "remove_from_album": "", "remove_from_favorites": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´Đ°Đ°Ņ Ņ…Đ°ŅĐ°Ņ…", - "remove_from_shared_link": "", "removed_from_favorites": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´Đ°Đ°Ņ Ņ…Đ°ŅĐ°ĐŗĐ´ŅĐ°ĐŊ", "removed_from_favorites_count": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´Đ°Đ°Ņ {count, plural, other {Removed #}} Ņ…Đ°ŅĐ°ĐŗĐ´Đģаа", - "repair": "", - "repair_no_results_message": "", - "replace_with_upload": "", - "require_password": "", - "reset": "", - "reset_password": "", - "reset_people_visibility": "", - "restore": "", - "restore_user": "", - "retry_upload": "", - "review_duplicates": "", - "role": "", - "save": "", - "save_to_gallery": "Save to gallery", - "saved_profile": "", - "saved_settings": "", - "say_something": "", - "scaffold_body_error_occurred": "Error occurred", - "scan_all_libraries": "", - "scan_settings": "", - "search": "", - "search_albums": "", - "search_by_context": "", - "search_camera_make": "", - "search_camera_model": "", - "search_city": "", - "search_country": "", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", - "search_for_existing_person": "", - "search_no_more_result": "No more results", - "search_no_result": "No results found, try a different search term or combination", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", - "search_people": "", "search_places": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ Ņ…Đ°ĐšŅ…", - "search_result_page_new_search_hint": "New Search", - "search_state": "", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", - "search_timezone": "", - "search_type": "", "search_your_photos": "Đ—ŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´Đ°Đ°ŅĐ°Đ° Ņ…Đ°ĐšĐģŅ‚ Ņ…Đ¸ĐšŅ…", - "searching_locales": "", - "second": "", - "select_album_cover": "", - "select_all": "", - "select_avatar_color": "", - "select_face": "", - "select_featured_photo": "", - "select_library_owner": "", - "select_new_face": "", - "select_photos": "", - "select_user_for_sharing_page_err_album": "Failed to create album", - "selected": "", - "send_message": "", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", - "server_info_box_server_url": "Server URL", "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ОĐŊĐģаКĐŊ", - "server_stats": "", - "set": "", - "set_as_album_cover": "", - "set_as_profile_picture": "", - "set_date_of_birth": "", - "set_profile_picture": "", - "set_slideshow_to_fullscreen": "", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "setting_notifications_subtitle": "Adjust your notification preferences", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "ĐĸĐžŅ…Đ¸Ņ€ĐŗĐžĐž", - "settings_require_restart": "Please restart Immich to apply this setting", - "settings_saved": "", - "share": "", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", - "shared": "", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", - "shared_by": "", - "shared_by_you": "", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", - "shared_links": "", - "shared_with_me": "Shared with me", "sharing": "ĐĨŅƒĐ˛Đ°Đ°ĐģŅ†Đ°Ņ…", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", - "sharing_sidebar_description": "", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", - "show_album_options": "", - "show_file_location": "", - "show_gallery": "", - "show_hidden_people": "", - "show_in_timeline": "", - "show_in_timeline_setting_description": "", - "show_keyboard_shortcuts": "", - "show_metadata": "", - "show_or_hide_info": "", - "show_password": "", - "show_person_options": "", - "show_progress_bar": "", - "show_search_options": "", - "shuffle": "", "sign_out": "Đ“Đ°Ņ€Đ°Ņ…", - "sign_up": "", - "size": "", - "skip_to_content": "", - "slideshow": "", - "slideshow_settings": "", - "sort_albums_by": "", - "stack": "", - "stack_selected_photos": "", - "stacktrace": "", - "start_date": "", - "state": "", - "status": "", - "stop_motion_photo": "", "storage": "Đ”Đ¸ŅĐēĐŊиК ĐąĐ°ĐŗŅ‚Đ°Đ°ĐŧĐļ", - "storage_label": "", "storage_usage": "ĐĐ¸ĐšŅ‚ {available} йОĐģĐžĐŧĐļŅ‚ĐžĐšĐŗĐžĐžŅ {used} Ņ…ŅŅ€ŅĐŗĐģŅŅŅĐŊ", - "submit": "", - "suggestions": "", - "sunrise_on_the_beach": "", - "swap_merge_direction": "", - "sync": "", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", - "template": "", - "theme": "", - "theme_selection": "", - "theme_selection_description": "", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", - "time_based_memories": "", - "timezone": "", - "toggle_settings": "", - "toggle_theme": "", - "total_usage": "", "trash": "ĐĨĐžĐŗĐ¸ĐšĐŊ ŅĐ°Đ˛", - "trash_all": "", - "trash_emptied": "Emptied trash", - "trash_no_results_message": "", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", - "type": "", - "unarchive": "", - "unfavorite": "", - "unhide_person": "", - "unknown": "", - "unknown_year": "", - "unlink_oauth": "", - "unlinked_oauth_account": "", - "unselect_all": "", - "unstack": "", - "up_next": "", - "updated_password": "", "upload": "Đ—ŅƒŅ€Đ°Đŗ Ņ…ŅƒŅƒĐģĐ°Ņ…", - "upload_concurrency": "", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "", - "usage": "", - "use_current_connection": "use current connection", - "user": "", - "user_id": "", - "user_usage_detail": "", - "username": "", - "users": "", "utilities": "Đ‘Đ°ĐŗĐ°Đļ Ņ…ŅŅ€ŅĐŗŅŅĐģ", - "validate": "", - "validate_endpoint_error": "Please enter a valid URL", - "variables": "", - "version": "", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available 🎉", - "video": "", - "video_hover_setting_description": "", - "videos": "", "view_all": "Đ‘Ō¯ĐŗĐ´Đ¸ĐšĐŗ Ņ…Đ°Ņ€Đ°Ņ…", "view_all_users": "Đ‘Ō¯Ņ… Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŗ Ņ…Đ°Ņ€Đ°Ņ…", - "view_links": "", - "view_next_asset": "", - "view_previous_asset": "", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", "waiting": "ĐĨŌ¯ĐģŅŅĐļ йаКĐŊа", "warning": "АĐŊŅ…Đ°Đ°Ņ€ŅƒŅƒĐģĐŗĐ°", "week": "ДоĐģОО Ņ…ĐžĐŊĐžĐŗ", @@ -1277,6 +136,5 @@ "years_ago": "{years, plural, one {# year} other {# years}} ĶŠĐŧĐŊĶŠ", "yes": "ĐĸиКĐŧ", "you_dont_have_any_shared_links": "ĐĸаĐŊĐ´ Ņ…ŅƒĐ˛Đ°Đ°ĐģŅ†ŅĐ°ĐŊ Ņ…ĐžĐģĐąĐžĐžŅ аĐģĐŗĐ°", - "your_wifi_name": "Your WiFi name", "zoom_image": "Đ—ŅƒŅ€ĐŗĐ¸ĐšĐŗ Ņ‚ĐžĐŧŅ€ŅƒŅƒĐģĐļ Ņ…Đ°Ņ€Đ°Ņ…" } diff --git a/i18n/ms.json b/i18n/ms.json index 5409b0e306..d5af40720d 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -22,6 +22,7 @@ "add_photos": "Tambah gambar", "add_to": "Tambah keâ€Ļ", "add_to_album": "Tambah ke album", + "add_to_album_bottom_sheet_already_exists": "Sudah ada di {album}", "add_to_shared_album": "Tambah ke album yang dikongsi", "add_url": "Tambah URL", "added_to_archive": "Tambah ke arkib", @@ -185,20 +186,13 @@ "oauth_auto_register": "Daftar secara automatik", "oauth_auto_register_description": "Daftar secara automatik pengguna-pengguna baharu selepas mendaftar masuk dengan OAuth", "oauth_button_text": "Teks butang", - "oauth_client_id": "ID Pelanggan", - "oauth_client_secret": "Rahsia Pelanggan", "oauth_enable_description": "Log masuk dengan OAuth", - "oauth_issuer_url": "URL Pengeluar", "oauth_mobile_redirect_uri": "URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override": "Penggantian URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti '{callback}'", - "oauth_profile_signing_algorithm": "Algoritma tandatangan profil", - "oauth_profile_signing_algorithm_description": "Algoritma digunakan untuk menandatangani profil pengguna.", - "oauth_scope": "Skop", "oauth_settings": "OAuth", "oauth_settings_description": "Urus tetapan-tetapan log masuk OAuth", "oauth_settings_more_details": "Untuk maklumat lanjut tentang ciri ini, rujuk ke dokumen.", - "oauth_signing_algorithm": "Tandatangan algoritma", "oauth_storage_label_claim": "Tuntutan label storan", "oauth_storage_label_claim_description": "Tetapkan label storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_claim": "Tuntutan kuota storan", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index df2fba7517..b5dd76ce56 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -26,6 +26,7 @@ "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_locked_folder": "Legg til i lÃĨst mappe", "add_to_shared_album": "Legg til delt album", "add_url": "Legg til URL", "added_to_archive": "Lagt til i arkiv", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Er du sikker pÃĨ at du ønsker ÃĨ deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", "authentication_settings_reenable": "For ÃĨ aktivere pÃĨ nytt, bruk en Server Command.", "background_task_job": "Bakgrunnsjobber", - "backup_database": "Backupdatabase", - "backup_database_enable_description": "Aktiver databasebackup", - "backup_keep_last_amount": "Antall backuper ÃĨ beholde", - "backup_settings": "Backupinnstillinger", - "backup_settings_description": "HÃĨndter innstillinger for databasebackup", + "backup_database": "Opprett database-dump", + "backup_database_enable_description": "Aktiver database-dump", + "backup_keep_last_amount": "Antall database-dumps ÃĨ beholde", + "backup_settings": "Database-dump instillinger", + "backup_settings_description": "HÃĨndter innstillinger for database-dump. Merk: Disse jobbene overvÃĨkes ikke, og du vil ikke bli varslet ved feil.", "check_all": "Merk Alle", "cleanup": "Opprydding", "cleared_jobs": "Ryddet opp jobber for: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "For ÃĨ bekrefte, skriv inn \"{email}\" nedenfor", "confirm_reprocess_all_faces": "Er du sikker pÃĨ at du vil behandle alle ansikter pÃĨ nytt? Dette vil ogsÃĨ fjerne navngitte personer.", "confirm_user_password_reset": "Er du sikker pÃĨ at du vil tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Er du sikker pÃĨ at du vil resette {user}'s PIN kode?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -85,7 +87,7 @@ "image_quality": "Kvalitet", "image_resolution": "Oppløsning", "image_resolution_description": "Høyere oppløsninger kan bevare flere detaljer, men det tar lengre tid ÃĨ kode, har større filstørrelser og kan redusere appresponsen.", - "image_settings": "Bildeinnstilliinger", + "image_settings": "Bildeinnstillinger", "image_settings_description": "Administrer kvalitet og oppløsning pÃĨ genererte bilder", "image_thumbnail_description": "SmÃĨ miniatyrbilder med strippet metadata, brukt nÃĨr du ser pÃĨ grupper av bilder som hovedtidslinjen", "image_thumbnail_quality_description": "Miniatyrbildekvalitet fra 1-100. Høyere er bedre, men produserer større filer og kan redusere appens respons.", @@ -192,26 +194,22 @@ "oauth_auto_register": "Automatisk registrering", "oauth_auto_register_description": "Registrer automatisk nye brukere etter innlogging med OAuth", "oauth_button_text": "Knappetekst", - "oauth_client_id": "Klient-ID", - "oauth_client_secret": "Klient hemmelighet", + "oauth_client_secret_description": "Kreves hvis PKCE (Proof Key for Code Exchange) ikke støttes av OAuth-leverandøren", "oauth_enable_description": "Logg inn med OAuth", - "oauth_issuer_url": "Utgiverens URL", "oauth_mobile_redirect_uri": "Mobil omdirigerings-URI", "oauth_mobile_redirect_uri_override": "Mobil omdirigerings-URI overstyring", "oauth_mobile_redirect_uri_override_description": "Aktiver nÃĨr OAuth-leverandøren ikke tillater en mobil URI, som '{callback}'", - "oauth_profile_signing_algorithm": "Profilsigneringsalgoritme", - "oauth_profile_signing_algorithm_description": "Algoritme brukt for ÃĨ signere brukerprofilen.", - "oauth_scope": "Omfang", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", - "oauth_signing_algorithm": "Signeringsalgoritme", "oauth_storage_label_claim": "Lagringsetikettkrav", "oauth_storage_label_claim_description": "Sett automatisk brukerens lagringsetikett til verdien av dette kravet.", "oauth_storage_quota_claim": "Lagringskvotekrav", "oauth_storage_quota_claim_description": "Sett automatisk brukerens lagringskvote til verdien av dette kravet.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", "oauth_storage_quota_default_description": "Kvote i GiB som skal brukes nÃĨr ingen krav er oppgitt (Skriv 0 for ubegrenset kvote).", + "oauth_timeout": "Forespørselen tok for lang tid", + "oauth_timeout_description": "Tidsavbrudd for forespørsel i millisekunder", "offline_paths": "Frakoblede filstier", "offline_paths_description": "Disse resultatene kan skyldes manuell sletting av filer som ikke er en del av et eksternt bibliotek.", "password_enable_description": "Logg inn med e-post og passord", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "Antall dager etter fjerning før en brukerkonto og dens filer permanent slettes. Brukerfjerningsjobben kjører ved midnatt for ÃĨ sjekke etter brukere som er klare for sletting. Endringer i denne innstillingen vil bli evaluert ved neste utførelse.", "user_delete_immediately": "{user}s konto og elementer vil bli lagt i kø for permanent sletting umiddelbart.", "user_delete_immediately_checkbox": "Legg bruker og elementer i kø for umiddelbar sletting", + "user_details": "Brukerdetaljer", "user_management": "Brukeradministrasjon", "user_password_has_been_reset": "Passordet til brukeren har blitt tilbakestilt:", "user_password_reset_description": "Vennligst oppgi det midlertidige passordet til brukeren og informer dem om at de mÃĨ endre passordet ved neste pÃĨlogging.", @@ -371,13 +370,17 @@ "admin_password": "Administrator Passord", "administration": "Administrasjon", "advanced": "Avansert", - "advanced_settings_log_level_title": "LoggnivÃĨ: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for ÃĨ filtrere mediefiler under synkronisering basert pÃĨ alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", + "advanced_settings_log_level_title": "LoggnivÃĨ: {level}", "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til ÃĨ hente mikrobilder fra enheten. Aktiver denne innstillingen for ÃĨ hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", - "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest ", + "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest", "advanced_settings_proxy_headers_title": "Proxy headere", "advanced_settings_self_signed_ssl_subtitle": "Hopper over SSL sertifikatverifikasjon for server-endepunkt. PÃĨkrevet for selvsignerte sertifikater.", "advanced_settings_self_signed_ssl_title": "Tillat selvsignerte SSL sertifikater", + "advanced_settings_sync_remote_deletions_subtitle": "Automatisk slette eller gjenopprette filer pÃĨ denne enheten hvis den handlingen har blitt gjort pÃĨ nettsiden", + "advanced_settings_sync_remote_deletions_title": "Synk sletting fra nettsiden [EKSPERIMENTELT]", "advanced_settings_tile_subtitle": "Avanserte brukerinnstillinger", "advanced_settings_troubleshooting_subtitle": "Aktiver ekstra funksjoner for feilsøking", "advanced_settings_troubleshooting_title": "Feilsøking", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Er du sikker pÃĨ at du vil fjerne {user}?", "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere ÃĨ dele det med.", "album_thumbnail_card_item": "1 objekt", - "album_thumbnail_card_items": "{} objekter", + "album_thumbnail_card_items": "{count} objekter", "album_thumbnail_card_shared": " ¡ Delt", - "album_thumbnail_shared_by": "Delt av {}", + "album_thumbnail_shared_by": "Delt av {user}", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling nÃĨr et delt album fÃĨr nye filer", "album_user_left": "Forlot {album}", @@ -440,7 +443,7 @@ "archive": "Arkiver", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", - "archive_page_title": "Arkiv ({})", + "archive_page_title": "Arkiv ({count})", "archive_size": "Arkivstørrelse", "archive_size_description": "Konfigurer arkivstørrelsen for nedlastinger (i GiB)", "archived": "Arkivert", @@ -465,7 +468,7 @@ "asset_list_settings_title": "Fotorutenett", "asset_offline": "Fil utilgjengelig", "asset_offline_description": "Dette elementet er offline. Immich kan ikke aksessere dets lokasjon. Vennlist pÃĨse at elementet er tilgijengelig og skann sÃĨ biblioteket pÃĨ nytt.", - "asset_restored_successfully": "{} objekt(er) Gjenopprettet", + "asset_restored_successfully": "Objekt(er) gjenopprettet", "asset_skipped": "Hoppet over", "asset_skipped_in_trash": "I søppelbøtten", "asset_uploaded": "Lastet opp", @@ -477,18 +480,18 @@ "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", "assets_added_to_name_count": "Lagt til {count, plural, one {# asset} other {# assets}} i {hasName, select, true {{name}} other {new album}}", "assets_count": "{count, plural, one {# fil} other {# filer}}", - "assets_deleted_permanently": "{} objekt(er) Slettet permanent", - "assets_deleted_permanently_from_server": "{} objekt(er) slettet permanent fra Immich serveren", + "assets_deleted_permanently": "{count} objekt(er) slettet permanent", + "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_permanently_from_device": "{} objekt(er) slettet permanent fra enheten din", + "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", "assets_restore_confirmation": "Er du sikker pÃĨ at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! VÃĻr oppmerksom pÃĨ at frakoblede ressurser ikke kan gjenopprettes pÃĨ denne mÃĨten.", "assets_restored_count": "Gjenopprettet {count, plural, one {# asset} other {# assets}}", - "assets_restored_successfully": "{} objekt(er) gjenopprettet", - "assets_trashed": "{} objekt(er) slettet", + "assets_restored_successfully": "{count} objekt(er) gjenopprettet", + "assets_trashed": "{count} objekt(er) slettet", "assets_trashed_count": "Kastet {count, plural, one {# asset} other {# assets}}", - "assets_trashed_from_server": "{} objekt(er) slettet fra Immich serveren", + "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} er allerede lagt til i albumet", "authorized_devices": "Autoriserte enheter", "automatic_endpoint_switching_subtitle": "Koble til lokalt over angitt Wi-Fi nÃĨr det er tilgjengelig, og bruk alternative tilkoblinger andre steder", @@ -497,46 +500,45 @@ "back_close_deselect": "Tilbake, lukk eller fjern merking", "background_location_permission": "Bakgrunnstillatelse for plassering", "background_location_permission_content": "For ÃĨ bytte nettverk nÃĨr du kjører i bakgrunnen, mÃĨ Immich *alltid* ha presis posisjonstilgang slik at appen kan lese Wi-Fi-nettverkets navn", - "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({})", + "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({count})", "backup_album_selection_page_albums_tap": "Trykk for ÃĨ inkludere, dobbelttrykk for ÃĨ ekskludere", "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", "backup_album_selection_page_select_albums": "Velg album", "backup_album_selection_page_selection_info": "Valginformasjon", "backup_album_selection_page_total_assets": "Totalt antall unike objekter", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Sikkerhetskopiering av objekter feilet. Prøver pÃĨ nytt ...", - "backup_background_service_connection_failed_message": "Tilkobling til server feilet. Prøver pÃĨ nytt ...", - "backup_background_service_current_upload_notification": "Laster opp {}", - "backup_background_service_default_notification": "Ser etter nye objekter ...", + "backup_background_service_backup_failed_message": "Sikkerhetskopiering av objekter feilet. Prøver pÃĨ nyttâ€Ļ", + "backup_background_service_connection_failed_message": "Tilkobling til server feilet. Prøver pÃĨ nyttâ€Ļ", + "backup_background_service_current_upload_notification": "Laster opp {filename}", + "backup_background_service_default_notification": "Ser etter nye objekterâ€Ļ", "backup_background_service_error_title": "Sikkerhetskopieringsfeil", - "backup_background_service_in_progress_notification": "Sikkerhetskopierer objekter ...", - "backup_background_service_upload_failure_notification": "Opplasting feilet {}", + "backup_background_service_in_progress_notification": "Sikkerhetskopierer objekterâ€Ļ", + "backup_background_service_upload_failure_notification": "Opplasting feilet {filename}", "backup_controller_page_albums": "Sikkerhetskopier albumer", "backup_controller_page_background_app_refresh_disabled_content": "Aktiver bakgrunnsoppdatering i Innstillinger > Generelt > Bakgrunnsoppdatering for ÃĨ bruke sikkerhetskopiering i bakgrunnen.", "backup_controller_page_background_app_refresh_disabled_title": "Bakgrunnsoppdateringer er deaktivert", "backup_controller_page_background_app_refresh_enable_button_text": "GÃĨ til innstillinger", "backup_controller_page_background_battery_info_link": "Vis meg hvordan", "backup_controller_page_background_battery_info_message": "For at sikkerhetskopiering i bakgrunnen skal fungere optimalt, deaktiver enhver batterioptimalisering som kan begrense bakgrunnsaktiviteten til Immich.\n\nSiden dette er en enhetsspesifikk justering, mÃĨ du finne det i innstillingene pÃĨ enheten din.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Batterioptimalisering", "backup_controller_page_background_charging": "Kun ved lading", "backup_controller_page_background_configure_error": "Konfigurering av bakgrunnstjenesten feilet", - "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {}", + "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {duration}", "backup_controller_page_background_description": "Skru pÃĨ bakgrunnstjenesten for ÃĨ automatisk sikkerhetskopiere alle nye objekter uten ÃĨ mÃĨtte ÃĨpne appen", "backup_controller_page_background_is_off": "Automatisk sikkerhetskopiering i bakgrunnen er deaktivert", "backup_controller_page_background_is_on": "Automatisk sikkerhetskopiering i bakgrunnen er aktivert", "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", "backup_controller_page_background_turn_on": "Skru pÃĨ bakgrunnstjenesten", - "backup_controller_page_background_wifi": "Kun pÃĨ WiFi", + "backup_controller_page_background_wifi": "Kun pÃĨ Wi-Fi", "backup_controller_page_backup": "Sikkerhetskopier", - "backup_controller_page_backup_selected": "Valgte:", + "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", - "backup_controller_page_created": "Opprettet: {}", + "backup_controller_page_created": "Opprettet: {date}", "backup_controller_page_desc_backup": "SlÃĨ pÃĨ sikkerhetskopiering i forgrunnen for automatisk ÃĨ laste opp nye objekter til serveren nÃĨr du ÃĨpner appen.", - "backup_controller_page_excluded": "Ekskludert:", - "backup_controller_page_failed": "Feilet ({})", - "backup_controller_page_filename": "Filnavn: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Ekskludert: ", + "backup_controller_page_failed": "Feilet ({count})", + "backup_controller_page_filename": "Filnavn: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informasjon om sikkerhetskopi", "backup_controller_page_none_selected": "Ingen valgt", "backup_controller_page_remainder": "GjenstÃĨr", @@ -545,7 +547,7 @@ "backup_controller_page_start_backup": "Start sikkerhetskopiering", "backup_controller_page_status_off": "Automatisk sikkerhetskopiering i forgrunnen er av", "backup_controller_page_status_on": "Automatisk sikkerhetskopiering i forgrunnen er pÃĨ", - "backup_controller_page_storage_format": "{} av {} brukt", + "backup_controller_page_storage_format": "{used} av {total} brukt", "backup_controller_page_to_backup": "Albumer som skal sikkerhetskopieres", "backup_controller_page_total_sub": "Alle unike bilder og videoer fra valgte album", "backup_controller_page_turn_off": "SlÃĨ av sikkerhetskopiering i forgrunnen", @@ -560,6 +562,10 @@ "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", "backward": "Bakover", + "biometric_auth_enabled": "Biometrisk autentisering aktivert", + "biometric_locked_out": "Du er lÃĨst ute av biometrisk verifisering", + "biometric_no_options": "Ingen biometriske valg tilgjengelige", + "biometric_not_available": "Biometrisk autentisering er ikke tilgjengelig pÃĨ denne enheten", "birthdate_saved": "Fødselsdato er vellykket lagret", "birthdate_set_description": "Fødelsdatoen er brukt for ÃĨ beregne alderen til denne personen ved tidspunktet til bildet.", "blurred_background": "Uskarp bakgrunn", @@ -570,21 +576,21 @@ "bulk_keep_duplicates_confirmation": "Er du sikker pÃĨ at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil løse alle dupliserte grupper uten ÃĨ slette noe.", "bulk_trash_duplicates_confirmation": "Er du sikker pÃĨ ønsker ÃĨ slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", - "cache_settings_album_thumbnails": "Bibliotekminiatyrbilder ({} objekter)", + "cache_settings_album_thumbnails": "Bibliotekminiatyrbilder ({count} objekter)", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning pÃĨ appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", - "cache_settings_duplicated_assets_title": "Dupliserte objekter ({})", - "cache_settings_image_cache_size": "Størrelse pÃĨ bildebuffer ({} objekter)", + "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", + "cache_settings_image_cache_size": "Størrelse pÃĨ bildebuffer ({count} objekter)", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", - "cache_settings_statistics_assets": "{} objekter ({})", + "cache_settings_statistics_assets": "{count} objekter ({size})", "cache_settings_statistics_full": "Originalbilder", "cache_settings_statistics_shared": "Delte albumminiatyrbilder", "cache_settings_statistics_thumbnail": "Miniatyrbilder", "cache_settings_statistics_title": "Bufferbruk", "cache_settings_subtitle": "Kontroller bufringsadferden til Immich-appen", - "cache_settings_thumbnail_size": "Størrelse pÃĨ miniatyrbildebuffer ({} objekter)", + "cache_settings_thumbnail_size": "Størrelse pÃĨ miniatyrbildebuffer ({count} objekter)", "cache_settings_tile_subtitle": "Kontroller lokal lagring", "cache_settings_tile_title": "Lokal lagring", "cache_settings_title": "Bufringsinnstillinger", @@ -598,6 +604,7 @@ "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", "change_date": "Endre dato", + "change_description": "Endre beskrivelsen", "change_display_order": "Endre visningsrekkefølge", "change_expiration_time": "Endre utløpstid", "change_location": "Endre sted", @@ -610,12 +617,13 @@ "change_password_form_new_password": "Nytt passord", "change_password_form_password_mismatch": "Passordene stemmer ikke", "change_password_form_reenter_new_password": "Skriv nytt passord igjen", + "change_pin_code": "Endre PIN kode", "change_your_password": "Endre passordet ditt", "changed_visibility_successfully": "Endret synlighet vellykket", "check_all": "Sjekk alle", "check_corrupt_asset_backup": "Sjekk etter korrupte backupobjekter", "check_corrupt_asset_backup_button": "Utfør sjekk", - "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og nÃĨr alle objekter har blitt lastet opp.\nDenne sjekken kan ta noen minutter.", + "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og nÃĨr alle objekter har blitt lastet opp. Denne sjekken kan ta noen minutter.", "check_logs": "Sjekk Logger", "choose_matching_people_to_merge": "Velg personer som skal slÃĨs sammen", "city": "By", @@ -624,13 +632,12 @@ "clear_all_recent_searches": "Fjern alle nylige søk", "clear_message": "Fjern melding", "clear_value": "Fjern verdi", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Skriv inn passord", "client_cert_import": "Importer", "client_cert_import_success_msg": "Klient sertifikat er importert", "client_cert_invalid_msg": "Ugyldig sertifikat eller feil passord", "client_cert_remove_msg": "Klient sertifikat er fjernet", - "client_cert_subtitle": "Støtter kun PKCS12 (.p12, .pfx) formater. Importering/Fjerning av sertifikater er kun mulig før innlogging.", + "client_cert_subtitle": "Støtter kun PKCS12 (.p12, .pfx) formater. Importering/Fjerning av sertifikater er kun mulig før innlogging", "client_cert_title": "SSL Klient sertifikat", "clockwise": "Med urviseren", "close": "Lukk", @@ -650,17 +657,18 @@ "confirm_delete_face": "Er du sikker pÃĨ at du vil slette {name} sitt ansikt fra ativia?", "confirm_delete_shared_link": "Er du sikker pÃĨ at du vil slette denne delte lenken?", "confirm_keep_this_delete_others": "Alle andre ressurser i denne stabelen vil bli slettet bortsett fra denne ressursen. Er du sikker pÃĨ at du vil fortsette?", + "confirm_new_pin_code": "Bekreft ny PIN kode", "confirm_password": "Bekreft passord", "contain": "Inneholder", "context": "Kontekst", "continue": "Fortsett", - "control_bottom_app_bar_album_info_shared": "{} objekter ¡ Delt", + "control_bottom_app_bar_album_info_shared": "{count} objekter ¡ Delt", "control_bottom_app_bar_create_new_album": "Lag nytt album", "control_bottom_app_bar_delete_from_immich": "Slett fra Immich", "control_bottom_app_bar_delete_from_local": "Slett fra enhet", "control_bottom_app_bar_edit_location": "Endre lokasjon", "control_bottom_app_bar_edit_time": "Endre Dato og tid", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Del Lenke", "control_bottom_app_bar_share_to": "Del til", "control_bottom_app_bar_trash_from_immich": "Flytt til søppelkasse", "copied_image_to_clipboard": "Bildet er kopiert til utklippstavlen.", @@ -692,19 +700,21 @@ "create_tag_description": "Lag en ny tag. For undertag, vennligst fullfør hele stien til taggen, inkludert forovervendt skrÃĨstrek.", "create_user": "Opprett Bruker", "created": "Opprettet", + "created_at": "Laget", "crop": "BeskjÃĻr", "curated_object_page_title": "Ting", "current_device": "NÃĨvÃĻrende enhet", + "current_pin_code": "NÃĨvÃĻrende PIN kode", "current_server_address": "NÃĨvÃĻrende serveradresse", "custom_locale": "Tilpasset lokalisering", "custom_locale_description": "Formater datoer og tall basert pÃĨ sprÃĨk og region", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "E MMM. dd", + "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "d LLL. E y â€ĸ hh:mm", "date_of_birth_saved": "Fødselsdatoen ble lagret vellykket", "date_range": "DatoomrÃĨde", "day": "Dag", @@ -746,7 +756,6 @@ "direction": "Retning", "disabled": "Deaktivert", "disallow_edits": "Forby redigering", - "discord": "Discord", "discover": "Oppdag", "dismiss_all_errors": "Avvis alle feil", "dismiss_error": "Avvis feil", @@ -763,7 +772,7 @@ "download_enqueue": "Nedlasting satt i kø", "download_error": "Nedlasting feilet", "download_failed": "Nedlasting feilet", - "download_filename": "fil: {}", + "download_filename": "fil: {filename}", "download_finished": "Nedlasting fullført", "download_include_embedded_motion_videos": "Innebygde videoer", "download_include_embedded_motion_videos_description": "Inkluder innebygde videoer i levende bilder som en egen fil", @@ -787,6 +796,8 @@ "edit_avatar": "Rediger avatar", "edit_date": "Rediger dato", "edit_date_and_time": "Rediger dato og tid", + "edit_description": "Endre beskrivelse", + "edit_description_prompt": "Vennligst velg en ny beskrivelse:", "edit_exclusion_pattern": "Rediger eksklusjonsmønster", "edit_faces": "Rediger ansikter", "edit_import_path": "Rediger import-sti", @@ -807,19 +818,23 @@ "editor_crop_tool_h2_aspect_ratios": "Sideforhold", "editor_crop_tool_h2_rotation": "Rotasjon", "email": "E-postadresse", - "empty_folder": "This folder is empty", + "email_notifications": "Epostvarsler", + "empty_folder": "Denne mappen er tom", "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker pÃĨ at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", + "enable_biometric_auth_description": "Skriv inn PINkoden for ÃĨ aktivere biometrisk autentisering", "enabled": "Aktivert", "end_date": "Slutt dato", "enqueued": "I kø", "enter_wifi_name": "Skriv inn Wi-Fi navn", + "enter_your_pin_code": "Skriv inn din PIN kode", + "enter_your_pin_code_subtitle": "Skriv inn din PIN kode for ÃĨ fÃĨ tilgang til lÃĨst mappe", "error": "Feil", "error_change_sort_album": "Feilet ved endring av sorteringsrekkefølge pÃĨ albumer", "error_delete_face": "Feil ved sletting av ansikt fra aktivia", "error_loading_image": "Feil ved lasting av bilde", - "error_saving_image": "Feil: {}", + "error_saving_image": "Feil: {error}", "error_title": "Feil - Noe gikk galt", "errors": { "cannot_navigate_next_asset": "Kan ikke navigere til neste fil", @@ -849,10 +864,12 @@ "failed_to_keep_this_delete_others": "Feilet med ÃĨ beholde dette bilde og slette de andre", "failed_to_load_asset": "Feilet med ÃĨ laste bilder", "failed_to_load_assets": "Feilet med ÃĨ laste bilde", + "failed_to_load_notifications": "Kunne ikke laste inn varsler", "failed_to_load_people": "Feilen med ÃĨ laste mennesker", "failed_to_remove_product_key": "Feilet med ÃĨ ta bort produkt nøkkel", "failed_to_stack_assets": "Feilet med ÃĨ stable bilder", "failed_to_unstack_assets": "Feilet med ÃĨ avstable bilder", + "failed_to_update_notification_status": "Kunne ikke oppdatere varslingsstatusen", "import_path_already_exists": "Denne importstien eksisterer allerede.", "incorrect_email_or_password": "Feil epost eller passord", "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", @@ -870,6 +887,7 @@ "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", "unable_to_change_date": "Kan ikke endre dato", + "unable_to_change_description": "Klarte ikke ÃĨ oppdatere beskrivelse", "unable_to_change_favorite": "Kan ikke endre favoritt for bildet", "unable_to_change_location": "Kan ikke endre plassering", "unable_to_change_password": "Kan ikke endre passord", @@ -907,6 +925,7 @@ "unable_to_log_out_all_devices": "Kan ikke logge ut fra alle enheter", "unable_to_log_out_device": "Kan ikke logge ut av enhet", "unable_to_login_with_oauth": "Kan ikke logge inn med OAuth", + "unable_to_move_to_locked_folder": "Klarte ikke ÃĨ flytte til lÃĨst mappe", "unable_to_play_video": "Kan ikke spille av video", "unable_to_reassign_assets_existing_person": "Kunne ikke endre bruker pÃĨ bildene til {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "Kunne ikke tildele bildene til en ny person", @@ -920,6 +939,7 @@ "unable_to_remove_reaction": "Kan ikke fjerne reaksjon", "unable_to_repair_items": "Kan ikke reparere elementer", "unable_to_reset_password": "Kan ikke tilbakestille passord", + "unable_to_reset_pin_code": "Klarte ikke ÃĨ resette PIN kode", "unable_to_resolve_duplicate": "Kan ikke løse duplikat", "unable_to_restore_assets": "Kan ikke gjenopprette filer", "unable_to_restore_trash": "Kan ikke gjenopprette papirkurven", @@ -953,10 +973,10 @@ "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", "exif_bottom_sheet_person_add_person": "Legg til navn", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Alder {age}", + "exif_bottom_sheet_person_age_months": "Alder {months} mÃĨneder", + "exif_bottom_sheet_person_age_year_months": "Alder 1 ÃĨr, {months} mÃĨneder", + "exif_bottom_sheet_person_age_years": "Alder {years}", "exit_slideshow": "Avslutt lysbildefremvisning", "expand_all": "Utvid alle", "experimental_settings_new_asset_list_subtitle": "Under utvikling", @@ -977,8 +997,9 @@ "external_network_sheet_info": "NÃĨr du ikke er pÃĨ det foretrukne Wi-Fi-nettverket, vil appen koble seg til serveren via den første av URL-ene nedenfor den kan nÃĨ, fra topp til bunn", "face_unassigned": "Ikke tilordnet", "failed": "Feilet", + "failed_to_authenticate": "Kunne ikke autentisere", "failed_to_load_assets": "Feilet med ÃĨ laste fil", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", @@ -990,12 +1011,12 @@ "file_name_or_extension": "Filnavn eller filtype", "filename": "Filnavn", "filetype": "Filtype", - "filter": "Filter", "filter_people": "Filtrer personer", + "filter_places": "Filtrer steder", "find_them_fast": "Finn dem raskt ved søking av navn", "fix_incorrect_match": "Fiks feilaktig match", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Mappe", + "folder_not_found": "Fant ikke mappe", "folders": "Mapper", "folders_feature_description": "Utforsker mappe visning for bilder og videoer pÃĨ fil systemet", "forward": "Fremover", @@ -1040,11 +1061,14 @@ "home_page_delete_remote_err_local": "Lokale objekter i fjernslettingsvalgene, hopper over", "home_page_favorite_err_local": "Kan ikke sette favoritt pÃĨ lokale objekter enda, hopper over", "home_page_favorite_err_partner": "Kan ikke merke partnerobjekter som favoritt enda, hopper over", - "home_page_first_time_notice": "Hvis dette er første gangen du benytter appen, velg et album (eller flere) for sikkerhetskopiering, slik at tidslinjen kan fylles med dine bilder og videoer.", + "home_page_first_time_notice": "Hvis dette er første gangen du benytter appen, velg et album (eller flere) for sikkerhetskopiering, slik at tidslinjen kan fylles med dine bilder og videoer", + "home_page_locked_error_local": "Kunne ikke flytte lokale objekter til lÃĨst mappe, hopper over", + "home_page_locked_error_partner": "Kunne ikke flytte partner objekter til lÃĨst mappe, hopper over", "home_page_share_err_local": "Kan ikke dele lokale objekter via link, hopper over", "home_page_upload_err_limit": "Maksimalt 30 objekter kan lastes opp om gangen, hopper over", "host": "Vert", "hour": "Time", + "id": "ID", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret pÃĨ iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1062,7 +1086,6 @@ "image_viewer_page_state_provider_download_started": "Nedlasting startet", "image_viewer_page_state_provider_download_success": "Nedlasting vellykket", "image_viewer_page_state_provider_share_error": "Delingsfeil", - "immich_logo": "Immich Logo", "immich_web_interface": "Immich webgrensesnitt", "import_from_json": "Importer fra JSON", "import_path": "Import-sti", @@ -1073,7 +1096,6 @@ "include_shared_partner_assets": "Inkluder delte partnerfiler", "individual_share": "Individuell deling", "individual_shares": "Individuelle delinger", - "info": "Info", "interval": { "day_at_onepm": "Hver dag klokken 13:00", "hours": "Hver {hours, plural, one {time} other {{hours, number} timer}}", @@ -1126,6 +1148,8 @@ "location_picker_latitude_hint": "Skriv inn breddegrad her", "location_picker_longitude_error": "Skriv inn en gyldig lengdegrad", "location_picker_longitude_hint": "Skriv inn lengdegrad her", + "lock": "LÃĨs", + "locked_folder": "LÃĨst mappe", "log_out": "Logg ut", "log_out_all_devices": "Logg ut fra alle enheter", "logged_out_all_devices": "Logg ut av alle enheter", @@ -1170,8 +1194,8 @@ "manage_your_devices": "Administrer dine innloggede enheter", "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", - "map_assets_in_bound": "{} bilde", - "map_assets_in_bounds": "{} bilder", + "map_assets_in_bound": "{count} bilde", + "map_assets_in_bounds": "{count} bilder", "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", @@ -1185,15 +1209,18 @@ "map_settings": "Kartinnstillinger", "map_settings_dark_mode": "Mørk modus", "map_settings_date_range_option_day": "Siste 24 timer", - "map_settings_date_range_option_days": "Siste {} dager", + "map_settings_date_range_option_days": "Siste {days} dager", "map_settings_date_range_option_year": "Sist ÃĨr", - "map_settings_date_range_option_years": "Siste {} ÃĨr", + "map_settings_date_range_option_years": "Siste {years} ÃĨr", "map_settings_dialog_title": "Kartinnstillinger", "map_settings_include_show_archived": "Inkluder arkiverte", "map_settings_include_show_partners": "Inkluder partner", "map_settings_only_show_favorites": "Vis kun favoritter", "map_settings_theme_settings": "Karttema", "map_zoom_to_see_photos": "Zoom ut for ÃĨ se bilder", + "mark_all_as_read": "Merk alle som lest", + "mark_as_read": "Merk som lest", + "marked_all_as_read": "Merket alle som lest", "matches": "Samsvarende", "media_type": "Mediatype", "memories": "Minner", @@ -1202,8 +1229,6 @@ "memories_setting_description": "Administrer hva du ser i minnene dine", "memories_start_over": "Start pÃĨ nytt", "memories_swipe_to_close": "Swipe opp for ÃĨ lukke", - "memories_year_ago": "Ett ÃĨr siden", - "memories_years_ago": "{} ÃĨr siden", "memory": "Minne", "memory_lane_title": "Minnefelt {title}", "menu": "Meny", @@ -1218,8 +1243,13 @@ "missing": "Mangler", "model": "Modell", "month": "MÃĨned", - "monthly_title_text_date_format": "MMMM y", "more": "Mer", + "move": "Flytt", + "move_off_locked_folder": "Flytt ut av lÃĨst mappe", + "move_to_locked_folder": "Flytt til lÃĨst mappe", + "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den lÃĨste mappen", + "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# asset} other {# assets}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato pÃĨ objekt(er) med kun lese-rettigheter, hopper over", "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon pÃĨ objekt(er) med kun lese-rettigheter, hopper over", @@ -1234,6 +1264,8 @@ "new_api_key": "Ny API-nøkkel", "new_password": "Nytt passord", "new_person": "Ny person", + "new_pin_code": "Ny PIN kode", + "new_pin_code_subtitle": "Dette er første gang du ÃĨpner den lÃĨste mappen. Lag en PIN kode for ÃĨ sikre tilgangen til denne siden", "new_user_created": "Ny bruker opprettet", "new_version_available": "NY VERSJON TILGJENGELIG", "newest_first": "Nyeste først", @@ -1251,7 +1283,10 @@ "no_explore_results_message": "Last opp flere bilder for ÃĨ utforske samlingen din.", "no_favorites_message": "Legg til favoritter for ÃĨ raskt finne dine beste bilder og videoer", "no_libraries_message": "Opprett et eksternt bibliotek for ÃĨ se bildene og videoene dine", + "no_locked_photos_message": "Bilder og videoer i den lÃĨste mappen er skjult og vil ikke vises nÃĨr du blar i biblioteket.", "no_name": "Ingen navn", + "no_notifications": "Ingen varsler", + "no_people_found": "Ingen samsvarende personer funnet", "no_places": "Ingen steder", "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", @@ -1260,19 +1295,18 @@ "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For ÃĨ bruke lagringsetiketten pÃĨ tidligere opplastede filer, kjør", "notes": "Notater", + "nothing_here_yet": "Ingenting her enda", "notification_permission_dialog_content": "For ÃĨ aktivere notifikasjoner, gÃĨ til Innstillinger og velg tillat.", - "notification_permission_list_tile_content": "Gi tilgang for ÃĨ aktivere notifikasjoner", + "notification_permission_list_tile_content": "Gi tilgang for ÃĨ aktivere notifikasjoner.", "notification_permission_list_tile_enable_button": "Aktiver notifikasjoner", "notification_permission_list_tile_title": "Notifikasjonstilgang", "notification_toggle_setting_description": "Aktiver e-postvarsler", "notifications": "Notifikasjoner", "notifications_setting_description": "Administrer varsler", - "oauth": "OAuth", "official_immich_resources": "Offisielle Immich Resurser", "offline": "Frakoblet", "offline_paths": "Frakoblede stier", "offline_paths_description": "Disse resultatene kan skyldes manuell sletting av filer som ikke er en del av et eksternt bibliotek.", - "ok": "Ok", "oldest_first": "Eldste først", "on_this_device": "PÃĨ denne enheten", "onboarding": "PÃĨmønstring", @@ -1282,19 +1316,18 @@ "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", + "open": "Åpne", "open_in_map_view": "Åpne i kartvisning", "open_in_openstreetmap": "Åpne i OpenStreetMap", "open_the_search_filters": "Åpne søkefiltrene", "options": "Valg", "or": "eller", "organize_your_library": "Organiser biblioteket ditt", - "original": "original", "other": "Annet", "other_devices": "Andre enheter", "other_variables": "Andre variabler", "owned": "Ditt album", "owner": "Eier", - "partner": "Partner", "partner_can_access": "{partner} har tilgang", "partner_can_access_assets": "Alle bildene og videoene dine unntatt de i arkivert og slettet tilstand", "partner_can_access_location": "Stedet der bildene dine ble tatt", @@ -1305,7 +1338,7 @@ "partner_page_partner_add_failed": "Klarte ikke ÃĨ legge til partner", "partner_page_select_partner": "Velg partner", "partner_page_shared_to_title": "Delt med", - "partner_page_stop_sharing_content": "{} vil ikke lenger ha tilgang til dine bilder.", + "partner_page_stop_sharing_content": "{partner} vil ikke lenger ha tilgang til dine bilder.", "partner_sharing": "Partnerdeling", "partners": "Partnere", "password": "Passord", @@ -1341,8 +1374,7 @@ "permission_onboarding_permission_denied": "Tilgang avvist. For ÃĨ bruke Immich, tillat ÃĨ vise bilde og videoer i Innstillinger.", "permission_onboarding_permission_granted": "Tilgang gitt! Du er i gang.", "permission_onboarding_permission_limited": "Begrenset tilgang. For ÃĨ la Immich sikkerhetskopiere og hÃĨndtere galleriet, tillatt bilde- og video-tilgang i Innstillinger.", - "permission_onboarding_request": "Immich trenger tilgang til ÃĨ se dine bilder og videoer", - "person": "Person", + "permission_onboarding_request": "Immich trenger tilgang til ÃĨ se dine bilder og videoer.", "person_birthdate": "Født den {date}", "person_hidden": "{name}{hidden, select, true { (skjult)} other {}}", "photo_shared_all_users": "Det ser ut som om du deler bildene med alle brukere eller det er ingen brukere ÃĨ dele med.", @@ -1351,6 +1383,10 @@ "photos_count": "{count, plural, one {{count, number} Bilde} other {{count, number} Bilder}}", "photos_from_previous_years": "Bilder fra tidliger ÃĨr", "pick_a_location": "Velg et sted", + "pin_code_changed_successfully": "Endring av PIN kode vellykket", + "pin_code_reset_successfully": "Vellykket resatt PIN kode", + "pin_code_setup_successfully": "Vellykket oppsett av PIN kode", + "pin_verification": "PINkode verifikasjon", "place": "Sted", "places": "Plasseringer", "places_count": "{count, plural, one {{count, number} Sted} other {{count, number} Steder}}", @@ -1358,7 +1394,7 @@ "play_memories": "Spill av minner", "play_motion_photo": "Spill av bevegelsesbilde", "play_or_pause_video": "Spill av eller pause video", - "port": "Port", + "please_auth_to_access": "Vennligst autentiser for ÃĨ fortsette", "preferences_settings_subtitle": "Administrer appens preferanser", "preferences_settings_title": "Innstillinger", "preset": "ForhÃĨndsinstilling", @@ -1368,11 +1404,11 @@ "previous_or_next_photo": "Forrige eller neste bilde", "primary": "PrimÃĻr", "privacy": "Privat", + "profile": "Profil", "profile_drawer_app_logs": "Logg", "profile_drawer_client_out_of_date_major": "Mobilapp er utdatert. Vennligst oppdater til nyeste versjon.", "profile_drawer_client_out_of_date_minor": "Mobilapp er utdatert. Vennligst oppdater til nyeste versjon.", "profile_drawer_client_server_up_to_date": "Klient og server er oppdatert", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server er utdatert. Vennligst oppdater til nyeste versjon.", "profile_drawer_server_out_of_date_minor": "Server er utdatert. Vennligst oppdater til nyeste versjon.", "profile_image_of_user": "Profil bilde av {user}", @@ -1381,7 +1417,7 @@ "public_share": "Offentlig deling", "purchase_account_info": "Støttespiller", "purchase_activated_subtitle": "Takk for at du støtter Immich og ÃĨpen kildekode programvare", - "purchase_activated_time": "Aktiver den {date, date}", + "purchase_activated_time": "Aktiver den {date}", "purchase_activated_title": "Du produktnøkkel har vellyket blitt aktivert", "purchase_button_activate": "Aktiver", "purchase_button_buy": "Kjøp", @@ -1409,7 +1445,6 @@ "purchase_remove_server_product_key_prompt": "Er du sikker pÃĨ at du vil ta bort Server Produktnøkkelen?", "purchase_server_description_1": "For hele serveren", "purchase_server_description_2": "Støttespiller status", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", @@ -1426,6 +1461,8 @@ "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", "recently_added_page_title": "Nylig lagt til", + "recently_taken": "Nylig tatt", + "recently_taken_page_title": "Nylig Tatt", "refresh": "Oppdater", "refresh_encoded_videos": "Oppdater kodete videoer", "refresh_faces": "Oppdater ansikter", @@ -1445,6 +1482,8 @@ "remove_deleted_assets": "Fjern fra frakoblede filer", "remove_from_album": "Fjern fra album", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_locked_folder": "Fjern fra lÃĨst mappe", + "remove_from_locked_folder_confirmation": "Er du sikker pÃĨ at du vil flytte disse bildene og videoene ut av den lÃĨste mappen? De vil bli synlige i biblioteket", "remove_from_shared_link": "Fjern fra delt lenke", "remove_memory": "Slett minne", "remove_photo_from_memory": "Slett bilde fra dette minne", @@ -1468,6 +1507,7 @@ "reset": "Tilbakestill", "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", + "reset_pin_code": "Resett PINkode", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", @@ -1479,7 +1519,6 @@ "retry_upload": "Prøv opplasting pÃĨ nytt", "review_duplicates": "GjennomgÃĨ duplikater", "role": "Rolle", - "role_editor": "Editor", "role_viewer": "Visning", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", @@ -1560,6 +1599,7 @@ "select_keep_all": "Velg beholde alle", "select_library_owner": "Velg bibliotekseier", "select_new_face": "Velg nytt ansikt", + "select_person_to_tag": "Velg en person ÃĨ tagge", "select_photos": "Velg bilder", "select_trash_all": "Velg ÃĨ flytte alt til papirkurven", "select_user_for_sharing_page_err_album": "Feilet ved oppretting av album", @@ -1590,27 +1630,28 @@ "setting_languages_apply": "Bekreft", "setting_languages_subtitle": "Endre app-sprÃĨk", "setting_languages_title": "SprÃĨk", - "setting_notifications_notify_failures_grace_period": "Varsle om sikkerhetskopieringsfeil i bakgrunnen: {}", - "setting_notifications_notify_hours": "{} timer", + "setting_notifications_notify_failures_grace_period": "Varsle om sikkerhetskopieringsfeil i bakgrunnen: {duration}", + "setting_notifications_notify_hours": "{count} timer", "setting_notifications_notify_immediately": "umiddelbart", - "setting_notifications_notify_minutes": "{} minutter", + "setting_notifications_notify_minutes": "{count} minutter", "setting_notifications_notify_never": "aldri", - "setting_notifications_notify_seconds": "{} sekunder", + "setting_notifications_notify_seconds": "{count} sekunder", "setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon per objekt", "setting_notifications_single_progress_title": "Vis detaljert status pÃĨ sikkerhetskopiering i bakgrunnen", "setting_notifications_subtitle": "Juster notifikasjonsinnstillinger", "setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt objekter)", "setting_notifications_total_progress_title": "Vis status pÃĨ sikkerhetskopiering i bakgrunnen", - "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "NÃĨr det streames en video fra serveren, spill originalkvaliteten selv om en omkodet versjon finnes. Dette kan medføre buffring. Videoer som er lagret lokalt pÃĨ enheten spilles i originalkvalitet uavhengig av denne innstillingen.", "setting_video_viewer_original_video_title": "Tving original video", "settings": "Innstillinger", "settings_require_restart": "Vennligst restart Immich for ÃĨ aktivere denne innstillingen", "settings_saved": "Innstillinger lagret", + "setup_pin_code": "Sett opp en PINkode", "share": "Del", "share_add_photos": "Legg til bilder", - "share_assets_selected": "{} valgt", + "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", + "share_link": "Del link", "shared": "Delt", "shared_album_activities_input_disable": "Kommenterer er deaktivert", "shared_album_activity_remove_content": "Vil du slette denne aktiviteten?", @@ -1623,34 +1664,33 @@ "shared_by_user": "Delt av {user}", "shared_by_you": "Delt av deg", "shared_from_partner": "Bilder fra {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Lastet opp", + "shared_intent_upload_button_progress_text": "{current} / {total} Lastet opp", "shared_link_app_bar_title": "Delte linker", "shared_link_clipboard_copied_massage": "Kopiert til utklippslisten", - "shared_link_clipboard_text": "Link: {}\nPassord: {}", + "shared_link_clipboard_text": "Link: {link}\nPassord: {password}", "shared_link_create_error": "Feil ved oppretting av delbar link", "shared_link_edit_description_hint": "Endre delebeskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", - "shared_link_edit_expire_after_option_days": "{} dager", + "shared_link_edit_expire_after_option_days": "{count} dager", "shared_link_edit_expire_after_option_hour": "1 time", - "shared_link_edit_expire_after_option_hours": "{} timer", + "shared_link_edit_expire_after_option_hours": "{count} timer", "shared_link_edit_expire_after_option_minute": "1 minutt", - "shared_link_edit_expire_after_option_minutes": "{} minutter", - "shared_link_edit_expire_after_option_months": "{} mÃĨneder", - "shared_link_edit_expire_after_option_year": "{} ÃĨr", + "shared_link_edit_expire_after_option_minutes": "{count} minutter", + "shared_link_edit_expire_after_option_months": "{count} mÃĨneder", + "shared_link_edit_expire_after_option_year": "{count} ÃĨr", "shared_link_edit_password_hint": "Skriv inn dele-passord", "shared_link_edit_submit_button": "Oppdater link", "shared_link_error_server_url_fetch": "Kan ikke hente server-url", - "shared_link_expires_day": "UtgÃĨr om {} dag", - "shared_link_expires_days": "UtgÃĨr om {} dager", - "shared_link_expires_hour": "UtgÃĨr om {} time", - "shared_link_expires_hours": "UtgÃĨr om {} timer", - "shared_link_expires_minute": "UtgÃĨr om {} minutt", - "shared_link_expires_minutes": "UtgÃĨr om {} minutter", + "shared_link_expires_day": "UtgÃĨr om {count} dag", + "shared_link_expires_days": "UtgÃĨr om {count} dager", + "shared_link_expires_hour": "UtgÃĨr om {count} time", + "shared_link_expires_hours": "UtgÃĨr om {count} timer", + "shared_link_expires_minute": "UtgÃĨr om {count} minutt", + "shared_link_expires_minutes": "UtgÃĨr om {count} minutter", "shared_link_expires_never": "UtgÃĨr ∞", - "shared_link_expires_second": "UtgÃĨr om {} sekund", - "shared_link_expires_seconds": "UtgÃĨr om {} sekunder", + "shared_link_expires_second": "UtgÃĨr om {count} sekund", + "shared_link_expires_seconds": "UtgÃĨr om {count} sekunder", "shared_link_individual_shared": "Individuelt delt", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "HÃĨndter delte linker", "shared_link_options": "Alternativer for delte lenke", "shared_links": "Delte linker", @@ -1713,16 +1753,15 @@ "stack_selected_photos": "Stable valgte bilder", "stacked_assets_count": "Stable {count, plural, one {# asset} other {# assets}}", "stacktrace": "Stakkspor", - "start": "Start", "start_date": "Startdato", "state": "Fylke", - "status": "Status", "stop_motion_photo": "Stopmotionbilde", "stop_photo_sharing": "Stopp deling av bildene dine?", "stop_photo_sharing_description": "{partner} vil ikke lenger ha tilgang til bildene dine.", "stop_sharing_photos_with_user": "Slutt ÃĨ dele bildene dine med denne brukeren", "storage": "Lagring", "storage_label": "Lagringsetikett", + "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", "suggestions": "Forslag", @@ -1749,8 +1788,8 @@ "theme_selection": "Temavalg", "theme_selection_description": "Automatisk sett tema til lys eller mørk basert pÃĨ nettleserens systeminnstilling", "theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor pÃĨ objekter i fotorutenettet", - "theme_setting_asset_list_tiles_per_row_title": "Antall objekter per rad ({})", - "theme_setting_colorful_interface_subtitle": "Angi primÃĻrfarge til bakgrunner", + "theme_setting_asset_list_tiles_per_row_title": "Antall objekter per rad ({count})", + "theme_setting_colorful_interface_subtitle": "Angi primÃĻrfarge til bakgrunner.", "theme_setting_colorful_interface_title": "Fargefullt grensesnitt", "theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten pÃĨ bilder i detaljvisning", "theme_setting_image_viewer_quality_title": "Kvalitet pÃĨ bildevisning", @@ -1774,7 +1813,6 @@ "to_trash": "Papirkurv", "toggle_settings": "Bytt innstillinger", "toggle_theme": "Bytt tema", - "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", "trash_all": "Slett alt", @@ -1784,13 +1822,14 @@ "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", "trash_page_empty_trash_dialog_content": "Vil du tømme søppelbøtten? Objektene vil bli permanent fjernet fra Immich", - "trash_page_info": "Objekter i søppelbøtten blir permanent fjernet etter {} dager", + "trash_page_info": "Objekter i søppelbøtten blir permanent fjernet etter {days} dager", "trash_page_no_assets": "Ingen forkastede objekter", "trash_page_restore_all": "Gjenopprett alt", "trash_page_select_assets_btn": "Velg objekter", - "trash_page_title": "Søppelbøtte ({})", + "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", - "type": "Type", + "unable_to_change_pin_code": "Klarte ikke ÃĨ endre PINkode", + "unable_to_setup_pin_code": "Klarte ikke ÃĨ sette opp PINkode", "unarchive": "Fjern fra arkiv", "unarchived_count": "{count, plural, other {uarkivert #}}", "unfavorite": "Fjern favoritt", @@ -1814,6 +1853,7 @@ "untracked_files": "Usporede Filer", "untracked_files_decription": "Disse filene er ikke sporet av applikasjonen. De kan vÃĻre resultatet av mislykkede flyttinger, avbrutte opplastinger eller etterlatt pÃĨ grunn av en feil", "up_next": "Neste", + "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", "upload_concurrency": "Samtidig opplastning", @@ -1826,15 +1866,18 @@ "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", "upload_success": "Opplasting vellykket, oppdater siden for ÃĨ se nye opplastninger.", - "upload_to_immich": "Last opp til Immich ({})", + "upload_to_immich": "Last opp til Immich ({count})", "uploading": "Laster opp", - "url": "URL", "usage": "Bruk", + "use_biometric": "Bruk biometri", "use_current_connection": "bruk nÃĨvÃĻrende tilkobling", "use_custom_date_range": "Bruk egendefinert datoperiode i stedet", "user": "Bruker", + "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", "user_liked": "{user} likte {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_pin_code_settings": "PINkode", + "user_pin_code_settings_description": "HÃĨndter din PINkode", "user_purchase_settings": "Kjøpe", "user_purchase_settings_description": "Administrer dine kjøp", "user_role_set": "Sett {user} som {role}", @@ -1857,7 +1900,6 @@ "version_announcement_overlay_title": "Ny serverversjon tilgjengelig", "version_history": "Verson Historie", "version_history_item": "Installert {version} den {date}", - "video": "Video", "video_hover_setting": "Spill av forhÃĨndsvisining mens du holder over musepekeren", "video_hover_setting_description": "Spill av forhÃĨndsvisning mens en musepeker er over elementet. Selv nÃĨr den er deaktivert, kan avspilling startes ved ÃĨ holde musepekeren over avspillingsikonet.", "videos": "Videoer", @@ -1883,7 +1925,8 @@ "week": "Uke", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "Wi-Fi navn", + "wifi_name": "Wi-Fi Navn", + "wrong_pin_code": "Feil PINkode", "year": "År", "years_ago": "{years, plural, one {# ÃĨr} other {# ÃĨr}} siden", "yes": "Ja", diff --git a/i18n/nl.json b/i18n/nl.json index 609d2add5c..80aa9ea76b 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -1,7 +1,6 @@ { "about": "Over", - "account": "Account", - "account_settings": "Accountinstellingen", + "account_settings": "Account Instellingen", "acknowledge": "Begrepen", "action": "Actie", "action_common_update": "Bijwerken", @@ -11,7 +10,7 @@ "activity_changed": "Activiteit is {enabled, select, true {ingeschakeld} other {uitgeschakeld}}", "add": "Toevoegen", "add_a_description": "Beschrijving toevoegen", - "add_a_location": "Locatie toevoegen", + "add_a_location": "Een locatie toevoegen", "add_a_name": "Naam toevoegen", "add_a_title": "Titel toevoegen", "add_endpoint": "Server toevoegen", @@ -26,6 +25,7 @@ "add_to_album": "Aan album toevoegen", "add_to_album_bottom_sheet_added": "Toegevoegd aan {album}", "add_to_album_bottom_sheet_already_exists": "Staat al in {album}", + "add_to_locked_folder": "Toevoegen aan vergrendelde map", "add_to_shared_album": "Aan gedeeld album toevoegen", "add_url": "URL toevoegen", "added_to_archive": "Toegevoegd aan archief", @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "Weet je zeker dat je alle inlogmethoden wilt uitschakelen? Inloggen zal volledig worden uitgeschakeld.", "authentication_settings_reenable": "Gebruik een servercommando om opnieuw in te schakelen.", "background_task_job": "Achtergrondtaken", - "backup_database": "Backup Database", - "backup_database_enable_description": "Database back-ups activeren", - "backup_keep_last_amount": "Maximaal aantal back-ups om te bewaren", - "backup_settings": "Back-up instellingen", - "backup_settings_description": "Database back-up instellingen beheren", + "backup_database": "Maak database backup", + "backup_database_enable_description": "Database dumps activeren", + "backup_keep_last_amount": "Aantal back-ups om te bewaren", + "backup_settings": "Database dump instellingen", + "backup_settings_description": "Beheer database back-up instellingen. Noot: Deze taken worden niet bijgehouden en je wordt niet op de hoogte gesteld van een fout.", "check_all": "Controleer het logboek", "cleanup": "Opruimen", "cleared_jobs": "Taken gewist voor: {job}", @@ -53,6 +53,7 @@ "confirm_email_below": "Typ hieronder \"{email}\" ter bevestiging", "confirm_reprocess_all_faces": "Weet je zeker dat je alle gezichten opnieuw wilt verwerken? Hiermee worden ook alle mensen gewist.", "confirm_user_password_reset": "Weet u zeker dat je het wachtwoord van {user} wilt resetten?", + "confirm_user_pin_code_reset": "Weet je zeker dat je de PIN code van {user} wilt resetten?", "create_job": "Taak maken", "cron_expression": "Cron expressie", "cron_expression_description": "Stel de scaninterval in met het cron-formaat. Voor meer informatie kun je kijken naar bijvoorbeeld Crontab Guru", @@ -192,26 +193,22 @@ "oauth_auto_register": "Automatisch registreren", "oauth_auto_register_description": "Nieuwe gebruikers automatisch registreren na inloggen met OAuth", "oauth_button_text": "Button tekst", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Client secret", + "oauth_client_secret_description": "Vereist als PKCE (Proof Key for Code Exchange) niet wordt ondersteund door de OAuth aanbieder", "oauth_enable_description": "Inloggen met OAuth", - "oauth_issuer_url": "Uitgever URL", "oauth_mobile_redirect_uri": "Omleidings URI voor mobiel", "oauth_mobile_redirect_uri_override": "Omleidings URI voor mobiele app overschrijven", "oauth_mobile_redirect_uri_override_description": "Inschakelen wanneer de OAuth-provider geen mobiele URI toestaat, zoals '{callback}'", - "oauth_profile_signing_algorithm": "Algoritme voor profielondertekening", - "oauth_profile_signing_algorithm_description": "Algoritme voor het ondertekenen van het gebruikersprofiel.", - "oauth_scope": "Scope", "oauth_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", - "oauth_signing_algorithm": "Signing algoritme", "oauth_storage_label_claim": "Claim voor opslaglabel", "oauth_storage_label_claim_description": "Stel het opslaglabel van de gebruiker automatisch in op de waarde van deze claim.", "oauth_storage_quota_claim": "Claim voor opslaglimiet", "oauth_storage_quota_claim_description": "Stel de opslaglimiet van de gebruiker automatisch in op de waarde van deze claim.", "oauth_storage_quota_default": "Standaard opslaglimiet (GiB)", "oauth_storage_quota_default_description": "Limiet in GiB die moet worden gebruikt als er geen claim is opgegeven (voer 0 in voor onbeperkt).", + "oauth_timeout": "Aanvraag timeout", + "oauth_timeout_description": "Time-out voor aanvragen in milliseconden", "offline_paths": "Offline paden", "offline_paths_description": "Deze resultaten kunnen het gevolg zijn van het handmatig verwijderen van bestanden die geen deel uitmaken van een externe bibliotheek.", "password_enable_description": "Inloggen met e-mailadres en wachtwoord", @@ -270,7 +267,7 @@ "template_email_update_album": "Update in album sjabloon", "template_email_welcome": "Welkom email sjabloon", "template_settings": "Melding sjablonen", - "template_settings_description": "Beheer aangepast sjablonen voor meldingen.", + "template_settings_description": "Beheer aangepast sjablonen voor meldingen", "theme_custom_css_settings": "Aangepaste CSS", "theme_custom_css_settings_description": "Met Cascading Style Sheets kan het ontwerp van Immich worden aangepast.", "theme_settings": "Thema instellingen", @@ -352,6 +349,7 @@ "user_delete_delay_settings_description": "Aantal dagen na verwijdering om het account en de assets van een gebruiker permanent te verwijderen. De taak voor het verwijderen van gebruikers wordt om middernacht uitgevoerd om te controleren of gebruikers verwijderd kunnen worden. Wijzigingen in deze instelling worden bij de volgende uitvoering meegenomen.", "user_delete_immediately": "Het account en de assets van {user} worden onmiddellijk in de wachtrij geplaatst voor permanente verwijdering.", "user_delete_immediately_checkbox": "Gebruikers en assets in de wachtrij plaatsen voor onmiddellijke verwijdering", + "user_details": "Gebruiker details", "user_management": "Gebruikersbeheer", "user_password_has_been_reset": "Het wachtwoord van de gebruiker is gereset:", "user_password_reset_description": "Geef het tijdelijke wachtwoord aan de gebruiker en informeer de gebruiker dat bij de volgende keer inloggen een wachtwoordwijziging vereist is.", @@ -371,15 +369,19 @@ "admin_password": "Beheerder wachtwoord", "administration": "Beheer", "advanced": "Geavanceerd", - "advanced_settings_log_level_title": "Log niveau: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gebruik deze optie om media te filteren tijdens de synchronisatie op basis van alternatieve criteria. Gebruik dit enkel als de app problemen heeft met het detecteren van albums.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTEEL] Gebruik een alternatieve album synchronisatie filter", + "advanced_settings_log_level_title": "Log niveau: {level}", "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van afbeeldingen die lokaal zijn opgeslagen op het apparaat. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", "advanced_settings_proxy_headers_subtitle": "Definieer proxy headers die Immich bij elk netwerkverzoek moet verzenden", "advanced_settings_proxy_headers_title": "Proxy headers", - "advanced_settings_self_signed_ssl_subtitle": "Slaat SSL-certificaatverificatie voor de connectie met de server over. Deze optie is vereist voor zelfondertekende certificaten", + "advanced_settings_self_signed_ssl_subtitle": "Slaat SSL-certificaatverificatie voor de connectie met de server over. Deze optie is vereist voor zelfondertekende certificaten.", "advanced_settings_self_signed_ssl_title": "Zelfondertekende SSL-certificaten toestaan", + "advanced_settings_sync_remote_deletions_subtitle": "Automatisch bestanden verwijderen of herstellen op dit apparaat als die actie op het web is ondernomen", + "advanced_settings_sync_remote_deletions_title": "Synchroniseer verwijderingen op afstand [EXPERIMENTEEL]", "advanced_settings_tile_subtitle": "Geavanceerde gebruikersinstellingen", - "advanced_settings_troubleshooting_subtitle": "Schakel extra functies voor probleemoplossing in ", + "advanced_settings_troubleshooting_subtitle": "Schakel extra functies voor probleemoplossing in", "advanced_settings_troubleshooting_title": "Probleemoplossing", "age_months": "Leeftijd {months, plural, one {# maand} other {# maanden}}", "age_year_months": "Leeftijd 1 jaar, {months, plural, one {# maand} other {# maanden}}", @@ -399,10 +401,8 @@ "album_remove_user": "Gebruiker verwijderen?", "album_remove_user_confirmation": "Weet je zeker dat je {user} wilt verwijderen?", "album_share_no_users": "Het lijkt erop dat je dit album met alle gebruikers hebt gedeeld, of dat je geen gebruikers hebt om mee te delen.", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", "album_thumbnail_card_shared": " ¡ Gedeeld", - "album_thumbnail_shared_by": "Gedeeld door {}", + "album_thumbnail_shared_by": "Gedeeld door {user}", "album_updated": "Album bijgewerkt", "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe assets heeft", "album_user_left": "{album} verlaten", @@ -416,7 +416,6 @@ "album_viewer_appbar_share_to": "Delen via", "album_viewer_page_share_add_users": "Gebruikers toevoegen", "album_with_link_access": "Iedereen met de link kan de foto's en mensen in dit album bekijken.", - "albums": "Albums", "albums_count": "{count, plural, one {{count, number} album} other {{count, number} albums}}", "all": "Alle", "all_albums": "Alle albums", @@ -440,7 +439,7 @@ "archive": "Archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", - "archive_page_title": "Archief ({})", + "archive_page_title": "Archief ({count})", "archive_size": "Archiefgrootte", "archive_size_description": "Configureer de archiefgrootte voor downloads (in GiB)", "archived": "Gearchiveerd", @@ -452,7 +451,6 @@ "asset_added_to_album": "Toegevoegd aan album", "asset_adding_to_album": "Toevoegen aan albumâ€Ļ", "asset_description_updated": "Asset beschrijving is bijgewerkt", - "asset_filename_is_offline": "Asset {filename} is offline", "asset_has_unassigned_faces": "Asset heeft niet-toegewezen gezichten", "asset_hashing": "Hashenâ€Ļ", "asset_list_group_by_sub_title": "Groepeer op", @@ -460,7 +458,6 @@ "asset_list_layout_settings_group_automatically": "Automatisch", "asset_list_layout_settings_group_by": "Groepeer assets per", "asset_list_layout_settings_group_by_month_day": "Maand + dag", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Fotorasterlayoutinstellingen", "asset_list_settings_title": "Fotoraster", "asset_offline": "Asset offline", @@ -472,32 +469,30 @@ "asset_uploading": "Uploadenâ€Ļ", "asset_viewer_settings_subtitle": "Beheer je instellingen voor gallerijweergave", "asset_viewer_settings_title": "Foto weergave", - "assets": "Assets", "assets_added_count": "{count, plural, one {# asset} other {# assets}} toegevoegd", "assets_added_to_album_count": "{count, plural, one {# asset} other {# assets}} aan het album toegevoegd", "assets_added_to_name_count": "{count, plural, one {# asset} other {# assets}} toegevoegd aan {hasName, select, true {{name}} other {nieuw album}}", - "assets_count": "{count, plural, one {# asset} other {# assets}}", - "assets_deleted_permanently": "{} asset(s) permanent verwijderd", - "assets_deleted_permanently_from_server": "{} asset(s) permanent verwijderd van de Immich server", + "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", + "assets_deleted_permanently_from_server": "{count} asset(s) permanent verwijderd van de Immich server", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} verplaatst naar prullenbak", "assets_permanently_deleted_count": "{count, plural, one {# asset} other {# assets}} permanent verwijderd", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} verwijderd", - "assets_removed_permanently_from_device": "{} asset(s) permanent verwijderd van je apparaat", + "assets_removed_permanently_from_device": "{count} asset(s) permanent verwijderd van je apparaat", "assets_restore_confirmation": "Weet je zeker dat je alle verwijderde assets wilt herstellen? Je kunt deze actie niet ongedaan maken! Offline assets kunnen op deze manier niet worden hersteld.", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} hersteld", - "assets_restored_successfully": "{} asset(s) succesvol hersteld", - "assets_trashed": "{} asset(s) naar de prullenbak verplaatst", + "assets_restored_successfully": "{count} asset(s) succesvol hersteld", + "assets_trashed": "{count} asset(s) naar de prullenbak verplaatst", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} naar prullenbak verplaatst", - "assets_trashed_from_server": "{} asset(s) naar de prullenbak verplaatst op de Immich server", + "assets_trashed_from_server": "{count} asset(s) naar de prullenbak verplaatst op de Immich server", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets waren}} al onderdeel van het album", "authorized_devices": "Geautoriseerde apparaten", - "automatic_endpoint_switching_subtitle": "Verbind lokaal bij het opgegeven wifi-netwerk en gebruik anders de externe url", + "automatic_endpoint_switching_subtitle": "Maak een lokale verbinding bij het opgegeven WiFi-netwerk en gebruik in andere gevallen de externe URL", "automatic_endpoint_switching_title": "Automatische serverwissel", "back": "Terug", "back_close_deselect": "Terug, sluiten of deselecteren", "background_location_permission": "Achtergrond locatie toestemming", - "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het wifi-netwerk te kunnen lezen", - "backup_album_selection_page_albums_device": "Albums op apparaat ({})", + "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het WiFi-netwerk te kunnen lezen", + "backup_album_selection_page_albums_device": "Albums op apparaat ({count})", "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.", "backup_album_selection_page_select_albums": "Albums selecteren", @@ -506,22 +501,21 @@ "backup_all": "Alle", "backup_background_service_backup_failed_message": "Fout bij back-uppen assets. Opnieuw proberenâ€Ļ", "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberenâ€Ļ", - "backup_background_service_current_upload_notification": "Uploaden {}", + "backup_background_service_current_upload_notification": "{filename} aan het uploaden...", "backup_background_service_default_notification": "Controleren op nieuwe assetsâ€Ļ", "backup_background_service_error_title": "Backupfout", "backup_background_service_in_progress_notification": "Back-up van assets makenâ€Ļ", - "backup_background_service_upload_failure_notification": "Fout bij upload {}", + "backup_background_service_upload_failure_notification": "Fout bij het uploaden van {filename}", "backup_controller_page_albums": "Back-up albums", "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via Instellingen > Algemeen > Ververs op achtergrond, om back-ups op de achtergrond te maken.", "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld", "backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen", "backup_controller_page_background_battery_info_link": "Laat zien hoe", "backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Batterijoptimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", "backup_controller_page_background_configure_error": "Achtergrondserviceconfiguratie mislukt", - "backup_controller_page_background_delay": "Back-upvertraging nieuwe assets: {}", + "backup_controller_page_background_delay": "Back-upvertraging voor nieuwe assets: {duration}", "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe assets zonder de app te hoeven openen", "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", @@ -531,12 +525,11 @@ "backup_controller_page_backup": "Back-up", "backup_controller_page_backup_selected": "Geselecteerd: ", "backup_controller_page_backup_sub": "Geback-upte foto's en video's", - "backup_controller_page_created": "Gemaakt op: {}", + "backup_controller_page_created": "Gemaakt op: {date}", "backup_controller_page_desc_backup": "Schakel back-up op de voorgrond in om automatisch nieuwe assets naar de server te uploaden bij het openen van de app.", "backup_controller_page_excluded": "Uitgezonderd: ", - "backup_controller_page_failed": "Mislukt ({})", - "backup_controller_page_filename": "Bestandsnaam: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Mislukt ({count})", + "backup_controller_page_filename": "Bestandsnaam: {filename} [{size}]", "backup_controller_page_info": "Back-up informatie", "backup_controller_page_none_selected": "Geen geselecteerd", "backup_controller_page_remainder": "Resterend", @@ -545,14 +538,14 @@ "backup_controller_page_start_backup": "Back-up uitvoeren", "backup_controller_page_status_off": "Automatische back-up op de voorgrond staat uit", "backup_controller_page_status_on": "Automatische back-up op de voorgrond staat aan", - "backup_controller_page_storage_format": "{} van {} gebruikt", + "backup_controller_page_storage_format": "{used} van {total} gebruikt", "backup_controller_page_to_backup": "Albums om een back-up van te maken", "backup_controller_page_total_sub": "Alle unieke foto's en video's uit geselecteerde albums", "backup_controller_page_turn_off": "Back-up op de voorgrond uitzetten", "backup_controller_page_turn_on": "Back-up op de voorgrond aanzetten", "backup_controller_page_uploading_file_info": "Bestandsgegevens uploaden", "backup_err_only_album": "Kan het enige album niet verwijderen", - "backup_info_card_assets": "assets", + "backup_info_card_assets": "bestanden", "backup_manual_cancelled": "Geannuleerd", "backup_manual_in_progress": "Het uploaden is al bezig. Probeer het na een tijdje", "backup_manual_success": "Succes", @@ -560,35 +553,36 @@ "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", "backward": "Achteruit", + "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", + "biometric_locked_out": "Biometrische authenticatie is vergrendeld", + "biometric_no_options": "Geen biometrische opties beschikbaar", + "biometric_not_available": "Biometrische authenticatie is niet beschikbaar op dit apparaat", "birthdate_saved": "Geboortedatum succesvol opgeslagen", "birthdate_set_description": "De geboortedatum wordt gebruikt om de leeftijd van deze persoon op het moment van de foto te berekenen.", "blurred_background": "Vervaagde achtergrond", "bugs_and_feature_requests": "Bugs & functieverzoeken", - "build": "Build", "build_image": "Build image", "bulk_delete_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} in bulk wilt verwijderen? Dit zal de grootste asset van elke groep behouden en alle andere duplicaten permanent verwijderen. Je kunt deze actie niet ongedaan maken!", "bulk_keep_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} wilt behouden? Dit zal alle groepen met duplicaten oplossen zonder iets te verwijderen.", "bulk_trash_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} in bulk naar de prullenbak wilt verplaatsen? Dit zal de grootste asset van elke groep behouden en alle andere duplicaten naar de prullenbak verplaatsen.", "buy": "Immich kopen", - "cache_settings_album_thumbnails": "Thumbnails bibliotheekpagina ({} assets)", + "cache_settings_album_thumbnails": "Thumbnails bibliotheekpagina ({count} assets)", "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", - "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({})", - "cache_settings_image_cache_size": "Grootte afbeeldingscache ({} assets)", + "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", + "cache_settings_image_cache_size": "Grootte afbeeldingscache ({count} assets)", "cache_settings_statistics_album": "Bibliotheekthumbnails", - "cache_settings_statistics_assets": "{} assets ({})", + "cache_settings_statistics_assets": "{count} bestanden ({size})", "cache_settings_statistics_full": "Volledige afbeeldingen", "cache_settings_statistics_shared": "Gedeeld-albumthumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", "cache_settings_statistics_title": "Cachegebruik", "cache_settings_subtitle": "Beheer het cachegedrag van de Immich app", - "cache_settings_thumbnail_size": "Thumbnail-cachegrootte ({} assets)", + "cache_settings_thumbnail_size": "Thumbnail-cachegrootte ({count} assets)", "cache_settings_tile_subtitle": "Beheer het gedrag van lokale opslag", "cache_settings_tile_title": "Lokale opslag", "cache_settings_title": "Cache-instellingen", - "camera": "Camera", "camera_brand": "Cameramerk", "camera_model": "Cameramodel", "cancel": "Annuleren", @@ -597,7 +591,9 @@ "cannot_merge_people": "Kan mensen niet samenvoegen", "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", + "cast": "Cast", "change_date": "Wijzig datum", + "change_description": "Wijzig beschrijving", "change_display_order": "Weergavevolgorde wijzigen", "change_expiration_time": "Verlooptijd wijzigen", "change_location": "Locatie wijzigen", @@ -610,12 +606,13 @@ "change_password_form_new_password": "Nieuw wachtwoord", "change_password_form_password_mismatch": "Wachtwoorden komen niet overeen", "change_password_form_reenter_new_password": "Vul het wachtwoord opnieuw in", + "change_pin_code": "Wijzig PIN code", "change_your_password": "Wijzig je wachtwoord", "changed_visibility_successfully": "Zichtbaarheid succesvol gewijzigd", "check_all": "Controleer alle", "check_corrupt_asset_backup": "Controleer op corrupte back-ups van assets", "check_corrupt_asset_backup_button": "Controle uitvoeren", - "check_corrupt_asset_backup_description": "Voer deze controle alleen uit via wifi en nadat alle assets zijn geback-upt. De procedure kan een paar minuten duren.", + "check_corrupt_asset_backup_description": "Voer deze controle alleen uit via WiFi en nadat alle assets zijn geback-upt. De procedure kan een paar minuten duren.", "check_logs": "Controleer logboek", "choose_matching_people_to_merge": "Kies overeenkomende mensen om samen te voegen", "city": "Stad", @@ -650,11 +647,12 @@ "confirm_delete_face": "Weet je zeker dat je {name} gezicht wilt verwijderen uit de asset?", "confirm_delete_shared_link": "Weet je zeker dat je deze gedeelde link wilt verwijderen?", "confirm_keep_this_delete_others": "Alle andere assets in de stack worden verwijderd, behalve deze. Weet je zeker dat je wilt doorgaan?", + "confirm_new_pin_code": "Bevestig nieuwe PIN code", "confirm_password": "Bevestig wachtwoord", + "connected_to": "Verbonden met", "contain": "Bevat", - "context": "Context", "continue": "Doorgaan", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Gedeeld", + "control_bottom_app_bar_album_info_shared": "{count} items ¡ Gedeeld", "control_bottom_app_bar_create_new_album": "Nieuw album maken", "control_bottom_app_bar_delete_from_immich": "Verwijderen van Immich", "control_bottom_app_bar_delete_from_local": "Verwijderen van apparaat", @@ -674,7 +672,6 @@ "copy_to_clipboard": "KopiÃĢren naar klembord", "country": "Land", "cover": "Bedekken", - "covers": "Covers", "create": "Aanmaken", "create_album": "Album aanmaken", "create_album_page_untitled": "Naamloos", @@ -692,9 +689,11 @@ "create_tag_description": "Maak een nieuwe tag. Voor geneste tags, voer het volledige pad van de tag in, inclusief schuine strepen.", "create_user": "Gebruiker aanmaken", "created": "Aangemaakt", + "created_at": "Aangemaakt", "crop": "Bijsnijden", "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", + "current_pin_code": "Huidige PIN code", "current_server_address": "Huidige serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", @@ -742,11 +741,9 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", - "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", "disallow_edits": "Geen bewerkingen toestaan", - "discord": "Discord", "discover": "Zoeken", "dismiss_all_errors": "Negeer alle fouten", "dismiss_error": "Negeer fout", @@ -763,7 +760,7 @@ "download_enqueue": "Download in wachtrij", "download_error": "Fout bij downloaden", "download_failed": "Download mislukt", - "download_filename": "bestand: {}", + "download_filename": "bestand: {filename}", "download_finished": "Download voltooid", "download_include_embedded_motion_videos": "Ingesloten video's", "download_include_embedded_motion_videos_description": "Voeg video's toe die ingesloten zijn in bewegende foto's als een apart bestand", @@ -787,6 +784,8 @@ "edit_avatar": "Avatar bewerken", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", + "edit_description": "Bewerk beschrijving", + "edit_description_prompt": "Selecteer een nieuwe beschrijving:", "edit_exclusion_pattern": "Uitsluitingspatroon bewerken", "edit_faces": "Gezichten bewerken", "edit_import_path": "Import-pad bewerken", @@ -807,19 +806,23 @@ "editor_crop_tool_h2_aspect_ratios": "Beeldverhoudingen", "editor_crop_tool_h2_rotation": "Rotatie", "email": "E-mailadres", + "email_notifications": "E-mailmeldingen", "empty_folder": "Deze map is leeg", "empty_trash": "Prullenbak leegmaken", "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle assets in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", "enable": "Inschakelen", + "enable_biometric_auth_description": "Voer uw pincode in om biometrische authenticatie in te schakelen", "enabled": "Ingeschakeld", "end_date": "Einddatum", "enqueued": "In de wachtrij", - "enter_wifi_name": "Voer de WiFi naam in", + "enter_wifi_name": "Voer de WiFi-naam in", + "enter_your_pin_code": "Voer uw pincode in", + "enter_your_pin_code_subtitle": "Voer uw pincode in om toegang te krijgen tot de vergrendelde map", "error": "Fout", "error_change_sort_album": "Sorteervolgorde van album wijzigen mislukt", "error_delete_face": "Fout bij verwijderen gezicht uit asset", "error_loading_image": "Fout bij laden afbeelding", - "error_saving_image": "Fout: {}", + "error_saving_image": "Fout: {error}", "error_title": "Fout - Er is iets misgegaan", "errors": { "cannot_navigate_next_asset": "Kan niet naar de volgende asset navigeren", @@ -849,10 +852,12 @@ "failed_to_keep_this_delete_others": "Het is niet gelukt om dit asset te behouden en de andere assets te verwijderen", "failed_to_load_asset": "Kan asset niet laden", "failed_to_load_assets": "Kan assets niet laden", + "failed_to_load_notifications": "Kon meldingen niet laden", "failed_to_load_people": "Kan mensen niet laden", "failed_to_remove_product_key": "Er is een fout opgetreden bij het verwijderen van de licentiesleutel", "failed_to_stack_assets": "Fout bij stapelen van assets", "failed_to_unstack_assets": "Fout bij ontstapelen van assets", + "failed_to_update_notification_status": "Kon notificatie status niet updaten", "import_path_already_exists": "Dit import-pad bestaat al.", "incorrect_email_or_password": "Onjuist e-mailadres of wachtwoord", "paths_validation_failed": "validatie van {paths, plural, one {# pad} other {# paden}} mislukt", @@ -870,6 +875,7 @@ "unable_to_archive_unarchive": "Kan niet {archived, select, true {toevoegen aan} other {verwijderen uit}} archief", "unable_to_change_album_user_role": "Kan rol van de albumgebruiker niet wijzigen", "unable_to_change_date": "Kan datum niet wijzigen", + "unable_to_change_description": "Beschrijving kan niet worden gewijzigd", "unable_to_change_favorite": "Kan asset niet toevoegen aan of verwijderen uit favorieten", "unable_to_change_location": "Kan locatie niet wijzigen", "unable_to_change_password": "Kan wachtwoord niet veranderen", @@ -907,6 +913,7 @@ "unable_to_log_out_all_devices": "Kan niet op alle apparaten uitloggen", "unable_to_log_out_device": "Kan apparaat niet uitloggen", "unable_to_login_with_oauth": "Kan niet inloggen met OAuth", + "unable_to_move_to_locked_folder": "Verplaatsen naar de vergrendelde map is niet gelukt", "unable_to_play_video": "Kan video niet afspelen", "unable_to_reassign_assets_existing_person": "Kan assets niet opnieuw toewijzen aan {name, select, null {een bestaand persoon} other {{name}}}", "unable_to_reassign_assets_new_person": "Kan assets niet opnieuw toewijzen aan een nieuw persoon", @@ -920,6 +927,7 @@ "unable_to_remove_reaction": "Kan reactie niet verwijderen", "unable_to_repair_items": "Kan items niet repareren", "unable_to_reset_password": "Kan wachtwoord niet resetten", + "unable_to_reset_pin_code": "Kan PIN code niet resetten", "unable_to_resolve_duplicate": "Kan duplicaat niet oplossen", "unable_to_restore_assets": "Kan assets niet herstellen", "unable_to_restore_trash": "Kan niet herstellen uit prullenbak", @@ -947,16 +955,14 @@ "unable_to_update_user": "Kan gebruiker niet bijwerken", "unable_to_upload_file": "Kan bestand niet uploaden" }, - "exif": "Exif", "exif_bottom_sheet_description": "Beschrijving toevoegen...", - "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", "exif_bottom_sheet_people": "MENSEN", "exif_bottom_sheet_person_add_person": "Naam toevoegen", - "exif_bottom_sheet_person_age": "Leeftijd {}", - "exif_bottom_sheet_person_age_months": "Leeftijd {} maanden", - "exif_bottom_sheet_person_age_year_months": "Leeftijd 1 jaar, {} maanden", - "exif_bottom_sheet_person_age_years": "Leeftijd {}", + "exif_bottom_sheet_person_age": "Leeftijd {age}", + "exif_bottom_sheet_person_age_months": "Leeftijd {months} maanden", + "exif_bottom_sheet_person_age_year_months": "Leeftijd 1 jaar, {months} maanden", + "exif_bottom_sheet_person_age_years": "Leeftijd {years}", "exit_slideshow": "Diavoorstelling sluiten", "expand_all": "Alles uitvouwen", "experimental_settings_new_asset_list_subtitle": "Werk in uitvoering", @@ -974,9 +980,10 @@ "external": "Extern", "external_libraries": "Externe bibliotheken", "external_network": "Extern netwerk", - "external_network_sheet_info": "Als je niet verbonden bent met het opgegeven wifi-netwerk, maakt de app verbinding met de server via de eerst bereikbare URL in de onderstaande lijst, van boven naar beneden", + "external_network_sheet_info": "Als je niet verbonden bent met het opgegeven WiFi-netwerk, maakt de app verbinding met de server via de eerst bereikbare URL in de onderstaande lijst, van boven naar beneden", "face_unassigned": "Niet toegewezen", "failed": "Mislukt", + "failed_to_authenticate": "Authenticatie mislukt", "failed_to_load_assets": "Kan assets niet laden", "failed_to_load_folder": "Laden van map mislukt", "favorite": "Favoriet", @@ -990,8 +997,8 @@ "file_name_or_extension": "Bestandsnaam of extensie", "filename": "Bestandsnaam", "filetype": "Bestandstype", - "filter": "Filter", "filter_people": "Filter op mensen", + "filter_places": "Filter locaties", "find_them_fast": "Vind ze snel op naam door te zoeken", "fix_incorrect_match": "Onjuiste overeenkomst corrigeren", "folder": "Map", @@ -1001,7 +1008,7 @@ "forward": "Vooruit", "general": "Algemeen", "get_help": "Krijg hulp", - "get_wifiname_error": "Kon de Wi-Fi naam niet ophalen. Zorg ervoor dat je de benodigde machtigingen hebt verleend en verbonden bent met een Wi-Fi-netwerk", + "get_wifiname_error": "Kon de WiFi-naam niet ophalen. Zorg ervoor dat je de benodigde machtigingen hebt verleend en verbonden bent met een WiFi-netwerk", "getting_started": "Aan de slag", "go_back": "Ga terug", "go_to_folder": "Ga naar map", @@ -1040,11 +1047,13 @@ "home_page_delete_remote_err_local": "Lokale assets staan in verwijder selectie externe assets, overslaan", "home_page_favorite_err_local": "Lokale assets kunnen nog niet als favoriet worden aangemerkt, overslaan", "home_page_favorite_err_partner": "Partner assets kunnen nog niet ge-favoriet worden, overslaan", - "home_page_first_time_notice": "Als dit de eerste keer is dat je de app gebruikt, zorg er dan voor dat je een back-up album kiest, zodat de tijdlijn gevuld kan worden met foto's en video's uit het album.", + "home_page_first_time_notice": "Als dit de eerste keer is dat je de app gebruikt, zorg er dan voor dat je een back-up album kiest, zodat de tijdlijn gevuld kan worden met foto's en video's uit het album", + "home_page_locked_error_local": "Kan lokale bestanden niet naar de vergrendelde map verplaatsen, sla over", + "home_page_locked_error_partner": "Kan partnerbestanden niet naar de vergrendelde map verplaatsen, sla over", "home_page_share_err_local": "Lokale assets kunnen niet via een link gedeeld worden, overslaan", "home_page_upload_err_limit": "Kan maximaal 30 assets tegelijk uploaden, overslaan", - "host": "Host", "hour": "Uur", + "id": "ID", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geÃŧpload naar de Immich server", "image": "Afbeelding", @@ -1063,17 +1072,14 @@ "image_viewer_page_state_provider_download_success": "Download succesvol", "image_viewer_page_state_provider_share_error": "Deel Error", "immich_logo": "Immich logo", - "immich_web_interface": "Immich Web Interface", "import_from_json": "Importeren vanuit JSON", "import_path": "Import-pad", - "in_albums": "In {count, plural, one {# album} other {# albums}}", "in_archive": "In archief", "include_archived": "Toon gearchiveerde", "include_shared_albums": "Toon gedeelde albums", "include_shared_partner_assets": "Toon assets van gedeelde partner", "individual_share": "Individuele deellink", "individual_shares": "Individuele deellinks", - "info": "Info", "interval": { "day_at_onepm": "Iedere dag om 13 uur", "hours": "{hours, plural, one {Ieder uur} other {Iedere {hours, number} uren}}", @@ -1084,7 +1090,6 @@ "invalid_date_format": "Ongeldig datumformaat", "invite_people": "Mensen uitnodigen", "invite_to_album": "Uitnodigen voor album", - "items_count": "{count, plural, one {# item} other {# items}}", "jobs": "Taken", "keep": "Behouden", "keep_all": "Behoud alle", @@ -1097,7 +1102,6 @@ "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", "leave": "Verlaten", - "lens_model": "Lens model", "let_others_respond": "Laat anderen reageren", "level": "Niveau", "library": "Bibliotheek", @@ -1118,14 +1122,16 @@ "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", "local_network": "Lokaal netwerk", - "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven wifi-netwerk wordt gebruikt", + "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatie toestemming", - "location_permission_content": "Om de functie voor automatische serverwissel te gebruiken, heeft Immich toegang tot de exacte locatie nodig om de naam van het huidige wifi-netwerk te kunnen bepalen.", + "location_permission_content": "Om de functie voor automatische serverwissel te gebruiken, heeft Immich toegang tot de exacte locatie nodig om de naam van het huidige WiFi-netwerk te kunnen bepalen", "location_picker_choose_on_map": "Kies op kaart", "location_picker_latitude_error": "Voer een geldige breedtegraad in", "location_picker_latitude_hint": "Voer hier je breedtegraad in", "location_picker_longitude_error": "Voer een geldige lengtegraad in", "location_picker_longitude_hint": "Voer hier je lengtegraad in", + "lock": "Vergrendel", + "locked_folder": "Vergrendelde map", "log_out": "Uitloggen", "log_out_all_devices": "Uitloggen op alle apparaten", "logged_out_all_devices": "Uitgelogd op alle apparaten", @@ -1170,8 +1176,8 @@ "manage_your_devices": "Beheer je ingelogde apparaten", "manage_your_oauth_connection": "Beheer je OAuth koppeling", "map": "Kaart", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} foto's", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} foto's", "map_cannot_get_user_location": "Kan locatie van de gebruiker niet ophalen", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Gebruik deze locatie", @@ -1185,15 +1191,18 @@ "map_settings": "Kaartinstellingen", "map_settings_dark_mode": "Donkere modus", "map_settings_date_range_option_day": "Afgelopen 24 uur", - "map_settings_date_range_option_days": "Afgelopen {} dagen", + "map_settings_date_range_option_days": "Afgelopen {days} dagen", "map_settings_date_range_option_year": "Afgelopen jaar", - "map_settings_date_range_option_years": "Afgelopen {} jaar", + "map_settings_date_range_option_years": "Afgelopen {years} jaar", "map_settings_dialog_title": "Kaart Instellingen", "map_settings_include_show_archived": "Toon gearchiveerde", "map_settings_include_show_partners": "Inclusief partners", "map_settings_only_show_favorites": "Toon enkel favorieten", "map_settings_theme_settings": "Kaart thema", "map_zoom_to_see_photos": "Zoom uit om foto's te zien", + "mark_all_as_read": "Alles markeren als gelezen", + "mark_as_read": "Markeren als gelezen", + "marked_all_as_read": "Allen gemarkeerd als gelezen", "matches": "Overeenkomsten", "media_type": "Mediatype", "memories": "Herinneringen", @@ -1202,11 +1211,8 @@ "memories_setting_description": "Beheer wat je ziet in je herinneringen", "memories_start_over": "Opnieuw beginnen", "memories_swipe_to_close": "Swipe omhoog om te sluiten", - "memories_year_ago": "Een jaar geleden", - "memories_years_ago": "{} jaar geleden", "memory": "Herinnering", "memory_lane_title": "Herinneringen {title}", - "menu": "Menu", "merge": "Samenvoegen", "merge_people": "Mensen samenvoegen", "merge_people_limit": "Je kunt maximaal 5 gezichten tegelijk samenvoegen", @@ -1216,10 +1222,14 @@ "minimize": "Minimaliseren", "minute": "Minuut", "missing": "Missend", - "model": "Model", "month": "Maand", - "monthly_title_text_date_format": "MMMM y", "more": "Meer", + "move": "Verplaats", + "move_off_locked_folder": "Verplaats uit vergrendelde map", + "move_to_locked_folder": "Verplaats naar vergrendelde map", + "move_to_locked_folder_confirmation": "Deze foto’s en video’s worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map", + "moved_to_archive": "{count, plural, one {# asset} other {# assets}} verplaatst naar archief", + "moved_to_library": "{count, plural, one {# asset} other {# assets}} verplaatst naar bibliotheek", "moved_to_trash": "Naar de prullenbak verplaatst", "multiselect_grid_edit_date_time_err_read_only": "Kan datum van alleen-lezen asset(s) niet wijzigen, overslaan", "multiselect_grid_edit_gps_err_read_only": "Kan locatie van alleen-lezen asset(s) niet wijzigen, overslaan", @@ -1234,6 +1244,8 @@ "new_api_key": "Nieuwe API key", "new_password": "Nieuw wachtwoord", "new_person": "Nieuw persoon", + "new_pin_code": "Nieuwe PIN code", + "new_pin_code_subtitle": "Dit is de eerste keer dat u de vergrendelde map opent. Stel een pincode in om deze pagina veilig te openen", "new_user_created": "Nieuwe gebruiker aangemaakt", "new_version_available": "NIEUWE VERSIE BESCHIKBAAR", "newest_first": "Nieuwste eerst", @@ -1251,7 +1263,10 @@ "no_explore_results_message": "Upload meer foto's om je verzameling te verkennen.", "no_favorites_message": "Voeg favorieten toe om snel je beste foto's en video's te vinden", "no_libraries_message": "Maak een externe bibliotheek om je foto's en video's te bekijken", + "no_locked_photos_message": "Foto’s en video’s in de vergrendelde map zijn verborgen en worden niet weergegeven wanneer je door je bibliotheek bladert of zoekt.", "no_name": "Geen naam", + "no_notifications": "Geen notificaties", + "no_people_found": "Geen mensen gevonden", "no_places": "Geen plaatsen", "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", @@ -1260,6 +1275,7 @@ "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade assets, voer de volgende taak uit", "notes": "Opmerkingen", + "nothing_here_yet": "Hier staan nog geen items", "notification_permission_dialog_content": "Om meldingen in te schakelen, ga naar Instellingen en selecteer toestaan.", "notification_permission_list_tile_content": "Geef toestemming om meldingen te versturen.", "notification_permission_list_tile_enable_button": "Meldingen inschakelen", @@ -1267,21 +1283,17 @@ "notification_toggle_setting_description": "E-mailmeldingen inschakelen", "notifications": "Meldingen", "notifications_setting_description": "Beheer meldingen", - "oauth": "OAuth", "official_immich_resources": "OfficiÃĢle Immich bronnen", - "offline": "Offline", "offline_paths": "Offline paden", "offline_paths_description": "Deze resultaten kunnen te wijten zijn aan het handmatig verwijderen van bestanden die geen deel uitmaken van een externe bibliotheek.", - "ok": "Ok", "oldest_first": "Oudste eerst", "on_this_device": "Op dit apparaat", - "onboarding": "Onboarding", "onboarding_privacy_description": "De volgende (optionele) functies zijn afhankelijk van externe services en kunnen op elk moment worden uitgeschakeld in de beheerdersinstellingen.", "onboarding_theme_description": "Kies een kleurenthema voor de applicatie. Dit kun je later wijzigen in je instellingen.", "onboarding_welcome_description": "Laten we de applicatie instellen met enkele veelgebruikte instellingen.", "onboarding_welcome_user": "Welkom, {user}", - "online": "Online", "only_favorites": "Alleen favorieten", + "open": "Openen", "open_in_map_view": "Openen in kaartweergave", "open_in_openstreetmap": "Openen in OpenStreetMap", "open_the_search_filters": "Open de zoekfilters", @@ -1294,7 +1306,6 @@ "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", - "partner": "Partner", "partner_can_access": "{partner} heeft toegang tot", "partner_can_access_assets": "Al je foto's en video's behalve die in het archief of de prullenbak", "partner_can_access_location": "De locatie waar je foto's zijn genomen", @@ -1305,9 +1316,8 @@ "partner_page_partner_add_failed": "Partner toevoegen mislukt", "partner_page_select_partner": "Selecteer partner", "partner_page_shared_to_title": "Gedeeld met", - "partner_page_stop_sharing_content": "{} zal geen toegang meer hebben tot je fotos's.", + "partner_page_stop_sharing_content": "{partner} zal geen toegang meer hebben tot je fotos's.", "partner_sharing": "Delen met partner", - "partners": "Partners", "password": "Wachtwoord", "password_does_not_match": "Wachtwoord komt niet overeen", "password_required": "Wachtwoord vereist", @@ -1351,6 +1361,10 @@ "photos_count": "{count, plural, one {{count, number} foto} other {{count, number} foto's}}", "photos_from_previous_years": "Foto's van voorgaande jaren", "pick_a_location": "Kies een locatie", + "pin_code_changed_successfully": "PIN code succesvol gewijzigd", + "pin_code_reset_successfully": "PIN code succesvol gereset", + "pin_code_setup_successfully": "PIN code succesvol ingesteld", + "pin_verification": "Pincodeverificatie", "place": "Plaats", "places": "Plaatsen", "places_count": "{count, plural, one {{count, number} Plaats} other {{count, number} Plaatsen}}", @@ -1358,6 +1372,7 @@ "play_memories": "Herinneringen afspelen", "play_motion_photo": "Bewegingsfoto afspelen", "play_or_pause_video": "Video afspelen of pauzeren", + "please_auth_to_access": "Verifieer om toegang te krijgen", "port": "Poort", "preferences_settings_subtitle": "Beheer de voorkeuren van de app", "preferences_settings_title": "Voorkeuren", @@ -1367,21 +1382,19 @@ "previous_memory": "Vorige herinnering", "previous_or_next_photo": "Vorige of volgende foto", "primary": "Primair", - "privacy": "Privacy", + "profile": "Profiel", "profile_drawer_app_logs": "Logboek", "profile_drawer_client_out_of_date_major": "Mobiele app is verouderd. Werk bij naar de nieuwste hoofdversie.", "profile_drawer_client_out_of_date_minor": "Mobiele app is verouderd. Werk bij naar de nieuwste subversie.", "profile_drawer_client_server_up_to_date": "App en server zijn up-to-date", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server is verouderd. Werk bij naar de nieuwste hoofdversie.", "profile_drawer_server_out_of_date_minor": "Server is verouderd. Werk bij naar de nieuwste subversie.", "profile_image_of_user": "Profielfoto van {user}", "profile_picture_set": "Profielfoto ingesteld.", "public_album": "Openbaar album", "public_share": "Openbare deellink", - "purchase_account_info": "Supporter", "purchase_activated_subtitle": "Bedankt voor het ondersteunen van Immich en open-source software", - "purchase_activated_time": "Geactiveerd op {date, date}", + "purchase_activated_time": "Geactiveerd op {date}", "purchase_activated_title": "Je licentiesleutel is succesvol geactiveerd", "purchase_button_activate": "Activeren", "purchase_button_buy": "Kopen", @@ -1401,7 +1414,6 @@ "purchase_panel_info_1": "Het bouwen van Immich kost veel tijd en moeite, en we hebben fulltime engineers die eraan werken om het zo goed mogelijk te maken. Onze missie is om open-source software en ethische bedrijfspraktijken een duurzame inkomstenbron te laten worden voor ontwikkelaars en een ecosysteem te creÃĢren dat de privacy respecteert met echte alternatieven voor uitbuitende cloudservices.", "purchase_panel_info_2": "Omdat we ons inzetten om geen paywalls toe te voegen, krijg je met deze aankoop geen extra functies in Immich. We vertrouwen op gebruikers zoals jij om de verdere ontwikkeling van Immich te ondersteunen.", "purchase_panel_title": "Steun het project", - "purchase_per_server": "Per server", "purchase_per_user": "Per gebruiker", "purchase_remove_product_key": "Verwijder licentiesleutel", "purchase_remove_product_key_prompt": "Weet je zeker dat je de licentiesleutel wilt verwijderen?", @@ -1409,7 +1421,6 @@ "purchase_remove_server_product_key_prompt": "Weet je zeker dat je de server licentiesleutel wilt verwijderen?", "purchase_server_description_1": "Voor de volledige server", "purchase_server_description_2": "Supporter badge", - "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", "rating": "Ster waardering", "rating_clear": "Waardering verwijderen", @@ -1421,11 +1432,12 @@ "reassigned_assets_to_existing_person": "{count, plural, one {# asset} other {# assets}} opnieuw toegewezen aan {name, select, null {een bestaand persoon} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {# asset} other {# assets}} opnieuw toegewezen aan een nieuw persoon", "reassing_hint": "Geselecteerde assets toewijzen aan een bestaand persoon", - "recent": "Recent", "recent-albums": "Recente albums", "recent_searches": "Recente zoekopdrachten", "recently_added": "Onlangs toegevoegd", "recently_added_page_title": "Recent toegevoegd", + "recently_taken": "Recent genomen", + "recently_taken_page_title": "Recent Genomen", "refresh": "Vernieuwen", "refresh_encoded_videos": "Vernieuw gecodeerde video's", "refresh_faces": "Vernieuw gezichten", @@ -1445,6 +1457,8 @@ "remove_deleted_assets": "Verwijder offline bestanden", "remove_from_album": "Verwijder uit album", "remove_from_favorites": "Verwijderen uit favorieten", + "remove_from_locked_folder": "Verwijder uit de vergrendelde map", + "remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.", "remove_from_shared_link": "Verwijderen uit gedeelde link", "remove_memory": "Herinnering verwijderen", "remove_photo_from_memory": "Foto uit deze herinnering verwijderen", @@ -1461,7 +1475,6 @@ "repair": "Repareren", "repair_no_results_message": "Niet bijgehouden en ontbrekende bestanden zullen hier verschijnen", "replace_with_upload": "Vervangen door upload", - "repository": "Repository", "require_password": "Wachtwoord vereisen", "require_user_to_change_password_on_first_login": "Vereisen dat de gebruiker het wachtwoord wijzigt bij de eerste keer inloggen", "rescan": "Herscannen", @@ -1527,9 +1540,7 @@ "search_page_motion_photos": "Bewegende foto's", "search_page_no_objects": "Geen objectgegevens beschikbaar", "search_page_no_places": "Geen locatiegegevens beschikbaar", - "search_page_screenshots": "Screenshots", "search_page_search_photos_videos": "Zoek naar je foto's en video's", - "search_page_selfies": "Selfies", "search_page_things": "Dingen", "search_page_view_all_button": "Bekijk alle", "search_page_your_activity": "Je activiteit", @@ -1560,6 +1571,7 @@ "select_keep_all": "Selecteer alles behouden", "select_library_owner": "Selecteer bibliotheekeigenaar", "select_new_face": "Selecteer nieuw gezicht", + "select_person_to_tag": "Selecteer een persoon om te taggen", "select_photos": "Selecteer foto's", "select_trash_all": "Selecteer alles naar prullenbak verplaatsen", "select_user_for_sharing_page_err_album": "Album aanmaken mislukt", @@ -1569,7 +1581,6 @@ "send_welcome_email": "Stuur welkomstmail", "server_endpoint": "Server url", "server_info_box_app_version": "Appversie", - "server_info_box_server_url": "Server URL", "server_offline": "Server offline", "server_online": "Server online", "server_stats": "Serverstatistieken", @@ -1590,12 +1601,12 @@ "setting_languages_apply": "Toepassen", "setting_languages_subtitle": "Wijzig de taal van de app", "setting_languages_title": "Taal", - "setting_notifications_notify_failures_grace_period": "Fouten van de achtergrond back-up melden: {}", - "setting_notifications_notify_hours": "{} uur", + "setting_notifications_notify_failures_grace_period": "Fouten van de achtergrond back-up melden: {duration}", + "setting_notifications_notify_hours": "{count} uur", "setting_notifications_notify_immediately": "meteen", - "setting_notifications_notify_minutes": "{} minuten", + "setting_notifications_notify_minutes": "{count} minuten", "setting_notifications_notify_never": "nooit", - "setting_notifications_notify_seconds": "{} seconden", + "setting_notifications_notify_seconds": "{count} seconden", "setting_notifications_single_progress_subtitle": "Gedetailleerde informatie over de uploadvoortgang per asset", "setting_notifications_single_progress_title": "Gedetailleerde informatie over achtergrond back-ups tonen", "setting_notifications_subtitle": "Voorkeuren voor meldingen beheren", @@ -1607,10 +1618,12 @@ "settings": "Instellingen", "settings_require_restart": "Start Immich opnieuw op om deze instelling toe te passen", "settings_saved": "Instellingen opgeslagen", + "setup_pin_code": "Stel een PIN code in", "share": "Delen", "share_add_photos": "Foto's toevoegen", - "share_assets_selected": "{} geselecteerd", + "share_assets_selected": "{count} geselecteerd", "share_dialog_preparing": "Voorbereiden...", + "share_link": "Deel link", "shared": "Gedeeld", "shared_album_activities_input_disable": "Reactie is uitgeschakeld", "shared_album_activity_remove_content": "Wil je deze activiteit verwijderen?", @@ -1623,34 +1636,33 @@ "shared_by_user": "Gedeeld door {user}", "shared_by_you": "Gedeeld door jou", "shared_from_partner": "Foto's van {partner}", - "shared_intent_upload_button_progress_text": "{} / {} geÃŧpload", + "shared_intent_upload_button_progress_text": "{current} / {total} geÃŧpload", "shared_link_app_bar_title": "Gedeelde links", "shared_link_clipboard_copied_massage": "Gekopieerd naar klembord", - "shared_link_clipboard_text": "Link: {}\nWachtwoord: {}", + "shared_link_clipboard_text": "Link: {link}\nWachtwoord: {password}", "shared_link_create_error": "Fout bij het maken van een gedeelde link", "shared_link_edit_description_hint": "Voer beschrijving voor de gedeelde link in", "shared_link_edit_expire_after_option_day": "1 dag", - "shared_link_edit_expire_after_option_days": "{} dagen", + "shared_link_edit_expire_after_option_days": "{count} dagen", "shared_link_edit_expire_after_option_hour": "1 uur", - "shared_link_edit_expire_after_option_hours": "{} uren", + "shared_link_edit_expire_after_option_hours": "{count} uren", "shared_link_edit_expire_after_option_minute": "1 minuut", - "shared_link_edit_expire_after_option_minutes": "{} minuten", - "shared_link_edit_expire_after_option_months": "{} maanden", - "shared_link_edit_expire_after_option_year": "{} jaar", + "shared_link_edit_expire_after_option_minutes": "{count} minuten", + "shared_link_edit_expire_after_option_months": "{count} maanden", + "shared_link_edit_expire_after_option_year": "{count} jaar", "shared_link_edit_password_hint": "Voer wachtwoord voor de gedeelde link in", "shared_link_edit_submit_button": "Link bijwerken", "shared_link_error_server_url_fetch": "Kan de server url niet ophalen", - "shared_link_expires_day": "Verloopt over {} dag", - "shared_link_expires_days": "Verloopt over {} dagen", - "shared_link_expires_hour": "Verloopt over {} uur", - "shared_link_expires_hours": "Verloopt over {} uur", - "shared_link_expires_minute": "Verloopt over {} minuut", - "shared_link_expires_minutes": "Verloopt over {} minuten", + "shared_link_expires_day": "Verloopt over {count} dag", + "shared_link_expires_days": "Verloopt over {count} dagen", + "shared_link_expires_hour": "Verloopt over {count} uur", + "shared_link_expires_hours": "Verloopt over {count} uur", + "shared_link_expires_minute": "Verloopt over {count} minuut", + "shared_link_expires_minutes": "Verloopt over {count} minuten", "shared_link_expires_never": "Verloopt ∞", - "shared_link_expires_second": "Verloopt over {} seconde", - "shared_link_expires_seconds": "Verloopt over {} seconden", + "shared_link_expires_second": "Verloopt over {count} seconde", + "shared_link_expires_seconds": "Verloopt over {count} seconden", "shared_link_individual_shared": "Individueel gedeeld", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Beheer gedeelde links", "shared_link_options": "Opties voor gedeelde links", "shared_links": "Gedeelde links", @@ -1685,7 +1697,6 @@ "show_search_options": "Zoekopties weergeven", "show_shared_links": "Toon gedeelde links", "show_slideshow_transition": "Diavoorstellingsovergang tonen", - "show_supporter_badge": "Supporter badge", "show_supporter_badge_description": "Toon een supporterbadge", "shuffle": "Willekeurig", "sidebar": "Zijbalk", @@ -1712,17 +1723,15 @@ "stack_select_one_photo": "Selecteer ÊÊn primaire foto voor de stapel", "stack_selected_photos": "Geselecteerde foto's stapelen", "stacked_assets_count": "{count, plural, one {# asset} other {# assets}} gestapeld", - "stacktrace": "Stacktrace", - "start": "Start", "start_date": "Startdatum", "state": "Staat", - "status": "Status", "stop_motion_photo": "Bewegingsfoto stoppen", "stop_photo_sharing": "Stoppen met het delen van je foto's?", "stop_photo_sharing_description": "{partner} zal geen toegang meer hebben tot je foto's.", "stop_sharing_photos_with_user": "Stop met het delen van je foto's met deze gebruiker", "storage": "Opslagruimte", "storage_label": "Opslaglabel", + "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", "suggestions": "Suggesties", @@ -1731,11 +1740,9 @@ "support_and_feedback": "Ondersteuning & feedback", "support_third_party_description": "Je Immich installatie is door een derde partij samengesteld. Problemen die je ervaart, kunnen door dat pakket veroorzaakt zijn. Meld problemen in eerste instantie bij hen via de onderstaande links.", "swap_merge_direction": "Wissel richting voor samenvoegen om", - "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geÃŧploade video’s en foto’s naar de geselecteerde back-up albums", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", - "tag": "Tag", "tag_assets": "Assets taggen", "tag_created": "Tag aangemaakt: {tag}", "tag_feature_description": "Bladeren door foto's en video's gegroepeerd op tags", @@ -1743,13 +1750,11 @@ "tag_people": "Mensen taggen", "tag_updated": "Tag bijgewerkt: {tag}", "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", - "tags": "Tags", - "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", "theme_selection_description": "Stel het thema automatisch in op licht of donker op basis van de systeemvoorkeuren van je browser", "theme_setting_asset_list_storage_indicator_title": "Toon opslag indicator bij de asset tegels", - "theme_setting_asset_list_tiles_per_row_title": "Aantal assets per rij ({})", + "theme_setting_asset_list_tiles_per_row_title": "Aantal assets per rij ({count})", "theme_setting_colorful_interface_subtitle": "Pas primaire kleuren toe op achtergronden.", "theme_setting_colorful_interface_title": "Kleurrijke interface", "theme_setting_image_viewer_quality_subtitle": "De kwaliteit van de gedetailleerde-fotoweergave aanpassen", @@ -1784,13 +1789,14 @@ "trash_no_results_message": "Hier verschijnen foto's en video's die in de prullenbak zijn geplaatst.", "trash_page_delete_all": "Verwijder alle", "trash_page_empty_trash_dialog_content": "Wil je de prullenbak leegmaken? Deze items worden permanent verwijderd van Immich", - "trash_page_info": "Verwijderde items worden permanent verwijderd na {} dagen", + "trash_page_info": "Verwijderde items worden permanent verwijderd na {days} dagen", "trash_page_no_assets": "Geen verwijderde assets", "trash_page_restore_all": "Herstel alle", "trash_page_select_assets_btn": "Selecteer assets", - "trash_page_title": "Prullenbak ({})", + "trash_page_title": "Prullenbak ({count})", "trashed_items_will_be_permanently_deleted_after": "Items in de prullenbak worden na {days, plural, one {# dag} other {# dagen}} permanent verwijderd.", - "type": "Type", + "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", + "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", "unarchive": "Herstellen uit archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", "unfavorite": "Verwijderen uit favorieten", @@ -1814,6 +1820,7 @@ "untracked_files": "Niet bijgehouden bestanden", "untracked_files_decription": "Deze bestanden worden niet bijgehouden door de applicatie. Dit kan het resultaat zijn van een mislukte verplaatsing, onderbroken upload of een bug", "up_next": "Volgende", + "updated_at": "GeÃŧpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", "upload_concurrency": "Upload gelijktijdigheid", @@ -1826,15 +1833,17 @@ "upload_status_errors": "Fouten", "upload_status_uploaded": "GeÃŧpload", "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", - "upload_to_immich": "Uploaden naar Immich ({})", + "upload_to_immich": "Uploaden naar Immich ({count})", "uploading": "Aan het uploaden", - "url": "URL", "usage": "Gebruik", + "use_biometric": "Gebruik biometrische authenticatie", "use_current_connection": "gebruik huidige verbinding", "use_custom_date_range": "Gebruik in plaats daarvan een aangepast datumbereik", "user": "Gebruiker", + "user_has_been_deleted": "Deze gebruiker is verwijderd.", "user_id": "Gebruikers ID", "user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} asset {deze asset} other {dit}} geliket", + "user_pin_code_settings_description": "Beheer je PIN code", "user_purchase_settings": "Kopen", "user_purchase_settings_description": "Beheer je aankoop", "user_role_set": "{user} instellen als {role}", @@ -1857,7 +1866,6 @@ "version_announcement_overlay_title": "Nieuwe serverversie beschikbaar 🎉", "version_history": "Versiegeschiedenis", "version_history_item": "{version} geïnstalleerd op {date}", - "video": "Video", "video_hover_setting": "Speel video thumbnail af bij hoveren", "video_hover_setting_description": "Speel video thumbnail af wanneer de muis over het item beweegt. Zelfs wanneer uitgeschakeld, kan het afspelen worden gestart door de muis over het afspeelpictogram te bewegen.", "videos": "Video's", @@ -1872,6 +1880,7 @@ "view_name": "Bekijken", "view_next_asset": "Bekijk volgende asset", "view_previous_asset": "Bekijk vorige asset", + "view_qr_code": "QR-code bekijken", "view_stack": "Bekijk stapel", "viewer_remove_from_stack": "Verwijder van Stapel", "viewer_stack_use_as_main_asset": "Gebruik als Hoofd Asset", @@ -1879,14 +1888,14 @@ "visibility_changed": "Zichtbaarheid gewijzigd voor {count, plural, one {# persoon} other {# mensen}}", "waiting": "Wachtend", "warning": "Waarschuwing", - "week": "Week", "welcome": "Welkom", "welcome_to_immich": "Welkom bij Immich", - "wifi_name": "WiFi naam", + "wifi_name": "WiFi-naam", + "wrong_pin_code": "Onjuiste pincode", "year": "Jaar", "years_ago": "{years, plural, one {# jaar} other {# jaar}} geleden", "yes": "Ja", "you_dont_have_any_shared_links": "Je hebt geen gedeelde links", - "your_wifi_name": "Je WiFi naam", + "your_wifi_name": "Je WiFi-naam", "zoom_image": "Inzoomen" } diff --git a/i18n/nn.json b/i18n/nn.json index 6de0f3401b..a26311cf81 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -2,8 +2,9 @@ "about": "Om", "account": "Konto", "account_settings": "Kontoinnstillingar", - "acknowledge": "Bekreft", + "acknowledge": "Merk som lese", "action": "Handling", + "action_common_update": "Oppdater", "actions": "Handlingar", "active": "Aktive", "activity": "Aktivitet", @@ -13,6 +14,7 @@ "add_a_location": "Legg til ein stad", "add_a_name": "Legg til eit namn", "add_a_title": "Legg til ein tittel", + "add_endpoint": "Legg til endepunkt", "add_exclusion_pattern": "Legg til unnlatingsmønster", "add_import_path": "Legg til sti for importering", "add_location": "Legg til stad", @@ -22,6 +24,8 @@ "add_photos": "Legg til bilete", "add_to": "Legg tilâ€Ļ", "add_to_album": "Legg til album", + "add_to_album_bottom_sheet_added": "Lagt til i {album}", + "add_to_album_bottom_sheet_already_exists": "Allereie i {album}", "add_to_shared_album": "Legg til delt album", "add_url": "Legg til URL", "added_to_archive": "Lagt til arkiv", @@ -35,12 +39,13 @@ "authentication_settings_disable_all": "Er du sikker at du ynskjer ÃĨ gjera alle innloggingsmetodar uverksame? Innlogging vil bli heilt uverksam.", "authentication_settings_reenable": "For ÃĨ aktivere pÃĨ nytt, bruk ein Server Command.", "background_task_job": "Bakgrunnsjobbar", - "backup_database": "Sikkerheistkopier database", - "backup_database_enable_description": "Aktiver sikkerheitskopiering av database", - "backup_keep_last_amount": "Antal sikkerheitskopiar ÃĨ behalde", - "backup_settings": "Sikkerheitskopi-innstillingar", - "backup_settings_description": "Handsam innstillingar for sikkerheitskopiering av database", + "backup_database": "Lag tryggingskopi av database", + "backup_database_enable_description": "Aktiver tryggingskopiering av database", + "backup_keep_last_amount": "Antal tryggingskopiar ÃĨ behalde", + "backup_settings": "Tryggingskopi-innstillingar", + "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du fÃĨr inga varsling ved feil.", "check_all": "Sjekk alle", + "cleanup": "Opprydding", "cleared_jobs": "Rydda jobbar for: {job}", "config_set_by_file": "Oppsettet blir sett av ei oppsettfil", "confirm_delete_library": "Er du sikker at du vil slette biblioteket {library}?", @@ -121,7 +126,7 @@ "machine_learning_max_detection_distance_description": "Den største skilnaden mellom to bilete for ÃĨ rekne dei som duplikat, frÃĨ 0.001-0.1. Større verdiar finn fleire duplikat, men kan gje falske treff.", "machine_learning_max_recognition_distance": "Maksimal attkjenningsverdi", "machine_learning_min_detection_score": "Minimum deteksjonsresultat", - "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, pÃĨ ein skala frÃĨ 0-1. LÃĨgare verdiar vil oppdaga fleire ansikt, men kan føre til falske positive", + "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, pÃĨ ein skala frÃĨ 0 til 1. LÃĨgare verdiar vil oppdage fleire ansikt, men kan føre til feilaktige treff.", "machine_learning_min_recognized_faces": "Minimum gjenkjende ansikt", "machine_learning_settings": "Innstillingar for maskinlÃĻring", "machine_learning_settings_description": "Administrer maskinlÃĻringsfunksjonar og innstillingar", @@ -200,14 +205,44 @@ "backward": "Bakover", "camera": "Kamera", "cancel": "Avbryt", + "change_password_form_confirm_password": "Stadfest passord", "city": "By", - "clear": "Fjern", + "clear": "Tøm", + "clear_all": "Tøm alt", + "clear_all_recent_searches": "Tøm alle nylige søk", + "clear_message": "Tøm melding", + "clear_value": "Tøm verdi", + "client_cert_enter_password": "Oppgi passord", + "client_cert_import": "Importer", + "client_cert_import_success_msg": "Klientsertifikat vart importert", + "client_cert_invalid_msg": "Ugyldig sertifikatfil eller feil passord", + "client_cert_remove_msg": "Klientsertifikat er fjerna", + "client_cert_subtitle": "Støttar berre PKCS12-formatet (.p12, .pfx). Import og fjerning av sertifikat er berre tilgjengeleg før innlogging", + "client_cert_title": "SSL-klientsertifikat", "clockwise": "Med klokka", "close": "Lukk", + "collapse": "Gøym", + "collapse_all": "Gøym alle", "color": "Farge", - "confirm": "Bekreft", - "contain": "Inneheld", + "color_theme": "Fargetema", + "comment_deleted": "Kommentar vart sletta", + "comment_options": "Kommentarval", + "comments_and_likes": "Kommentarar og likerklikk", + "comments_are_disabled": "Kommentering er slÃĨtt av", + "common_create_new_album": "Lag nytt album", + "common_server_error": "Kontroller nettverkstilkoplinga di, sørg for at tenaren er tilgjengeleg, og at app- og tenarversjonane er kompatible.", + "completed": "Fullført", + "confirm": "Stadfest", + "confirm_admin_password": "Stadfest administratorpassord", + "confirm_delete_face": "Er du sikker pÃĨ at du vil slette {name} sitt ansikt frÃĨ ressursen?", + "confirm_delete_shared_link": "Er du sikker pÃĨ at du vil slette denne delte lenka?", + "confirm_keep_this_delete_others": "Alle andre ressursar i bunken vil bli sletta, bortsett frÃĨ denne. Er du sikker pÃĨ at du vil halde fram?", + "confirm_password": "Stadfest passord", + "contain": "Tilpass til vindauget", + "context": "Samanheng", "continue": "Hald fram", + "control_bottom_app_bar_album_info_shared": "{count} element ¡ Delt", + "control_bottom_app_bar_create_new_album": "Lag nytt album", "country": "Land", "cover": "Dekk", "covers": "Dekker", @@ -238,7 +273,6 @@ "folders_feature_description": "Bla gjennom mappe for bileta og videoane pÃĨ filsystemet", "hour": "Time", "image": "Bilde", - "info": "Info", "jobs": "OppgÃĨver", "keep": "Behald", "language": "SprÃĨk", @@ -249,7 +283,6 @@ "light": "Lys", "list": "Liste", "loading": "Lastar", - "login": "Login", "longitude": "Lengdegrad", "look": "UtsjÃĨnad", "make": "Produsent", @@ -276,24 +309,19 @@ "no_shared_albums_message": "Lag eit album for ÃĨ dele bilete og videoar med folk i nettverket ditt", "notes": "Noter", "notifications": "Varsel", - "ok": "Ok", "options": "Val", "or": "eller", - "original": "original", "other": "Anna", "owner": "Eigar", - "partner": "Partner", "partner_can_access_assets": "Alle bileta og videoane dine unntatt dei i Arkivert og Sletta", "partner_can_access_location": "Staden der bileta dine vart tekne", "password": "Passord", "path": "Sti", "pattern": "Mønster", - "pause": "Pause", "paused": "Pausa", "pending": "Ventar", "people": "Folk", "people_feature_description": "Bla gjennom foto og videoar gruppert etter folk", - "person": "Person", "photo_shared_all_users": "Ser ut som du delte bileta dine med alle brukarar eller at du ikkje har nokon brukar ÃĨ dele med.", "photos": "Bilete", "photos_and_videos": "Foto og Video", @@ -301,7 +329,6 @@ "place": "Stad", "places": "Stad", "play": "Spel av", - "port": "Port", "preview": "Førehandsvisning", "previous": "Forrige", "primary": "Hoved", @@ -310,24 +337,156 @@ "purchase_button_buy": "Kjøp", "purchase_button_select": "Vel", "purchase_individual_title": "Induviduell", - "purchase_server_title": "Server", "reassign": "Vel pÃĨ nytt", "recent": "Nyleg", - "refresh": "Oppdater", + "refresh": "Last inn pÃĨ nytt", + "refresh_encoded_videos": "Oppfrisk ferdigbehandla videoa", + "refresh_faces": "Oppfrisk ansikt", + "refresh_metadata": "Oppfrisk metadata", + "refresh_thumbnails": "Oppfrisk miniatyrbilete", "refreshed": "Oppdatert", + "refreshes_every_file": "Les alle eksisterande og nye filer pÃĨ nytt", + "refreshing_encoded_video": "Lastar inn ferdigbehandla video pÃĨ nytt", + "refreshing_faces": "Oppfriskar ansiktsdata", + "refreshing_metadata": "Oppfriskar metadata", + "regenerating_thumbnails": "Regenererer miniatyrbilete", "remove": "Fjern", - "rename": "Endre namn", - "repair": "Reparasjon", + "remove_assets_album_confirmation": "Er du sikker pÃĨ at du vil fjerne {count, plural, one {# asset} other {# assets}} fra albumet?", + "remove_assets_shared_link_confirmation": "Er du sikker pÃĨ at du vil fjerne {count, plural, one {# asset} other {# assets}} frÃĨ denne delte lenka?", + "remove_assets_title": "Fjern ressursar?", + "remove_custom_date_range": "Fjern egendefinert datoperiode", + "remove_deleted_assets": "Fjern sletta ressursar", + "remove_from_album": "Fjern frÃĨ album", + "remove_from_favorites": "Fjern frÃĨ favorittar", + "remove_from_shared_link": "Fjern frÃĨ delt lenke", + "remove_memory": "Fjern minne", + "remove_photo_from_memory": "Fjern bilete frÃĨ dette minne", + "remove_url": "Fjern URL", + "remove_user": "Fjern brukar", + "removed_api_key": "Fjerna API-nøkkel: {name}", + "removed_from_archive": "Fjerna frÃĨ arkiv", + "removed_from_favorites": "Fjerna frÃĨ favorittar", + "removed_from_favorites_count": "{count, plural, other {Fjerna #}} frÃĨ favorittar", + "removed_memory": "Fjerna minne", + "removed_photo_from_memory": "Fjerna bilete frÃĨ minne", + "removed_tagged_assets": "Fjerna tagg frÃĨ {count, plural, one {# ressurs} other {# ressursar}}", + "rename": "Gi nytt namn", + "repair": "Reparer", + "repair_no_results_message": "Uspora og manglande filer vil visast her", + "replace_with_upload": "Erstatt med opplasting", + "repository": "Lager", + "require_password": "Krev passord", + "require_user_to_change_password_on_first_login": "Krev at brukaren endrar passord ved første innlogging", + "rescan": "Skann pÃĨ nytt", "reset": "Tilbakestill", - "restore": "Tilbakestill", - "resume": "Fortsett", + "reset_password": "Tilbakestill passord", + "reset_people_visibility": "Tilbakestill synlegheit for personar", + "reset_to_default": "Tilbakestill til standard", + "resolve_duplicates": "Handter duplikat", + "resolved_all_duplicates": "Alle duplikat er handterte", + "restore": "Gjenopprett", + "restore_all": "Gjenopprett alle", + "restore_user": "Gjenopprett brukar", + "restored_asset": "Ressurs gjenoppretta", + "resume": "Gjenoppta", + "retry_upload": "Prøv opplasting pÃĨ nytt", + "review_duplicates": "GÃĨ gjennom duplikat", "role": "Rolle", + "role_editor": "Redaktør", + "role_viewer": "Observatør", "save": "Lagre", + "save_to_gallery": "Lagre til galleri", + "saved_api_key": "API-nøkkel lagra", + "saved_profile": "Profil lagra", + "saved_settings": "Innstillingar lagra", + "say_something": "Skriv ein kommentar", + "scaffold_body_error_occurred": "Det oppstod ein feil", + "scan_all_libraries": "Skann gjennom alle bibliotek", "scan_library": "Skann", + "scan_settings": "Skann innstillingar", + "scanning_for_album": "Skanning for album...", "search": "Søk", + "search_albums": "Søk album", + "search_by_context": "Søk etter samanheng", + "search_by_description": "Søk etter beskrivelse", + "search_by_description_example": "Søndagstur med kvikklunsj", + "search_by_filename": "Søk etter filnamn eller filformat", + "search_by_filename_example": "t.d. IMG_1234.JPG eller PNG", + "search_camera_make": "Søk etter kamera produsent...", + "search_camera_model": "Søk etter kamera modell...", + "search_city": "Søk etter by...", + "search_country": "Søk etter land...", + "search_filter_apply": "Bruk filter", + "search_filter_camera_title": "Vel kameratype", + "search_filter_date": "Dato", + "search_filter_date_interval": "{start} til {end}", + "search_filter_date_title": "Vel eit datointervall", + "search_filter_display_option_not_in_album": "Ikkje i album", + "search_filter_display_options": "Visingsval", + "search_filter_filename": "Søk etter filnamn", + "search_filter_location": "Lokasjon", + "search_filter_location_title": "Vel lokasjon", + "search_filter_media_type": "Mediatype", + "search_filter_media_type_title": "Vel mediatype", + "search_filter_people_title": "Vel personar", + "search_for": "Søk etter", + "search_for_existing_person": "Søk etter ein eksisterande person", + "search_no_more_result": "Ingen fleire resultat", + "search_no_people": "Ingen personar", + "search_no_people_named": "Ingen personar ved namn \"{name}\"", + "search_no_result": "Fann ingen resultat – prøv eit anna søkjeord eller ei anna kombinasjon", + "search_options": "Søkjeval", + "search_page_categories": "Kategoriar", + "search_page_motion_photos": "Levande bilete", + "search_page_no_objects": "Ingen objektinformasjon tilgjengeleg", + "search_page_no_places": "Ingen stadinformasjon tilgjengeleg", + "search_page_screenshots": "Skjermbilete", + "search_page_search_photos_videos": "Søk etter bileta og videoane dine", + "search_page_selfies": "Sjølvbilete", + "search_page_things": "Ting", + "search_page_view_all_button": "Vis alle", + "search_page_your_activity": "Din aktivitet", + "search_page_your_map": "Ditt kart", + "search_people": "Søk etter personar", + "search_places": "Søk etter stad", + "search_rating": "Søk etter vurdering â€Ļ", + "search_result_page_new_search_hint": "Nytt søk", + "search_settings": "Søkjeinnstillingar", + "search_state": "Søk etter fylke â€Ļ", + "search_suggestion_list_smart_search_hint_1": "Smart søk er aktivert som standard. For ÃĨ søkje etter metadata, bruk denne syntaksen: ", + "search_suggestion_list_smart_search_hint_2": "m:søkjeord", + "search_tags": "Søk etter taggar â€Ļ", + "search_timezone": "Søk etter tidssone â€Ļ", + "search_type": "Søketype", "search_your_photos": "Søk i dine bilete", + "searching_locales": "Søkjer etter sprÃĨkinnstillingarâ€Ļ", "second": "Sekund", + "see_all_people": "SjÃĨ alle personar", + "select": "Vel", + "select_album_cover": "Vel forsidebilete", + "select_all": "Vel alle", + "select_all_duplicates": "Vel alle duplikatar", + "select_avatar_color": "Vel avatarfarge", + "select_face": "Vel ansikt", + "select_featured_photo": "Vel framheva bilete", + "select_from_computer": "Vel frÃĨ datamaskin", + "select_keep_all": "Vel ÃĨ behald alle", + "select_library_owner": "Vel bibliotekeigar", + "select_new_face": "Vel nytt ansikt", + "select_photos": "Vel bilete", + "select_trash_all": "Vel fjern alle", + "select_user_for_sharing_page_err_album": "Feil ved oppretting av album", "selected": "Valgt", + "selected_count": "{count, plural, other {# valgt}}", + "send_message": "Send melding", + "send_welcome_email": "Send velkomst-e-post", + "server_endpoint": "Tenar-endepunkt", + "server_info_box_app_version": "App Versjon", + "server_info_box_server_url": "Tenar URL", + "server_offline": "Tenar Frakopla", + "server_online": "Tenar i drift", + "server_stats": "Tenarstatistikk", + "server_version": "Tenarversjon", "set": "Sett", "settings": "Innstillingar", "share": "Del", @@ -341,20 +500,15 @@ "sort_title": "Tittel", "source": "Kjelde", "stack": "Stabel", - "start": "Start", "state": "Region", - "status": "Status", "stop_photo_sharing": "Stopp ÃĨ dele bileta dine?", "stop_photo_sharing_description": "{partner} vil ikkje lenger kunne fÃĨ tilgang til bileta dine.", "stop_sharing_photos_with_user": "Stopp ÃĨ dele bileta dine med denne brukaren", "storage": "Lagringsplass", "submit": "Send inn", "suggestions": "Forslag", - "support": "Support", "sync": "Synk", - "tag": "Tag", "tag_feature_description": "Bla gjennom bilete og videoar gruppert etter logiske tag-tema", - "tags": "Tags", "theme": "Tema", "timeline": "Tidslinje", "timezone": "Tidssone", @@ -362,10 +516,8 @@ "to_favorite": "Favoritt", "to_login": "Innlogging", "to_trash": "Søppel", - "total": "Total", "trash": "Søppel", "trash_no_results_message": "Sletta foto og videoar vil dukke opp her.", - "type": "Type", "unfavorite": "Fjern favoritt", "unknown": "Ukjent", "unlimited": "Ubegrensa", @@ -373,7 +525,6 @@ "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplasta", - "url": "URL", "usage": "Bruk", "user": "Brukar", "user_purchase_settings": "Kjøp", @@ -389,7 +540,6 @@ "version_announcement_closing": "Din ven, Alex", "version_history": "Versjonshistorie", "version_history_item": "Installert {version} den {date}", - "video": "Video", "video_hover_setting": "Spel av førehandsvisining medan du held over musepeikaren", "videos": "Videoar", "videos_count": "{count, plural, one {# Video} other {# Videoar}}", diff --git a/i18n/pl.json b/i18n/pl.json index 1fc130ff56..5da38eac60 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -1,5 +1,5 @@ { - "about": "O", + "about": "O aplikacji", "account": "Konto", "account_settings": "Ustawienia konta", "acknowledge": "Zrozumiałem/łam", @@ -26,6 +26,7 @@ "add_to_album": "Dodaj do albumu", "add_to_album_bottom_sheet_added": "Dodano do {album}", "add_to_album_bottom_sheet_already_exists": "JuÅŧ w {album}", + "add_to_locked_folder": "Dodaj do folderu zablokowanego", "add_to_shared_album": "Dodaj do udostępnionego albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano do archiwum", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Czy jesteś pewny, Åŧe chcesz wyłączyć wszystkie metody logowania? Logowanie będzie całkowicie wyłączone.", "authentication_settings_reenable": "Aby ponownie włączyć, uÅŧyj Polecenia serwera.", "background_task_job": "Zadania w Tle", - "backup_database": "Kopia zapasowa bazy danych", - "backup_database_enable_description": "Włącz kopię zapasową bazy danych", - "backup_keep_last_amount": "Ile poprzednich kopii zapasowych przechowywać", - "backup_settings": "Ustawienia kopii zapasowej", - "backup_settings_description": "Zarządzaj ustawieniami kopii zapasowej bazy danych", + "backup_database": "UtwÃŗrz Zrzut Bazy Danych", + "backup_database_enable_description": "Włącz zrzuty bazy danych", + "backup_keep_last_amount": "Ile poprzednich zrzutÃŗw przechowywać", + "backup_settings": "Ustawienia zrzutu bazy danych", + "backup_settings_description": "Zarządzanie ustawieniami zrzutu bazy danych. Uwaga: Te zadania nie są monitorowane i nie otrzymasz powiadomienia o niepowodzeniu.", "check_all": "Zaznacz Wszystko", "cleanup": "Czyszczenie", "cleared_jobs": "Usunięto zadania dla: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Aby potwierdzić, wpisz \"{email}\" poniÅŧej", "confirm_reprocess_all_faces": "Czy na pewno chcesz ponownie przetworzyć wszystkie twarze? Spowoduje to utratę nazwanych osÃŗb.", "confirm_user_password_reset": "Czy na pewno chcesz zresetować hasło uÅŧytkownika {user}?", + "confirm_user_pin_code_reset": "Czy jesteś pewny, Åŧe chcesz zresetować kod pin dla uÅŧytkownika {user}?", "create_job": "UtwÃŗrz zadanie", "cron_expression": "WyraÅŧenie Cron", "cron_expression_description": "Ustaw interwał skanowania przy pomocy formatu Cron'a. Po więcej informacji na temat formatu Cron zobacz . Crontab Guru", @@ -63,7 +65,7 @@ "external_library_created_at": "Biblioteka zewnętrzna (stworzona dnia {date})", "external_library_management": "Zarządzanie Bibliotekami Zewnętrznymi", "face_detection": "Wykrywanie twarzy", - "face_detection_description": "Wykrywanie twarzy w zasobach uÅŧywając uczenia maszynowego. Twarze w filmach wykryte zostaną tylko jeÅŧeli są widoczne w miniaturze. \"Wszystkie\" ponownie przetwarza wszystkie zasoby. \"Reset\" dodatkowo usuwa wszystkie bieÅŧące dane twarzy. \"Brakujące\" dodaje do kolejki tylko zasoby, ktÃŗre nie zostały jeszcze przetworzone. Wykryte twarze zostaną dodane do kolejki Rozpoznawania Twarzy, aby związać je z istniejącą osobą albo stworzyć nową osobę.", + "face_detection_description": "Wykrywanie twarzy w zasobach uÅŧywając uczenia maszynowego. Twarze w filmach wykryte zostaną tylko jeÅŧeli są widoczne w miniaturze. \"OdświeÅŧ\" ponownie przetwarza wszystkie zasoby. \"Reset\" dodatkowo usuwa wszystkie bieÅŧące dane twarzy. \"Brakujące\" dodaje do kolejki tylko zasoby, ktÃŗre nie zostały jeszcze przetworzone. Wykryte twarze zostaną dodane do kolejki Rozpoznawania Twarzy, aby związać je z istniejącą osobą albo stworzyć nową osobę.", "facial_recognition_job_description": "Grupuj wykryte twarze. Ten krok uruchamiany jest po zakończeniu wykrywania twarzy. „Wszystkie” – ponownie kategoryzuje wszystkie twarze. „Brakujące” – kategoryzuje twarze, do ktÃŗrych nie przypisano osoby.", "failed_job_command": "Polecenie {command} nie powiodło się dla zadania: {job}", "force_delete_user_warning": "UWAGA: UÅŧytkownik i wszystkie zasoby uÅŧytkownika zostaną natychmiast trwale usunięte. Nie moÅŧna tego cofnąć, a plikÃŗw nie będzie moÅŧna przywrÃŗcić.", @@ -96,8 +98,8 @@ "job_settings": "Ustawienia Zadań", "job_settings_description": "Zarządzaj wspÃŗÅ‚bieÅŧnością zadań", "job_status": "Status Zadań", - "jobs_delayed": "{jobCount, plural, other {# oczekujących}}", - "jobs_failed": "{jobCount, plural, other {# nieudane}}", + "jobs_delayed": "{jobCount, plural, one {# oczekujący} few {# oczekujące} other {# oczekujących}}", + "jobs_failed": "{jobCount, plural, one {# nieudany} few {# nieudane} other {# nieudanych}}", "library_created": "Utworzono bibliotekę: {library}", "library_deleted": "Biblioteka usunięta", "library_import_path_description": "Określ folder do załadowania plikÃŗw. Ten folder, łącznie z podfolderami, zostanie przeskanowany w poszukiwaniu obrazÃŗw i filmÃŗw.", @@ -112,7 +114,7 @@ "library_watching_settings_description": "Automatycznie obserwuj zmienione pliki", "logging_enable_description": "Uruchom zapisywanie logÃŗw", "logging_level_description": "Kiedy włączone, jakiego poziomu uÅŧyć.", - "logging_settings": "Logowanie", + "logging_settings": "Rejestrowanie logÃŗw", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Nazwa modelu CLIP jest wymieniona tutaj. ZwrÃŗÄ‡ uwagę, Åŧe po zmianie modelu musisz ponownie uruchomić zadanie 'Smart Search' dla wszystkich obrazÃŗw.", "machine_learning_duplicate_detection": "Wykrywanie DuplikatÃŗw", @@ -192,26 +194,22 @@ "oauth_auto_register": "Automatyczna rejestracja", "oauth_auto_register_description": "Automatycznie rejestruj nowych uÅŧytkownikÃŗw po zalogowaniu się za pomocą protokołu OAuth", "oauth_button_text": "Tekst na przycisku", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Sekret klienta", + "oauth_client_secret_description": "Wymagane jeÅŧeli PKCE (Proof Key for Code Exchange) nie jest wspierane przez dostawcę OAuth", "oauth_enable_description": "Loguj się za pomocą OAuth", - "oauth_issuer_url": "Adres URL wydawcy", "oauth_mobile_redirect_uri": "Mobilny adres zwrotny", "oauth_mobile_redirect_uri_override": "Zapasowy URI przekierowania mobilnego", "oauth_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu '{callback}'", - "oauth_profile_signing_algorithm": "Algorytm logowania do profilu", - "oauth_profile_signing_algorithm_description": "Algorytm uÅŧywany podczas logowania do profilu uÅŧytkownika.", - "oauth_scope": "Zakres", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", - "oauth_signing_algorithm": "Algorytm podpisywania", "oauth_storage_label_claim": "Roszczenie dotyczące etykiety przechowywania", "oauth_storage_label_claim_description": "Automatycznie ustaw etykietę przechowywania uÅŧytkownika na podaną niÅŧej wartość.", "oauth_storage_quota_claim": "Ilość miejsca w magazynie", "oauth_storage_quota_claim_description": "Automatycznie ustaw ilość miejsca w magazynie na podaną niÅŧej wartość.", "oauth_storage_quota_default": "Domyślna ilość miejsca w magazynie (GiB)", "oauth_storage_quota_default_description": "Limit w GiB do wykorzystania, gdy nie podano Åŧadnej wartości (wpisz 0, aby wyłączyć limit).", + "oauth_timeout": "Upłynął czas Åŧądania", + "oauth_timeout_description": "Limit czasu Åŧądania (w milisekundach)", "offline_paths": "ŚcieÅŧki Offline", "offline_paths_description": "Wyniki te mogą wynikać z ręcznego usunięcia plikÃŗw, ktÃŗre nie są częścią biblioteki zewnętrznej.", "password_enable_description": "Zaloguj uÅŧywając e-mail i hasła", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "Liczba dni po usunięciu, po ktÃŗrej następuje trwałe usunięcie konta uÅŧytkownika i zasobÃŗw. Zadanie usuwania uÅŧytkownikÃŗw jest uruchamiane o pÃŗÅ‚nocy w celu sprawdzenia, czy uÅŧytkownicy są gotowi do usunięcia. Zmiany tego ustawienia zostaną sprawdzone przy następnym wykonaniu.", "user_delete_immediately": "Konto {user} i powiązane zasoby zostaną zakolejkowane do natychmiastowego usunięcia.", "user_delete_immediately_checkbox": "Umieść uÅŧytkownika i zasoby w kolejce do natychmiastowego usunięcia", + "user_details": "SzczegÃŗÅ‚y UÅŧytkownika", "user_management": "Zarządzenie UÅŧytkownikami", "user_password_has_been_reset": "Hasło uÅŧytkownika zostało zresetowane:", "user_password_reset_description": "Proszę przekazać tymczasowe hasło uÅŧytkownikowi i poinformuj o konieczności jego zmiany przy najbliÅŧszym logowaniu.", @@ -371,13 +370,17 @@ "admin_password": "Hasło Administratora", "administration": "Administracja", "advanced": "Zaawansowane", - "advanced_settings_log_level_title": "Poziom dziennika: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "UÅŧyj tej opcji do filtrowania mediÃŗw podczas synchronizacji alternatywnych kryteriÃŗw. UÅŧywaj tylko wtedy gdy aplikacja ma problemy z wykrywaniem wszystkich albumÃŗw.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERYMENTALNE] UÅŧyj alternatywnego filtra synchronizacji albumu", + "advanced_settings_log_level_title": "Poziom szczegÃŗÅ‚owości dziennika: {level}", "advanced_settings_prefer_remote_subtitle": "NiektÃŗre urządzenia bardzo wolno ładują miniatury z zasobÃŗw na urządzeniu. Aktywuj to ustawienie, aby ładować zdalne obrazy.", "advanced_settings_prefer_remote_title": "Preferuj obrazy zdalne", "advanced_settings_proxy_headers_subtitle": "Zdefiniuj nagÅ‚Ãŗwki proxy, ktÃŗre Immich powinien wysyłać z kaÅŧdym Åŧądaniem sieciowym", "advanced_settings_proxy_headers_title": "NagÅ‚Ãŗwki proxy", "advanced_settings_self_signed_ssl_subtitle": "Pomija weryfikację certyfikatu SSL dla punktu końcowego serwera. Wymagane w przypadku certyfikatÃŗw z podpisem własnym.", "advanced_settings_self_signed_ssl_title": "Zezwalaj na certyfikaty SSL z podpisem własnym", + "advanced_settings_sync_remote_deletions_subtitle": "Automatycznie usuń lub przywrÃŗÄ‡ zasÃŗb na tym urządzeniu po wykonaniu tej czynności w interfejsie webowym", + "advanced_settings_sync_remote_deletions_title": "Synchronizuj zdalne usunięcia [EKSPERYMENTALNE]", "advanced_settings_tile_subtitle": "Zaawansowane ustawienia uÅŧytkownika", "advanced_settings_troubleshooting_subtitle": "Włącz dodatkowe funkcje rozwiązywania problemÃŗw", "advanced_settings_troubleshooting_title": "Rozwiązywanie problemÃŗw", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Na pewno chcesz usunąć {user}?", "album_share_no_users": "Wygląda na to, Åŧe ten album albo udostępniono wszystkim uÅŧytkownikom, albo nie ma komu go udostępnić.", "album_thumbnail_card_item": "1 pozycja", - "album_thumbnail_card_items": "{} pozycje", - "album_thumbnail_card_shared": "Udostępniony", - "album_thumbnail_shared_by": "Udostępnione przez {}", + "album_thumbnail_card_items": "{count} plikÃŗw", + "album_thumbnail_card_shared": " ¡ Udostępniony", + "album_thumbnail_shared_by": "Udostępnione przez {user}", "album_updated": "Album zaktualizowany", "album_updated_setting_description": "Otrzymaj powiadomienie e-mail, gdy do udostępnionego Ci albumu zostaną dodane nowe zasoby", "album_user_left": "Opuszczono {album}", @@ -440,7 +443,7 @@ "archive": "Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasÃŗb z archiwum", "archive_page_no_archived_assets": "Nie znaleziono zarchiwizowanych zasobÃŗw", - "archive_page_title": "Archiwum ({})", + "archive_page_title": "Archiwum {count}", "archive_size": "Rozmiar archiwum", "archive_size_description": "Podziel pobierane pliki na więcej niÅŧ jedno archiwum, jeÅŧeli rozmiar archiwum przekroczy tę wartość w GiB", "archived": "Zarchiwizowane", @@ -473,22 +476,22 @@ "asset_viewer_settings_subtitle": "Zarządzaj ustawieniami przeglądarki galerii", "asset_viewer_settings_title": "Przeglądarka zasobÃŗw", "assets": "Zasoby", - "assets_added_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_added_to_album_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}} do albumu", - "assets_added_to_name_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}} do {hasName, select, true {{name}} other {new album}}", - "assets_count": "{count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_deleted_permanently": "{} zasoby trwale usunięto", - "assets_deleted_permanently_from_server": " {} zasoby zostały trwale usunięte z serwera Immich", - "assets_moved_to_trash_count": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}} do kosza", - "assets_permanently_deleted_count": "Trwale usunięto {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_removed_count": "Usunięto {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_removed_permanently_from_device": " {} zasoby zostały trwale usunięte z Twojego urządzenia", + "assets_added_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_added_to_album_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do albumu", + "assets_added_to_name_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do {hasName, select, true {{name}} other {new album}}", + "assets_count": "{count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_deleted_permanently": "{count, plural, one {# zasÃŗb został trwale usunięty} few {# zasoby zostały trwale usunięte} other {# zasobÃŗw zostało trwale usuniętych}}", + "assets_deleted_permanently_from_server": "{count, plural, one {# zasÃŗb został trwale usunięty} few {# zasoby zostały trwale usunięte} other {# zasobÃŗw zostało trwale usuniętych}} z serwera Immich", + "assets_moved_to_trash_count": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do kosza", + "assets_permanently_deleted_count": "Trwale usunięto {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_removed_count": "Usunięto {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_removed_permanently_from_device": "{count, plural, one {# zasÃŗb został trwale usunięty} few {# zasoby zostały trwale usunięte} other {# zasobÃŗw zostało trwale usuniętych}} z Twojego urządzenia", "assets_restore_confirmation": "Na pewno chcesz przywrÃŗcić wszystkie zasoby z kosza? Nie da się tego cofnąć! NaleÅŧy pamiętać, Åŧe w ten sposÃŗb nie moÅŧna przywrÃŗcić zasobÃŗw offline.", - "assets_restored_count": "PrzywrÃŗcono {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_restored_successfully": " {} zasoby pomyślnie przywrÃŗcono", - "assets_trashed": "{} zasoby zostały usunięte", - "assets_trashed_count": "Wrzucono do kosza {count, plural, one {# zasÃŗb} few {# zasoby} many {# zasobÃŗw} other {# zasobÃŗw}}", - "assets_trashed_from_server": "{} zasoby usunięte z serwera Immich", + "assets_restored_count": "PrzywrÃŗcono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_restored_successfully": "{count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} pomyślnie przywrÃŗcono", + "assets_trashed": "{count, plural, one {# zasÃŗb został wrzucony} few {# zasoby zostały wrzucone} other {# zasobÃŗw zostało wrzucone}} do kosza", + "assets_trashed_count": "Wrzucono do kosza {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "assets_trashed_from_server": "{count, plural, one {# zasÃŗb usunięty} few {# zasoby usunięte} other {# zasobÃŗw usuniętych}} z serwera Immich", "assets_were_part_of_album_count": "{count, plural, one {ZasÃŗb był} few {Zasoby były} many {ZasobÃŗw było} other {ZasobÃŗw było}} juÅŧ częścią albumu", "authorized_devices": "UpowaÅŧnione Urządzenia", "automatic_endpoint_switching_subtitle": "Połącz się lokalnie przez wyznaczoną sieć Wi-Fi, jeśli jest dostępna, i korzystaj z alternatywnych połączeń gdzie indziej", @@ -497,46 +500,44 @@ "back_close_deselect": "WrÃŗÄ‡, zamknij lub odznacz", "background_location_permission": "Uprawnienia do lokalizacji w tle", "background_location_permission_content": "Aby mÃŗc przełączać sieć podczas pracy w tle, Immich musi *zawsze* mieć dostęp do dokładnej lokalizacji, aby aplikacja mogła odczytać nazwę sieci Wi-Fi", - "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({})", + "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({count})", "backup_album_selection_page_albums_tap": "Stuknij, aby włączyć, stuknij dwukrotnie, aby wykluczyć", "backup_album_selection_page_assets_scatter": "Pliki mogą być rozproszone w wielu albumach. Dzięki temu albumy mogą być włączane lub wyłączane podczas procesu tworzenia kopii zapasowej.", "backup_album_selection_page_select_albums": "Zaznacz albumy", "backup_album_selection_page_selection_info": "Info o wyborze", "backup_album_selection_page_total_assets": "Łącznie unikalnych plikÃŗw", "backup_all": "Wszystkie", - "backup_background_service_backup_failed_message": "Nie udało się wykonać kopii zapasowej zasobÃŗw. PrÃŗbuję ponownie...", - "backup_background_service_connection_failed_message": "Nie udało się połączyć z serwerem. PrÃŗbuję ponownie...", - "backup_background_service_current_upload_notification": "Wysyłanie {}", - "backup_background_service_default_notification": "Sprawdzanie nowych zasobÃŗw...", + "backup_background_service_backup_failed_message": "Nie udało się wykonać kopii zapasowej zasobÃŗw. Ponowna prÃŗbaâ€Ļ", + "backup_background_service_connection_failed_message": "Nie udało się połączyć z serwerem. Ponowna prÃŗbaâ€Ļ", + "backup_background_service_current_upload_notification": "Wysyłanie {filename}", + "backup_background_service_default_notification": "Sprawdzanie nowych zasobÃŗwâ€Ļ", "backup_background_service_error_title": "Błąd kopii zapasowej", - "backup_background_service_in_progress_notification": "Tworzę kopię twoich zasobÃŗw...", - "backup_background_service_upload_failure_notification": "Nie udało się przesłać {}", - "backup_controller_page_albums": "Backup AlbumÃŗw", + "backup_background_service_in_progress_notification": "Tworzenie kopii zapasowej twoich zasobÃŗwâ€Ļ", + "backup_background_service_upload_failure_notification": "Błąd przesyłania {filename}", + "backup_controller_page_albums": "Kopia Zapasowa albumÃŗw", "backup_controller_page_background_app_refresh_disabled_content": "Włącz odświeÅŧanie aplikacji w tle w Ustawienia > OgÃŗlne > OdświeÅŧanie aplikacji w tle, aby mÃŗc korzystać z kopii zapasowej w tle.", "backup_controller_page_background_app_refresh_disabled_title": "OdświeÅŧanie aplikacji w tle wyłączone", "backup_controller_page_background_app_refresh_enable_button_text": "PrzejdÅē do ustawień", "backup_controller_page_background_battery_info_link": "PokaÅŧ mi jak", "backup_controller_page_background_battery_info_message": "Aby uzyskać najlepsze rezultaty podczas tworzenia kopii zapasowej w tle, naleÅŧy wyłączyć wszelkie optymalizacje baterii ograniczające aktywność w tle dla Immich w urządzeniu.\n\nPoniewaÅŧ jest to zaleÅŧne od urządzenia, proszę sprawdzić wymagane informacje dla producenta urządzenia.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optymalizacja Baterii", "backup_controller_page_background_charging": "Tylko podczas ładowania", "backup_controller_page_background_configure_error": "Nie udało się skonfigurować usługi w tle", - "backup_controller_page_background_delay": "OpÃŗÅēnij tworzenie kopii zapasowych nowych zasobÃŗw: {}", + "backup_controller_page_background_delay": "OpÃŗÅēnienie tworzenia kopii zapasowych nowych zasobÃŗw: {duration}", "backup_controller_page_background_description": "Włącz usługę w tle, aby automatycznie tworzyć kopie zapasowe wszelkich nowych zasobÃŗw bez konieczności otwierania aplikacji", "backup_controller_page_background_is_off": "Automatyczna kopia zapasowa w tle jest wyłączona", "backup_controller_page_background_is_on": "Automatyczna kopia zapasowa w tle jest włączona", "backup_controller_page_background_turn_off": "Wyłącz usługę w tle", "backup_controller_page_background_turn_on": "Włącz usługę w tle", - "backup_controller_page_background_wifi": "Tylko na WiFi", + "backup_controller_page_background_wifi": "Tylko Wi-Fi", "backup_controller_page_backup": "Kopia Zapasowa", "backup_controller_page_backup_selected": "Zaznaczone: ", - "backup_controller_page_backup_sub": "Tworzenie kopii zapasowych zdjęć i filmÃŗw", - "backup_controller_page_created": "Utworzony na: {}", + "backup_controller_page_backup_sub": "Skopiowane zdjęcia oraz filmy", + "backup_controller_page_created": "Utworzono dnia: {date}", "backup_controller_page_desc_backup": "Włącz kopię zapasową, aby automatycznie przesyłać nowe zasoby na serwer.", "backup_controller_page_excluded": "Wykluczone: ", - "backup_controller_page_failed": "Nieudane ({})", - "backup_controller_page_filename": "Nazwa pliku: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Nieudane ({count})", + "backup_controller_page_filename": "Nazwa pliku: {filename} [{size}]", "backup_controller_page_info": "Informacje o kopii zapasowej", "backup_controller_page_none_selected": "Brak wybranych", "backup_controller_page_remainder": "Reszta", @@ -545,13 +546,13 @@ "backup_controller_page_start_backup": "Rozpocznij Kopię Zapasową", "backup_controller_page_status_off": "Kopia Zapasowa jest wyłaczona", "backup_controller_page_status_on": "Kopia Zapasowa jest włączona", - "backup_controller_page_storage_format": "{} z {} wykorzystanych", - "backup_controller_page_to_backup": "Albumy do backupu", + "backup_controller_page_storage_format": "{used} z {total} wykorzystanych", + "backup_controller_page_to_backup": "Albumy z Kopią Zapasową", "backup_controller_page_total_sub": "Wszystkie unikalne zdjęcia i filmy z wybranych albumÃŗw", "backup_controller_page_turn_off": "Wyłącz Kopię Zapasową", "backup_controller_page_turn_on": "Włącz Kopię Zapasową", "backup_controller_page_uploading_file_info": "Przesyłanie informacji o pliku", - "backup_err_only_album": "Nie moÅŧna usunąć tylko i wyłącznie albumu", + "backup_err_only_album": "Nie moÅŧna usunąć jedynego albumu", "backup_info_card_assets": "zasoby", "backup_manual_cancelled": "Anulowano", "backup_manual_in_progress": "Przesyłanie juÅŧ trwa. SprÃŗbuj po pewnym czasie", @@ -560,31 +561,35 @@ "backup_options_page_title": "Opcje kopi zapasowej", "backup_setting_subtitle": "Zarządzaj ustawieniami przesyłania w tle i na pierwszym planie", "backward": "Do tyłu", + "biometric_auth_enabled": "Włączono logowanie biometryczne", + "biometric_locked_out": "Uwierzytelnianie biometryczne jest dla Ciebie zablokowane", + "biometric_no_options": "Brak moÅŧliwości biometrii", + "biometric_not_available": "Logowanie biometryczne nie jest dostępne na tym urządzeniu", "birthdate_saved": "Data urodzenia zapisana pomyślnie", "birthdate_set_description": "Data urodzenia jest uÅŧywana do obliczenia wieku danej osoby podczas wykonania zdjęcia.", "blurred_background": "Rozmyte tło", "bugs_and_feature_requests": "Błędy i prośby o funkcje", "build": "Kompilacja", "build_image": "Obraz Buildu", - "bulk_delete_duplicates_confirmation": "Czy na pewno chcesz trwale usunąć {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} many {# zduplikowanych zasobÃŗw} other {# zduplikowanych zasobÃŗw}}? Zostanie zachowany największy zasÃŗb z kaÅŧdej grupy, a wszystkie pozostałe duplikaty zostaną trwale usunięte. Nie moÅŧna cofnąć tej operacji!", - "bulk_keep_duplicates_confirmation": "Czy na pewno chcesz zachować {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} many {# zduplikowanych zasobÃŗw} other {# zduplikowanych zasobÃŗw}}? To spowoduje rozwiązanie wszystkich grup duplikatÃŗw bez usuwania czegokolwiek.", - "bulk_trash_duplicates_confirmation": "Czy na pewno chcesz wrzucić do kosza {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} many {# zduplikowanych zasobÃŗw} other {# zduplikowanych zasobÃŗw}}? Zostanie zachowany największy zasÃŗb z kaÅŧdej grupy, a wszystkie pozostałe duplikaty zostaną wrzucone do kosza.", + "bulk_delete_duplicates_confirmation": "Czy na pewno chcesz trwale usunąć {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} other {# zduplikowanych zasobÃŗw}}? Zostanie zachowany największy zasÃŗb z kaÅŧdej grupy, a wszystkie pozostałe duplikaty zostaną trwale usunięte. Nie moÅŧna cofnąć tej operacji!", + "bulk_keep_duplicates_confirmation": "Czy na pewno chcesz zachować {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} other {# zduplikowanych zasobÃŗw}}? To spowoduje rozwiązanie wszystkich grup duplikatÃŗw bez usuwania czegokolwiek.", + "bulk_trash_duplicates_confirmation": "Czy na pewno chcesz wrzucić do kosza {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} other {# zduplikowanych zasobÃŗw}}? Zostanie zachowany największy zasÃŗb z kaÅŧdej grupy, a wszystkie pozostałe duplikaty zostaną wrzucone do kosza.", "buy": "Kup Immich", - "cache_settings_album_thumbnails": "Miniatury stron bibliotek ({} zasobÃŗw)", + "cache_settings_album_thumbnails": "Miniatury na stronie biblioteki ({count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}})", "cache_settings_clear_cache_button": "Wyczyść Cache", "cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopÃŗki pamięć podręczna nie zostanie odbudowana.", "cache_settings_duplicated_assets_clear_button": "WYCZYŚĆ", "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na czarnej liście aplikacji", - "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({})", - "cache_settings_image_cache_size": "Rozmiar pamięci podręcznej obrazÃŗw ({} zasobÃŗw)", + "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({count})", + "cache_settings_image_cache_size": "Rozmiar pamięci podręcznej obrazÃŗw ({count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}})", "cache_settings_statistics_album": "Biblioteka miniatur", - "cache_settings_statistics_assets": "{} zasoby ({})", + "cache_settings_statistics_assets": "{count} zasoby ({size})", "cache_settings_statistics_full": "Pełne Zdjęcia", "cache_settings_statistics_shared": "Udostępnione miniatury albumÃŗw", "cache_settings_statistics_thumbnail": "Miniatury", "cache_settings_statistics_title": "UÅŧycie Cache", "cache_settings_subtitle": "Kontrolowanie zachowania buforowania aplikacji mobilnej Immich", - "cache_settings_thumbnail_size": "Rozmiar pamięci podręcznej miniatur ({} zasobÃŗw)", + "cache_settings_thumbnail_size": "Rozmiar pamięci podręcznej miniatur ({count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}})", "cache_settings_tile_subtitle": "Kontroluj zachowanie lokalnego magazynu", "cache_settings_tile_title": "Lokalny magazyn", "cache_settings_title": "Ustawienia Buforowania", @@ -597,7 +602,9 @@ "cannot_merge_people": "Złączenie osÃŗb nie powiodło się", "cannot_undo_this_action": "Nie da się tego cofnąć!", "cannot_update_the_description": "Nie moÅŧna zaktualizować opisu", + "cast": "Obsada", "change_date": "Zmień datę", + "change_description": "Zmiana opisu", "change_display_order": "Zmień kolejność wyświetlania", "change_expiration_time": "Zmień czas waÅŧności", "change_location": "Zmień lokalizację", @@ -610,6 +617,7 @@ "change_password_form_new_password": "Nowe Hasło", "change_password_form_password_mismatch": "Hasła nie są zgodne", "change_password_form_reenter_new_password": "WprowadÅē ponownie Nowe Hasło", + "change_pin_code": "Zmień kod PIN", "change_your_password": "Zmień swoje hasło", "changed_visibility_successfully": "Pomyślnie zmieniono widoczność", "check_all": "Zaznacz wszystko", @@ -624,7 +632,6 @@ "clear_all_recent_searches": "Usuń ostatnio wyszukiwane", "clear_message": "Zamknij wiadomość", "clear_value": "Wyczyść wartość", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "WprowadÅē hasło", "client_cert_import": "Importuj", "client_cert_import_success_msg": "Certyfikat klienta został zaimportowany", @@ -650,17 +657,19 @@ "confirm_delete_face": "Czy na pewno chcesz usunąć twarz {name} z zasobÃŗw?", "confirm_delete_shared_link": "Czy na pewno chcesz usunąć ten udostępniony link?", "confirm_keep_this_delete_others": "Wszystkie inne zasoby zostaną usunięte poza tym zasobem. Czy jesteś pewien, Åŧe chcesz kontynuować?", + "confirm_new_pin_code": "PotwierdÅē nowy kod PIN", "confirm_password": "PotwierdÅē hasło", + "connected_to": "Połączony z", "contain": "Zawiera", "context": "Kontekst", "continue": "Kontynuuj", - "control_bottom_app_bar_album_info_shared": "{} pozycji ¡ Udostępnionych", + "control_bottom_app_bar_album_info_shared": "{count, plural, one {# element ¡ Udostępniony} few {# elementy ¡ Udostępnione} other {# elementÃŗw ¡ Udostępnionych}}", "control_bottom_app_bar_create_new_album": "UtwÃŗrz nowy album", "control_bottom_app_bar_delete_from_immich": "Usuń z Immicha", "control_bottom_app_bar_delete_from_local": "Usuń z urządzenia", "control_bottom_app_bar_edit_location": "Edytuj lokalizację", "control_bottom_app_bar_edit_time": "Edytuj datę i godzinę", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Udostępnij link", "control_bottom_app_bar_share_to": "Wyślij", "control_bottom_app_bar_trash_from_immich": "Przenieść do kosza", "copied_image_to_clipboard": "Skopiowano obraz do schowka.", @@ -692,19 +701,21 @@ "create_tag_description": "StwÃŗrz nową etykietę. Dla etykiet zagnieÅŧdÅŧonych, wprowadÅē pełną ścieÅŧkę etykiety zawierającą ukośniki.", "create_user": "StwÃŗrz uÅŧytkownika", "created": "Utworzono", + "created_at": "Utworzony", "crop": "Przytnij", "curated_object_page_title": "Rzeczy", "current_device": "Obecne urządzenie", + "current_pin_code": "Aktualny kod PIN", "current_server_address": "Aktualny adres serwera", "custom_locale": "Niestandardowy Region", "custom_locale_description": "Formatuj daty i liczby na podstawie języka i regionu", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "E, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "E d. LLL y â€ĸ hh:mm", "date_of_birth_saved": "Data urodzenia zapisana pomyślnie", "date_range": "Zakres dat", "day": "Dzień", @@ -746,7 +757,6 @@ "direction": "Kierunek", "disabled": "Wyłączone", "disallow_edits": "Nie pozwalaj edytować", - "discord": "Discord", "discover": "Odkryj", "dismiss_all_errors": "Odrzuć wszystkie błędy", "dismiss_error": "Odrzuć błąd", @@ -763,7 +773,7 @@ "download_enqueue": "Pobieranie w kolejce", "download_error": "Błąd pobierania", "download_failed": "Pobieranie nieudane", - "download_filename": "plik: {}", + "download_filename": "plik: {filename}", "download_finished": "Pobieranie zakończone", "download_include_embedded_motion_videos": "Pobierz filmy ruchomych zdjęć", "download_include_embedded_motion_videos_description": "Dołącz filmy osadzone w ruchomych zdjęciach jako oddzielny plik", @@ -787,6 +797,8 @@ "edit_avatar": "Edytuj awatar", "edit_date": "Edytuj datę", "edit_date_and_time": "Edytuj datę i czas", + "edit_description": "Edycja opisu", + "edit_description_prompt": "Wybierz nowy opis:", "edit_exclusion_pattern": "Edytuj wzÃŗr wykluczający", "edit_faces": "Edytuj twarze", "edit_import_path": "Edytuj ścieÅŧkę importu", @@ -807,19 +819,23 @@ "editor_crop_tool_h2_aspect_ratios": "Proporcje obrazu", "editor_crop_tool_h2_rotation": "ObrÃŗt", "email": "E-mail", - "empty_folder": "This folder is empty", + "email_notifications": "Powiadomienia e-mail", + "empty_folder": "Ten folder jest pusty", "empty_trash": "OprÃŗÅŧnij kosz", "empty_trash_confirmation": "Czy na pewno chcesz oprÃŗÅŧnić kosz? Spowoduje to trwałe usunięcie wszystkich zasobÃŗw znajdujących się w koszu z Immich.\nNie moÅŧna cofnąć tej operacji!", "enable": "Włącz", + "enable_biometric_auth_description": "WprowadÅē kod PIN aby włączyć logowanie biometryczne", "enabled": "Włączone", "end_date": "Do dnia", "enqueued": "Kolejka", - "enter_wifi_name": "WprowadÅē nazwę Wi-Fi", + "enter_wifi_name": "WprowadÅē nazwę punktu dostępu Wi-Fi", + "enter_your_pin_code": "Wpisz swÃŗj kod PIN", + "enter_your_pin_code_subtitle": "WprowadÅē twÃŗj kod PIN, aby uzyskać dostęp do folderu zablokowanego", "error": "Błąd", "error_change_sort_album": "Nie udało się zmienić kolejności sortowania albumÃŗw", "error_delete_face": "Wystąpił błąd podczas usuwania twarzy z zasobÃŗw", "error_loading_image": "Błąd podczas ładowania zdjęcia", - "error_saving_image": "Błąd: {}", + "error_saving_image": "Błąd: {error}", "error_title": "Błąd - Coś poszło nie tak", "errors": { "cannot_navigate_next_asset": "Nie moÅŧna przejść do następnego zasobu", @@ -849,10 +865,12 @@ "failed_to_keep_this_delete_others": "Nie udało się zachować tego zasobu i usunąć innych zasobÃŗw", "failed_to_load_asset": "Nie udało się załadować zasobu", "failed_to_load_assets": "Nie udało się załadować zasobÃŗw", + "failed_to_load_notifications": "Błąd ładowania powiadomień", "failed_to_load_people": "Błąd pobierania ludzi", "failed_to_remove_product_key": "Nie udało się usunąć klucza produktu", "failed_to_stack_assets": "Nie udało się zestawić zasobÃŗw", "failed_to_unstack_assets": "Nie udało się rozdzielić zasobÃŗw", + "failed_to_update_notification_status": "Błąd aktualizacji statusu powiadomienia", "import_path_already_exists": "Ta ścieÅŧka importu juÅŧ istnieje.", "incorrect_email_or_password": "Nieprawidłowy e-mail lub hasło", "paths_validation_failed": "{paths, plural, one {# ścieÅŧka} few {# ścieÅŧki} other {# ścieÅŧek}}", @@ -870,6 +888,7 @@ "unable_to_archive_unarchive": "Nie moÅŧna {archived, select, true {zarchwizować} other {odarchiwizować}}", "unable_to_change_album_user_role": "Nie moÅŧna zmienić roli uÅŧytkownika albumu", "unable_to_change_date": "Nie moÅŧna zmienić daty", + "unable_to_change_description": "Nie udało się zmienić opisu", "unable_to_change_favorite": "Nie moÅŧna zmienić ulubionego zasobu", "unable_to_change_location": "Nie moÅŧna zmienić lokalizacji", "unable_to_change_password": "Nie moÅŧna zmienić hasła", @@ -907,6 +926,7 @@ "unable_to_log_out_all_devices": "Nie moÅŧna wylogować wszystkich urządzeń", "unable_to_log_out_device": "Nie moÅŧna wylogować się z urządzenia", "unable_to_login_with_oauth": "Nie moÅŧna zalogować się za pomocą OAuth", + "unable_to_move_to_locked_folder": "Nie moÅŧna przenieść do folderu zablokowanego", "unable_to_play_video": "Odtwarzanie filmu nie powiodło się", "unable_to_reassign_assets_existing_person": "Nie moÅŧna ponownie przypisać zasobÃŗw do {name,select, null {istniejącej osoby} other {{name}}}", "unable_to_reassign_assets_new_person": "Nie moÅŧna ponownie przypisać zasobÃŗw nowej osobie", @@ -919,7 +939,8 @@ "unable_to_remove_partner": "Nie moÅŧna usunąć partnerÃŗw", "unable_to_remove_reaction": "Usunięcie reakcji nie powiodło się", "unable_to_repair_items": "Naprawianie elementÃŗw nie powiodło się", - "unable_to_reset_password": "Nie moÅŧna resetować hasła", + "unable_to_reset_password": "Zresetowanie hasła nie powiodło się", + "unable_to_reset_pin_code": "Zresetowanie kodu PIN nie powiodło się", "unable_to_resolve_duplicate": "Usuwanie duplikatÃŗw nie powiodło się", "unable_to_restore_assets": "Przywracanie zasobÃŗw nie powiodło się", "unable_to_restore_trash": "Przywracanie zasobÃŗw z kosza nie powiodło się", @@ -953,10 +974,10 @@ "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", "exif_bottom_sheet_person_add_person": "Dodaj nazwę", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Wiek {age}", + "exif_bottom_sheet_person_age_months": "Wiek {months, plural, one {# miesiąc} few {# miesiące} other {# miesięcy}}", + "exif_bottom_sheet_person_age_year_months": "Wiek 1 rok, {months, plural, one {# miesiąc} few {# miesiące} other {# miesięcy}}", + "exif_bottom_sheet_person_age_years": "Wiek {years, plural, few {# lata} other {# lat}}", "exit_slideshow": "Zamknij Pokaz SlajdÃŗw", "expand_all": "Rozwiń wszystko", "experimental_settings_new_asset_list_subtitle": "Praca w toku", @@ -974,16 +995,17 @@ "external": "Zewnętrzny", "external_libraries": "Biblioteki Zewnętrzne", "external_network": "Sieć zewnętrzna", - "external_network_sheet_info": "Jeśli nie korzystasz z preferowanej sieci Wi-Fi, aplikacja połączy się z serwerem za pośrednictwem pierwszego z poniÅŧszych adresÃŗw URL, do ktÃŗrego moÅŧe dotrzeć, zaczynając od gÃŗry do dołu", + "external_network_sheet_info": "Jeśli nie korzystasz z preferowanej sieci Wi-Fi aplikacja połączy się z serwerem za pośrednictwem pierwszego z dostępnych poniÅŧej adresÃŗw URL, zaczynając od gÃŗry do dołu", "face_unassigned": "Nieprzypisany", "failed": "Niepowodzenie", + "failed_to_authenticate": "Nie udało się uwierzytelnić", "failed_to_load_assets": "Nie udało się załadować zasobÃŗw", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Nie udało się załadować folderu", "favorite": "Ulubione", "favorite_or_unfavorite_photo": "Dodaj lub usuń z ulubionych", "favorites": "Ulubione", "favorites_page_no_favorites": "Nie znaleziono ulubionych zasobÃŗw", - "feature_photo_updated": "Pomyślnie zmieniono gÅ‚Ãŗwne zdjęcie", + "feature_photo_updated": "Zdjęcie gÅ‚Ãŗwne zaktualizowane pomyślnie", "features": "Funkcje", "features_setting_description": "Zarządzaj funkcjami aplikacji", "file_name": "Nazwa pliku", @@ -992,10 +1014,10 @@ "filetype": "Typ pliku", "filter": "Filtr", "filter_people": "Szukaj osoby", + "filter_places": "Filtruj miejsca", "find_them_fast": "Wyszukuj szybciej przypisując nazwę", "fix_incorrect_match": "Napraw nieprawidłowe dopasowanie", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder_not_found": "Nie znaleziono folderu", "folders": "Foldery", "folders_feature_description": "Przeglądanie zdjęć i filmÃŗw w widoku folderÃŗw", "forward": "Do przodu", @@ -1037,15 +1059,16 @@ "home_page_archive_err_partner": "Nie moÅŧna zarchiwizować zasobÃŗw partnera, pomijam", "home_page_building_timeline": "Budowanie osi czasu", "home_page_delete_err_partner": "Nie moÅŧna usunąć zasobÃŗw partnera, pomijam", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Nie moÅŧna dodać do ulubionych lokalnych zasobÃŗw, pomijam", + "home_page_delete_remote_err_local": "Lokalne zasoby w wyborze do zdalnego usunięcia, pomijam", + "home_page_favorite_err_local": "Nie moÅŧna jeszcze dodać do ulubionych lokalnych zasobÃŗw, pomijam", "home_page_favorite_err_partner": "Nie moÅŧna jeszcze dodać do ulubionych zasobÃŗw partnera, pomijam", - "home_page_first_time_notice": "Jeśli korzystasz z aplikacji po raz pierwszy, pamiętaj o wybraniu albumÃŗw zapasowych, aby oś czasu mogła zapełnić zdjęcia i filmy w albumach.", - "home_page_share_err_local": "Nie moÅŧna udostępniać zasobÃŗw lokalnych za pośrednictwem linku, pomijajam", + "home_page_first_time_notice": "Jeśli korzystasz z aplikacji po raz pierwszy, pamiętaj o wybraniu albumÃŗw do kopii zapasowej, aby oś czasu mogła wypełnić się zdjęciami i filmami", + "home_page_locked_error_local": "Nie moÅŧna przenieść zasobÃŗw lokalnych do folderu zablokowanego, pomijam", + "home_page_locked_error_partner": "Nie moÅŧna przenieść zasobÃŗw partnera do folderu zablokowanego, pomijam", + "home_page_share_err_local": "Nie moÅŧna udostępnić zasobÃŗw lokalnych za pośrednictwem linku, pomijam", "home_page_upload_err_limit": "MoÅŧna przesłać maksymalnie 30 zasobÃŗw jednocześnie, pomijanie", - "host": "Host", "hour": "Godzina", - "ignore_icloud_photos": "Ignoruj ​​zdjęcia w iCloud", + "ignore_icloud_photos": "Ignoruj zdjęcia w iCloud", "ignore_icloud_photos_description": "Zdjęcia przechowywane w usłudze iCloud nie zostaną przesłane na serwer Immich", "image": "Zdjęcie", "image_alt_text_date": "{isVideo, select, true {Wideo} other {Zdjęcie}} zrobione dnia {date}", @@ -1120,17 +1143,19 @@ "local_network": "Sieć lokalna", "local_network_sheet_info": "Aplikacja połączy się z serwerem za pośrednictwem tego adresu URL podczas korzystania z określonej sieci Wi-Fi", "location_permission": "Zezwolenie na lokalizację", - "location_permission_content": "Aby mÃŗc korzystać z funkcji automatycznego przełączania, Immich potrzebuje precyzyjnego pozwolenia na lokalizację, aby mÃŗc odczytać nazwę bieÅŧącej sieci WiFi", + "location_permission_content": "Aby mÃŗc korzystać z funkcji automatycznego przełączania, Immich potrzebuje uprawnienia do dokładnej lokalizacji, aby mÃŗc odczytać nazwę bieÅŧącej sieci Wi-Fi", "location_picker_choose_on_map": "Wybierz na mapie", "location_picker_latitude_error": "WprowadÅē prawidłową szerokość geograficzną", "location_picker_latitude_hint": "Wpisz tutaj swoją szerokość geograficzną", "location_picker_longitude_error": "WprowadÅē prawidłową długość geograficzną", "location_picker_longitude_hint": "Wpisz tutaj swoją długość geograficzną", + "lock": "Zablokuj", + "locked_folder": "Folder zablokowany", "log_out": "Wyloguj", "log_out_all_devices": "Wyloguj ze Wszystkich Urządzeń", "logged_out_all_devices": "Wylogowano ze wszystkich urządzeń", "logged_out_device": "Wylogowany z urządzenia", - "login": "Login", + "login": "Logowanie", "login_disabled": "Logowanie zostało wyłączone", "login_form_api_exception": "Wyjątek API. SprawdÅē adres URL serwera i sprÃŗbuj ponownie.", "login_form_back_button_text": "Cofnij", @@ -1144,7 +1169,7 @@ "login_form_err_trailing_whitespace": "Białe znaki po przecinku", "login_form_failed_get_oauth_server_config": "Błąd logowania przy uÅŧyciu OAuth. SprawdÅē adres URL serwera", "login_form_failed_get_oauth_server_disable": "Funkcja OAuth nie jest dostępna na tym serwerze", - "login_form_failed_login": "Błąd logowania, sprawdÅē adres url serwera, email i hasło.", + "login_form_failed_login": "Błąd logowania, sprawdÅē adres url serwera, email i hasło", "login_form_handshake_exception": "Wystąpił wyjątek uzgadniania z serwerem. Włącz obsługę certyfikatÃŗw z podpisem własnym w ustawieniach, jeśli uÅŧywasz certyfikatu z podpisem własnym.", "login_form_password_hint": "hasło", "login_form_save_login": "Pozostań zalogowany", @@ -1170,8 +1195,8 @@ "manage_your_devices": "Zarządzaj swoimi zalogowanymi urządzeniami", "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", - "map_assets_in_bound": "{} zdjęć", - "map_assets_in_bounds": "{} zdjęć", + "map_assets_in_bound": "{count} zdjęcie", + "map_assets_in_bounds": "{count, plural, few {# zdjęcia} other {# zdjęć}}", "map_cannot_get_user_location": "Nie moÅŧna uzyskać lokalizacji uÅŧytkownika", "map_location_dialog_yes": "Tak", "map_location_picker_page_use_location": "UÅŧyj tej lokalizacji", @@ -1185,15 +1210,18 @@ "map_settings": "Ustawienia mapy", "map_settings_dark_mode": "Tryb ciemny", "map_settings_date_range_option_day": "Ostatnie 24 godziny", - "map_settings_date_range_option_days": "Ostatnie {} dni", - "map_settings_date_range_option_year": "Poprzedni rok", - "map_settings_date_range_option_years": "Ostatnie {} lat", + "map_settings_date_range_option_days": "Minione {days} dni", + "map_settings_date_range_option_year": "Miniony rok", + "map_settings_date_range_option_years": "Minione {count, plural, few {# lata} other {# lat}}", "map_settings_dialog_title": "Ustawienia mapy", "map_settings_include_show_archived": "Uwzględnij zarchiwizowane", "map_settings_include_show_partners": "Uwzględnij partnerÃŗw", "map_settings_only_show_favorites": "PokaÅŧ tylko ulubione", "map_settings_theme_settings": "Motyw mapy", "map_zoom_to_see_photos": "Pomniejsz, aby zobaczyć zdjęcia", + "mark_all_as_read": "Zaznacz wszystkie jako odczytane", + "mark_as_read": "Zaznacz jako odczytane", + "marked_all_as_read": "Zaznaczono wszystkie jako przeczytane", "matches": "Powiązania", "media_type": "Typ zasobu", "memories": "Wspomnienia", @@ -1202,24 +1230,25 @@ "memories_setting_description": "Zarządzaj wspomnieniami", "memories_start_over": "Zacznij od nowa", "memories_swipe_to_close": "Przesuń w gÃŗrę, aby zamknąć", - "memories_year_ago": "Rok temu", - "memories_years_ago": "{} lat temu", "memory": "Pamięć", "memory_lane_title": "Aleja Wspomnień {title}", - "menu": "Menu", "merge": "Złącz", "merge_people": "Złącz osoby", "merge_people_limit": "MoÅŧesz łączyć maksymalnie 5 twarzy naraz", "merge_people_prompt": "Czy chcesz połączyć te osoby? Ta czynność jest nieodwracalna.", "merge_people_successfully": "Pomyślnie złączono osoby", - "merged_people_count": "Połączono {count, plural, one {# osobę} other {# osÃŗb}}", + "merged_people_count": "Połączono {count, plural, one {# osobę} few {# osoby} other {# osÃŗb}}", "minimize": "Zminimalizuj", "minute": "Minuta", "missing": "Brakujące", - "model": "Model", "month": "Miesiąc", - "monthly_title_text_date_format": "MMMM y", - "more": "Więcej...", + "more": "Więcej", + "move": "Przenieś", + "move_off_locked_folder": "Przenieś z folderu zablokowanego", + "move_to_locked_folder": "Przenieś do folderu zablokowanego", + "move_to_locked_folder_confirmation": "Te zdjęcia i filmy zostaną usunięte ze wszystkich albumÃŗw i będą widzialne tylko w folderze zablokowanym", + "moved_to_archive": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do archiwum", + "moved_to_library": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do biblioteki", "moved_to_trash": "Przeniesiono do kosza", "multiselect_grid_edit_date_time_err_read_only": "Nie moÅŧna edytować daty zasobÃŗw tylko do odczytu, pomijanie", "multiselect_grid_edit_gps_err_read_only": "Nie moÅŧna edytować lokalizacji zasobÃŗw tylko do odczytu, pomijanie", @@ -1228,12 +1257,14 @@ "name": "Nazwa", "name_or_nickname": "Nazwa lub pseudonim", "networking_settings": "Sieć", - "networking_subtitle": "Zarządzaj ustawieniami serwera końcowego ", + "networking_subtitle": "Zarządzaj ustawieniami punktu końcowego serwera", "never": "nigdy", "new_album": "Nowy album", "new_api_key": "Nowy Klucz API", "new_password": "Nowe hasło", "new_person": "Nowa osoba", + "new_pin_code": "Nowy kod PIN", + "new_pin_code_subtitle": "Jest to pierwszy raz, kiedy wchodzisz do folderu zablokowanego. UtwÃŗrz kod PIN, aby bezpiecznie korzystać z tej strony", "new_user_created": "Pomyślnie stworzono nowego uÅŧytkownika", "new_version_available": "NOWA WERSJA DOSTĘPNA", "newest_first": "Od najnowszych", @@ -1251,7 +1282,10 @@ "no_explore_results_message": "Prześlij więcej zdjęć, aby przeglądać swÃŗj zbiÃŗr.", "no_favorites_message": "Dodaj ulubione aby szybko znaleÅēć swoje najlepsze zdjęcia i filmy", "no_libraries_message": "StwÃŗrz bibliotekę zewnętrzną, aby przeglądać swoje zdjęcia i filmy", + "no_locked_photos_message": "Zdjęcia i filmy w folderze zablokowanym są ukryte i nie będą wyświetlane podczas przeglądania biblioteki.", "no_name": "Brak Nazwy", + "no_notifications": "Brak powiadomień", + "no_people_found": "Brak pasujących osÃŗb", "no_places": "Brak miejsc", "no_results": "Brak wynikÃŗw", "no_results_description": "SprÃŗbuj uÅŧyć synonimu lub bardziej ogÃŗlnego słowa kluczowego", @@ -1260,6 +1294,7 @@ "not_selected": "Nie wybrano", "note_apply_storage_label_to_previously_uploaded assets": "Uwaga: Aby przypisać etykietę magazynowania do wcześniej przesłanych zasobÃŗw, uruchom", "notes": "Uwagi", + "nothing_here_yet": "Nic tu jeszcze nie ma", "notification_permission_dialog_content": "Aby włączyć powiadomienia, przejdÅē do Ustawień i wybierz opcję Zezwalaj.", "notification_permission_list_tile_content": "Przyznaj uprawnienia, aby włączyć powiadomienia.", "notification_permission_list_tile_enable_button": "Włącz Powiadomienia", @@ -1267,12 +1302,9 @@ "notification_toggle_setting_description": "Włącz powiadomienia e-mail", "notifications": "Powiadomienia", "notifications_setting_description": "Zarządzanie powiadomieniami", - "oauth": "OAuth", "official_immich_resources": "Oficjalne zasoby Immicha", - "offline": "Offline", "offline_paths": "ŚcieÅŧki offline", "offline_paths_description": "Te wyniki mogą być spowodowane ręcznym usunięciem plikÃŗw, ktÃŗre nie są częścią zewnętrznej biblioteki.", - "ok": "Ok", "oldest_first": "Od najstarszych", "on_this_device": "Na tym urządzeniu", "onboarding": "WdroÅŧenie", @@ -1282,6 +1314,7 @@ "onboarding_welcome_user": "Witaj, {user}", "online": "Połączony", "only_favorites": "Tylko ulubione", + "open": "OtwÃŗrz", "open_in_map_view": "OtwÃŗrz w widoku mapy", "open_in_openstreetmap": "OtwÃŗrz w OpenStreetMap", "open_the_search_filters": "OtwÃŗrz filtry wyszukiwania", @@ -1294,18 +1327,17 @@ "other_variables": "Inne zmienne", "owned": "Posiadany", "owner": "Właściciel", - "partner": "Partner", "partner_can_access": "{partner} ma dostęp do", "partner_can_access_assets": "Twoje wszystkie zdjęcia i filmy, oprÃŗcz tych w Archiwum i Koszu", "partner_can_access_location": "Informacji o tym, gdzie zostały zrobione Twoje zdjęcia", "partner_list_user_photos": "{user} zdjęcia", "partner_list_view_all": "PokaÅŧ wszystkie", - "partner_page_empty_message": "Twoje zdjęcia nie są udostępnione Åŧadnemu partnerowi", + "partner_page_empty_message": "Twoje zdjęcia nie są udostępnione Åŧadnemu partnerowi.", "partner_page_no_more_users": "Brak uÅŧytkownikÃŗw do dodania", "partner_page_partner_add_failed": "Nie udało się dodać partnera", "partner_page_select_partner": "Wybierz partnera", "partner_page_shared_to_title": "Udostępniono", - "partner_page_stop_sharing_content": "{} nie będzie juÅŧ mieć dostępu do twoich zdjęć.", + "partner_page_stop_sharing_content": "{partner} nie będzie juÅŧ mieć dostępu do twoich zdjęć.", "partner_sharing": "Dzielenie z Partnerami", "partners": "Partnerzy", "password": "Hasło", @@ -1351,6 +1383,10 @@ "photos_count": "{count, plural, one {{count, number} Zdjęcie} few {{count, number} Zdjęcia} other {{count, number} Zdjęć}}", "photos_from_previous_years": "Zdjęcia z ubiegłych lat", "pick_a_location": "Oznacz lokalizację", + "pin_code_changed_successfully": "Pomyślnie zmieniono kod PIN", + "pin_code_reset_successfully": "Pomyślnie zresetowano kod PIN", + "pin_code_setup_successfully": "Pomyślnie ustawiono kod PIN", + "pin_verification": "Weryfikacja kodem PIN", "place": "Miejsce", "places": "Miejsca", "places_count": "{count, plural, one {{count, number} Miejsce} few {{count, number} Miejsca}other {{count, number} Miejsc}}", @@ -1358,7 +1394,7 @@ "play_memories": "OdtwÃŗrz wspomnienia", "play_motion_photo": "OdtwÃŗrz Ruchome Zdjęcie", "play_or_pause_video": "OdtwÃŗrz lub wstrzymaj wideo", - "port": "Port", + "please_auth_to_access": "Uwierzytelnij się, aby uzyskać dostęp", "preferences_settings_subtitle": "Zarządzaj preferencjami aplikacji", "preferences_settings_title": "Ustawienia", "preset": "Ustawienie", @@ -1368,11 +1404,11 @@ "previous_or_next_photo": "Poprzednie lub następne zdjęcie", "primary": "GÅ‚Ãŗwny", "privacy": "Prywatność", + "profile": "Profil", "profile_drawer_app_logs": "Logi", "profile_drawer_client_out_of_date_major": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej wersji gÅ‚Ãŗwnej.", "profile_drawer_client_out_of_date_minor": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej wersji dodatkowej.", "profile_drawer_client_server_up_to_date": "Klient i serwer są aktualne", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serwer jest nieaktualny. Zaktualizuj do najnowszej wersji gÅ‚Ãŗwnej.", "profile_drawer_server_out_of_date_minor": "Serwer jest nieaktualny. Zaktualizuj do najnowszej wersji dodatkowej.", "profile_image_of_user": "Zdjęcie profilowe {user}", @@ -1381,7 +1417,7 @@ "public_share": "Udostępnienie publiczne", "purchase_account_info": "Wspierający", "purchase_activated_subtitle": "Dziękuję za wspieranie Immich i oprogramowania open-source", - "purchase_activated_time": "Aktywowane dnia {date, date}", + "purchase_activated_time": "Aktywowane dnia {date}", "purchase_activated_title": "TwÃŗj klucz został pomyślnie aktywowany", "purchase_button_activate": "Aktywuj", "purchase_button_buy": "Kup", @@ -1426,6 +1462,8 @@ "recent_searches": "Ostatnie wyszukiwania", "recently_added": "Ostatnio dodane", "recently_added_page_title": "Ostatnio Dodane", + "recently_taken": "Ostatnio wykonane", + "recently_taken_page_title": "Ostatnio Wykonane", "refresh": "OdświeÅŧ", "refresh_encoded_videos": "OdświeÅŧ enkodowane wideo", "refresh_faces": "OdświeÅŧ twarze", @@ -1445,6 +1483,8 @@ "remove_deleted_assets": "Usuń Niedostępne Pliki", "remove_from_album": "Usuń z albumu", "remove_from_favorites": "Usuń z ulubionych", + "remove_from_locked_folder": "Usuń z folderu zablokowanego", + "remove_from_locked_folder_confirmation": "Czy na pewno chcesz przenieść te zdjęcia i filmy z folderu zablokowanego? Będą one widoczne w bibliotece", "remove_from_shared_link": "Usuń z udostępnionego linku", "remove_memory": "Usuń pamięć", "remove_photo_from_memory": "Usuń zdjęcia z tej pamięci", @@ -1465,9 +1505,9 @@ "require_password": "Wymagaj hasło", "require_user_to_change_password_on_first_login": "Zmuś uÅŧytkownika do zmiany hasła podczas następnego logowania", "rescan": "Ponowne skanowanie", - "reset": "Reset", "reset_password": "Resetuj hasło", "reset_people_visibility": "Zresetuj widoczność osÃŗb", + "reset_pin_code": "Zresetuj kod PIN", "reset_to_default": "PrzywrÃŗÄ‡ ustawienia domyślne", "resolve_duplicates": "RozwiąÅŧ problemy z duplikatami", "resolved_all_duplicates": "Rozwiązano wszystkie duplikaty", @@ -1539,7 +1579,7 @@ "search_rating": "Wyszukaj według ocen...", "search_result_page_new_search_hint": "Nowe wyszukiwanie", "search_settings": "Ustawienia przeszukiwania", - "search_state": "Wyszukaj stan...", + "search_state": "Wyszukaj wojewÃŗdztwo...", "search_suggestion_list_smart_search_hint_1": "Inteligentne wyszukiwanie jest domyślnie włączone, aby wyszukiwać metadane, uÅŧyj składni ", "search_suggestion_list_smart_search_hint_2": "m:wyszukiwane hasło", "search_tags": "Wyszukaj etykiety...", @@ -1560,14 +1600,15 @@ "select_keep_all": "Zaznacz zachowaj wszystko", "select_library_owner": "Wybierz właściciela biblioteki", "select_new_face": "Wybierz nową twarz", + "select_person_to_tag": "Wybierz osobę do oznaczenia", "select_photos": "Wybierz zdjęcia", - "select_trash_all": "Zaznacz cały kosz", + "select_trash_all": "Zaznacz wszystko do kosza", "select_user_for_sharing_page_err_album": "Nie udało się utworzyć albumu", "selected": "Zaznaczone", "selected_count": "{count, plural, other {# wybrane}}", "send_message": "Wyślij wiadomość", "send_welcome_email": "Wyślij e-mail powitalny", - "server_endpoint": "Serwer końcowy", + "server_endpoint": "Punkt końcowy serwera", "server_info_box_app_version": "Wersja Aplikacji", "server_info_box_server_url": "Adres URL", "server_offline": "Serwer Offline", @@ -1590,12 +1631,12 @@ "setting_languages_apply": "Zastosuj", "setting_languages_subtitle": "Zmień język aplikacji", "setting_languages_title": "Języki", - "setting_notifications_notify_failures_grace_period": "Powiadomienie o awariach kopii zapasowych w tle: {}", - "setting_notifications_notify_hours": "{} godzin", + "setting_notifications_notify_failures_grace_period": "Powiadomienie o awariach kopii zapasowych w tle: {duration}", + "setting_notifications_notify_hours": "{count, plural, one {# godzina} few {# godziny} other {# godzin}}", "setting_notifications_notify_immediately": "natychmiast", - "setting_notifications_notify_minutes": "{} minut", + "setting_notifications_notify_minutes": "{count, plural, one {# minuta} few {# minuty} other {# minut}}", "setting_notifications_notify_never": "nigdy", - "setting_notifications_notify_seconds": "{} sekund", + "setting_notifications_notify_seconds": "{count, plural, one {# sekunda} few {# sekundy} other {# sekund}}", "setting_notifications_single_progress_subtitle": "SzczegÃŗÅ‚owe informacje o postępie przesyłania dla kaÅŧdego zasobu", "setting_notifications_single_progress_title": "PokaÅŧ postęp szczegÃŗÅ‚Ãŗw kopii zapasowej w tle", "setting_notifications_subtitle": "Dostosuj preferencje powiadomień", @@ -1607,10 +1648,12 @@ "settings": "Ustawienia", "settings_require_restart": "Aby zastosować to ustawienie, uruchom ponownie Immich", "settings_saved": "Ustawienia zapisane", + "setup_pin_code": "Ustaw kod PIN", "share": "Udostępnij", "share_add_photos": "Dodaj zdjęcia", - "share_assets_selected": "{} wybrano ", - "share_dialog_preparing": "Przygotowywanie...", + "share_assets_selected": "Wybrano {count}", + "share_dialog_preparing": "Przygotowywanieâ€Ļ", + "share_link": "Udostępnij link", "shared": "Udostępnione", "shared_album_activities_input_disable": "Komentarz jest wyłączony", "shared_album_activity_remove_content": "Czy chcesz usunąć tę aktywność?", @@ -1623,34 +1666,33 @@ "shared_by_user": "Udostępnione przez {user}", "shared_by_you": "Udostępnione przez ciebie", "shared_from_partner": "Zdjęcia od {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Przesłano", + "shared_intent_upload_button_progress_text": "{current} / {total} Przesłano", "shared_link_app_bar_title": "Udostępnione linki", "shared_link_clipboard_copied_massage": "Skopiowane do schowka", - "shared_link_clipboard_text": "Link: {}\nHasło: {}", - "shared_link_create_error": "Błąd podczas tworzenia udostępnionego linka", + "shared_link_clipboard_text": "Link: {link}\nHasło: {password}", + "shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia", "shared_link_edit_description_hint": "WprowadÅē opis udostępnienia", - "shared_link_edit_expire_after_option_day": "1 dzień", - "shared_link_edit_expire_after_option_days": "{} dni", - "shared_link_edit_expire_after_option_hour": "1 godzina", - "shared_link_edit_expire_after_option_hours": "{} godzin", - "shared_link_edit_expire_after_option_minute": "1 minuta", - "shared_link_edit_expire_after_option_minutes": "{} minut", - "shared_link_edit_expire_after_option_months": "{} miesięcy", - "shared_link_edit_expire_after_option_year": "{} lat", + "shared_link_edit_expire_after_option_day": "1 dniu", + "shared_link_edit_expire_after_option_days": "{count} dniach", + "shared_link_edit_expire_after_option_hour": "1 godzinie", + "shared_link_edit_expire_after_option_hours": "{count} godzinach", + "shared_link_edit_expire_after_option_minute": "1 minucie", + "shared_link_edit_expire_after_option_minutes": "{count} minutach", + "shared_link_edit_expire_after_option_months": "{count} miesiącach", + "shared_link_edit_expire_after_option_year": "{count, plural, one {# roku} other {# latach}}", "shared_link_edit_password_hint": "WprowadÅē hasło udostępniania", "shared_link_edit_submit_button": "Aktualizuj link", "shared_link_error_server_url_fetch": "Nie moÅŧna pobrać adresu URL serwera", - "shared_link_expires_day": "Wygasa za {} dni", - "shared_link_expires_days": "Wygasa za {} dni", - "shared_link_expires_hour": "Wygasa za {} godzin", - "shared_link_expires_hours": "Wygasa za {} godzin", - "shared_link_expires_minute": "Wygasa za {} minut", - "shared_link_expires_minutes": "Wygasa za {} minut", + "shared_link_expires_day": "Wygasa za {count} dzień", + "shared_link_expires_days": "Wygasa za {count} dni", + "shared_link_expires_hour": "Wygasa za {count} godzinę", + "shared_link_expires_hours": "Wygasa za {count, plural, one {# godzinę} few {# godziny} other {# godzin}}", + "shared_link_expires_minute": "Wygasa za {count} minutę", + "shared_link_expires_minutes": "Wygasa za {count, plural, one {# minutę} few {# minuty} other {# minut}}", "shared_link_expires_never": "Wygasa ∞", - "shared_link_expires_second": "Wygasa za {} sekund", - "shared_link_expires_seconds": "Wygasa za {} sekund", + "shared_link_expires_second": "Wygasa za {count} sekundę", + "shared_link_expires_seconds": "Wygasa za {count, plural, one {# sekundę} few {# sekundy} other {# sekund}}", "shared_link_individual_shared": "Indywidualnie udostępnione", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Zarządzaj udostępnionymi linkami", "shared_link_options": "Opcje udostępniania linku", "shared_links": "Udostępnione linki", @@ -1713,16 +1755,15 @@ "stack_selected_photos": "Układaj wybrane zdjęcia", "stacked_assets_count": "UłoÅŧone {count, plural, one {# zasÃŗb} other{# zasoby}}", "stacktrace": "Ślad stosu", - "start": "Start", "start_date": "Od dnia", - "state": "Stan", - "status": "Status", + "state": "WojewÃŗdztwo", "stop_motion_photo": "Zatrzymaj zdjęcie w ruchu", "stop_photo_sharing": "Przestać udostępniać swoje zdjęcia?", "stop_photo_sharing_description": "Od teraz {partner} nie będzie widzieć Twoich zdjęć.", "stop_sharing_photos_with_user": "Przestań udostępniać zdjęcia temu uÅŧytkownikowi", "storage": "Przestrzeń dyskowa", "storage_label": "Etykieta magazynu", + "storage_quota": "Limit pamięci", "storage_usage": "{used} z {available} uÅŧyte", "submit": "ZatwierdÅē", "suggestions": "Sugestie", @@ -1749,7 +1790,7 @@ "theme_selection": "WybÃŗr motywu", "theme_selection_description": "Automatycznie zmień motyw na jasny lub ciemny zaleÅŧnie od ustawień przeglądarki", "theme_setting_asset_list_storage_indicator_title": "PokaÅŧ wskaÅēnik przechowywania na kafelkach zasobÃŗw", - "theme_setting_asset_list_tiles_per_row_title": "Liczba zasobÃŗw w wierszu ({})", + "theme_setting_asset_list_tiles_per_row_title": "Liczba zasobÃŗw w wierszu ({count})", "theme_setting_colorful_interface_subtitle": "Zastosuj kolor podstawowy do powierzchni tła.", "theme_setting_colorful_interface_title": "Kolorowy interfejs", "theme_setting_image_viewer_quality_subtitle": "Dostosuj jakość podglądu szczegÃŗÅ‚owości", @@ -1769,7 +1810,7 @@ "to_archive": "Archiwum", "to_change_password": "Zmień hasło", "to_favorite": "Dodaj do ulubionych", - "to_login": "Login", + "to_login": "Zaloguj się", "to_parent": "IdÅē do rodzica", "to_trash": "Kosz", "toggle_settings": "Przełącz ustawienia", @@ -1777,22 +1818,24 @@ "total": "Całkowity", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", - "trash_all": "Usuń wszystko", + "trash_all": "Usuń wszystkie", "trash_count": "Kosz {count, number}", "trash_delete_asset": "Kosz/Usuń zasÃŗb", "trash_emptied": "OprÃŗÅŧnione śmieci", "trash_no_results_message": "Tu znajdziesz wyrzucone zdjęcia i filmy.", "trash_page_delete_all": "Usuń wszystko", "trash_page_empty_trash_dialog_content": "Czy chcesz oprÃŗÅŧnić swoje usunięte zasoby? Przedmioty te zostaną trwale usunięte z Immich", - "trash_page_info": "Elementy przeniesione do kosza zostaną trwale usunięte po {} dniach", + "trash_page_info": "Elementy przeniesione do kosza zostaną trwale usunięte po {days, plural, one {# dniu} other {# dniach}}", "trash_page_no_assets": "Brak usuniętych zasobÃŗw", "trash_page_restore_all": "PrzywrÃŗcić wszystkie", "trash_page_select_assets_btn": "Wybierz zasoby", - "trash_page_title": "Kosz({})", - "trashed_items_will_be_permanently_deleted_after": "Wyrzucone zasoby zostaną trwale usunięte po {days, plural, one {jednym dniu} other {{days, number} dniach}}.", + "trash_page_title": "Kosz ({count})", + "trashed_items_will_be_permanently_deleted_after": "Wyrzucone zasoby zostaną trwale usunięte po {days, plural, one {jednym dniu} other {# dniach}}.", "type": "Typ", + "unable_to_change_pin_code": "Nie moÅŧna zmienić kodu PIN", + "unable_to_setup_pin_code": "Nie moÅŧna ustawić kodu PIN", "unarchive": "Cofnij archiwizację", - "unarchived_count": "{count, plural, other {Niezarchiwizowane #}}", + "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", "unfavorite": "Usuń z ulubionych", "unhide_person": "PrzywrÃŗÄ‡ osobę", "unknown": "Nieznany", @@ -1809,32 +1852,36 @@ "unsaved_change": "Niezapisana zmiana", "unselect_all": "Odznacz wszystko", "unselect_all_duplicates": "Odznacz wszystkie duplikaty", - "unstack": "Usuń stos", - "unstacked_assets_count": "NieułoÅŧone {count, plural, one {# zasÃŗb} other{# zasoby}}", + "unstack": "RozÅ‚ÃŗÅŧ stos", + "unstacked_assets_count": "{count, plural, one {RozłoÅŧony # zasÃŗb} few {RozłoÅŧone # zasoby} other {RozłoÅŧonych # zasobÃŗw}}", "untracked_files": "Nieśledzone pliki", "untracked_files_decription": "Pliki te nie są śledzone przez aplikację. Mogą być wynikiem nieudanych przeniesień, przerwanego przesyłania lub pozostawienia z powodu błędu", "up_next": "Do następnego", + "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", "upload": "Prześlij", "upload_concurrency": "WspÃŗÅ‚bieÅŧność wysyłania", "upload_dialog_info": "Czy chcesz wykonać kopię zapasową wybranych zasobÃŗw na serwerze?", "upload_dialog_title": "Prześlij ZasÃŗb", - "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błąd} other {# błędy}}. OdświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", + "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błędem} other {# błędami}}. OdświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", "upload_progress": "Pozostałe {remaining, number} - Przetworzone {processed, number}/{total, number}", - "upload_skipped_duplicates": "Pominięte {count, plural, one {# zduplikowany zasÃŗb} other {# zduplikowane zasoby}}", + "upload_skipped_duplicates": "Pominięto {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} other {# zduplikowanych zasobÃŗw}}", "upload_status_duplicates": "Duplikaty", "upload_status_errors": "Błędy", "upload_status_uploaded": "Przesłano", "upload_success": "Przesyłanie powiodło się, odświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", - "upload_to_immich": "Prześlij do Immich ({})", + "upload_to_immich": "Prześlij do Immich ({count})", "uploading": "Przesyłanie", - "url": "URL", "usage": "UÅŧycie", + "use_biometric": "UÅŧyj biometrii", "use_current_connection": "uÅŧyj bieÅŧącego połączenia", "use_custom_date_range": "Zamiast tego uÅŧyj niestandardowego zakresu dat", "user": "UÅŧytkownik", + "user_has_been_deleted": "Ten uÅŧytkownik został usunięty.", "user_id": "ID uÅŧytkownika", "user_liked": "{user} polubił {type, select, photo {to zdjęcie} video {to wideo} asset {ten zasÃŗb} other {to}}", + "user_pin_code_settings": "Kod PIN", + "user_pin_code_settings_description": "Zarządzaj swoim kodem PIN", "user_purchase_settings": "Zakup", "user_purchase_settings_description": "Zarządzaj swoim zakupem", "user_role_set": "Ustaw {user} jako {role}", @@ -1849,7 +1896,7 @@ "variables": "Zmienne", "version": "Wersja", "version_announcement_closing": "TwÃŗj przyjaciel Aleks", - "version_announcement_message": "Witaj! Dostępna jest nowa wersja Immich. Poświęć trochę czasu na zapoznanie się z informacjami o wydaniu, aby upewnić się, Åŧe twoja konfiguracja jest aktualna, aby uniknąć błędÃŗw, szczegÃŗlnie jeśli uÅŧywasz WatchTower lub jakiegokolwiek mechanizmu odpowiedzialnego za automatyczne aktualizowanie Immich.", + "version_announcement_message": "Witaj! Dostępna jest nowa wersja Immich. Poświęć trochę czasu na zapoznanie się z informacjami o wydaniu, aby upewnić się, Åŧe ustawienia twojej instalacji są aktualne i zapobiec błędnym konfiguracjom. SzczegÃŗlnie jeśli uÅŧywasz WatchTower lub jakiegokolwiek mechanizmu odpowiedzialnego za automatyczne aktualizowanie Immich.", "version_announcement_overlay_release_notes": "informacje o wydaniu", "version_announcement_overlay_text_1": "Cześć przyjacielu, jest nowe wydanie", "version_announcement_overlay_text_2": "prosimy o poświęcenie czasu na odwiedzenie ", @@ -1872,17 +1919,19 @@ "view_name": "Widok", "view_next_asset": "Wyświetl następny zasÃŗb", "view_previous_asset": "Wyświetl poprzedni zasÃŗb", + "view_qr_code": "PokaÅŧ kod QR", "view_stack": "Zobacz UłoÅŧenie", "viewer_remove_from_stack": "Usuń ze stosu", "viewer_stack_use_as_main_asset": "UÅŧyj jako gÅ‚Ãŗwnego zasobu", - "viewer_unstack": "Usuń stos", - "visibility_changed": "Zmieniono widoczność dla {count, plural, one {# osoba} other {# osoby}}", - "waiting": "Oczekiwanie", + "viewer_unstack": "RozÅ‚ÃŗÅŧ Stos", + "visibility_changed": "Zmieniono widoczność dla {count, plural, one {# osoby} other {# osÃŗb}}", + "waiting": "Oczekujące", "warning": "OstrzeÅŧenie", "week": "Tydzień", "welcome": "Witaj", "welcome_to_immich": "Witamy w immich", "wifi_name": "Nazwa Wi-Fi", + "wrong_pin_code": "Nieprawidłowy kod PIN", "year": "Rok", "years_ago": "{years, plural, one {# rok} few {# lata} other {# lat}} temu", "yes": "Tak", diff --git a/i18n/pt.json b/i18n/pt.json index 3e1bd463ef..c22c64ab4b 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -26,6 +26,7 @@ "add_to_album": "Adicionar ao ÃĄlbum", "add_to_album_bottom_sheet_added": "Adicionado a {album}", "add_to_album_bottom_sheet_already_exists": "JÃĄ existe em {album}", + "add_to_locked_folder": "Adicionar a pasta trancada", "add_to_shared_album": "Adicionar ao ÃĄlbum partilhado", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Tem a certeza que deseja desativar todos os mÊtodos de início de sessÃŖo? O início de sessÃŖo serÃĄ completamente desativado.", "authentication_settings_reenable": "Para reativar, use um Comando de servidor.", "background_task_job": "Tarefas em segundo plano", - "backup_database": "CÃŗpia de Segurança da Base de Dados", - "backup_database_enable_description": "Ativar cÃŗpias de segurança da base de dados", - "backup_keep_last_amount": "Quantidade de cÃŗpias de segurança anteriores a manter", - "backup_settings": "DefiniçÃĩes de CÃŗpia de Segurança", - "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia de segurança da base de dados", + "backup_database": "Criar CÃŗpia da Base de Dados", + "backup_database_enable_description": "Ativar cÃŗpias da base de dados", + "backup_keep_last_amount": "Quantidade de cÃŗpias anteriores a manter", + "backup_settings": "DefiniçÃĩes de CÃŗpia da Base de Dados", + "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia da base de dados. Aviso: Estas tarefas nÃŖo sÃŖo monitorizadas, pelo que nÃŖo serÃĄ notificado(a) em caso de erro.", "check_all": "Selecionar Tudo", "cleanup": "Limpeza", "cleared_jobs": "Eliminadas as tarefas de: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "Para confirmar, escreva \"{email}\" abaixo", "confirm_reprocess_all_faces": "Tem a certeza de que deseja reprocessar todos os rostos? Isto tambÊm limparÃĄ os nomes das pessoas.", "confirm_user_password_reset": "Tem a certeza de que deseja redefinir a palavra-passe de {user}?", + "confirm_user_pin_code_reset": "Tem a certeza de que quer repor o cÃŗdigo PIN de {user}?", "create_job": "Criar tarefa", "cron_expression": "ExpressÃŖo Cron", "cron_expression_description": "Definir o intervalo de anÃĄlise utilizando o formato Cron. Para mais informaçÃĩes, por favor veja o Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "Registo automÃĄtico", "oauth_auto_register_description": "Registar automaticamente novos utilizadores apÃŗs iniciarem sessÃŖo com o OAuth", "oauth_button_text": "Texto do botÃŖo", - "oauth_client_id": "ID do Cliente", - "oauth_client_secret": "Segredo do cliente", + "oauth_client_secret_description": "ObrigatÃŗrio se PKCE (Proof Key for Code Exchange) nÃŖo for suportado pelo provedor OAuth", "oauth_enable_description": "Iniciar sessÃŖo com o OAuth", - "oauth_issuer_url": "URL do emissor", "oauth_mobile_redirect_uri": "URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override": "SubstituiÃ§ÃŖo de URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo permite um URI mÃŗvel, como '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmo de assinatura de perfis", - "oauth_profile_signing_algorithm_description": "Algoritmo utilizado para assinar o perfil de utilizador.", - "oauth_scope": "Escopo", "oauth_settings": "OAuth", "oauth_settings_description": "Gerir definiçÃĩes de inicio de sessÃŖo do OAuth", "oauth_settings_more_details": "Para mais informaçÃĩes sobre esta funcionalidade, veja a documentaÃ§ÃŖo.", - "oauth_signing_algorithm": "Algoritmo de assinatura", "oauth_storage_label_claim": "ReivindicaÃ§ÃŖo de RÃŗtulo de Armazenamento", "oauth_storage_label_claim_description": "Definir automaticamente o RÃŗtulo de Armazenamento do utilizador para o valor desta declaraÃ§ÃŖo.", "oauth_storage_quota_claim": "ReivindicaÃ§ÃŖo de quota de armazenamento", "oauth_storage_quota_claim_description": "Definir automaticamente a quota de armazenamento do utilizador para o valor desta declaraÃ§ÃŖo.", "oauth_storage_quota_default": "Quota de armazenamento padrÃŖo (GiB)", "oauth_storage_quota_default_description": "Quota em GiB a ser usada quando nenhuma reivindicaÃ§ÃŖo for fornecida (insira 0 para quota ilimitada).", + "oauth_timeout": "Tempo Limite de RequisiÃ§ÃŖo", + "oauth_timeout_description": "Tempo limite para requisiçÃĩes, em milissegundos", "offline_paths": "Caminhos Offline", "offline_paths_description": "Estes resultados podem ser devidos à eliminaÃ§ÃŖo manual de ficheiros que nÃŖo fazem parte de uma biblioteca externa.", "password_enable_description": "Iniciar sessÃŖo com e-mail e palavra-passe", @@ -270,7 +268,7 @@ "template_email_update_album": "Modelo do e-mail de atualizaÃ§ÃŖo do ÃĄlbum", "template_email_welcome": "Modelos do email de boas vindas", "template_settings": "Modelos de notificaÃ§ÃŖo", - "template_settings_description": "Gerir modelos personalizados para notificaçÃĩes.", + "template_settings_description": "Gerir modelos personalizados para notificaçÃĩes", "theme_custom_css_settings": "CSS Personalizado", "theme_custom_css_settings_description": "Folhas de estilo em cascata (CSS) permitem que o design do Immich seja personalizado.", "theme_settings": "DefiniçÃĩes de Tema", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "NÃēmero de dias apÃŗs a remoÃ§ÃŖo para excluir permanentemente a conta e os ficheiros de um utilizador. A tarefa de eliminaÃ§ÃŖo de utilizadores Ê executada à meia-noite para verificar utilizadores que estÃŖo prontos para eliminaÃ§ÃŖo. As alteraçÃĩes a esta definiÃ§ÃŖo serÃŖo avaliadas na prÃŗxima execuÃ§ÃŖo.", "user_delete_immediately": "A conta e os ficheiros de {user} serÃŖo colocados em fila para eliminaÃ§ÃŖo permanente de imediato.", "user_delete_immediately_checkbox": "Adicionar utilizador e ficheiros à fila para eliminaÃ§ÃŖo imediata", + "user_details": "Detalhes do utilizador", "user_management": "GestÃŖo de utilizadores", "user_password_has_been_reset": "A palavra-passe do utilizador foi redefinida:", "user_password_reset_description": "Por favor forneça a palavra-passe temporÃĄria ao utilizador e informe-o(a) de que serÃĄ necessÃĄrio alterÃĄ-la prÃŗximo início de sessÃŖo.", @@ -371,13 +370,17 @@ "admin_password": "Palavra-passe do administrador", "administration": "AdministraÃ§ÃŖo", "advanced": "Avançado", - "advanced_settings_log_level_title": "Nível de log: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Utilize esta definiÃ§ÃŖo para filtrar ficheiros durante a sincronizaÃ§ÃŖo baseada em critÊrios alternativos. Utilize apenas se a aplicaÃ§ÃŖo estiver com problemas a detetar todos os ÃĄlbuns.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar um filtro alternativo de sincronizaÃ§ÃŖo de ÃĄlbuns em dispositivos", + "advanced_settings_log_level_title": "Nível de registo: {level}", "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos sÃŖo extremamente lentos para carregar miniaturas da memÃŗria. Ative esta opÃ§ÃŖo para preferir imagens do servidor.", "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicaçÃĩes com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", "advanced_settings_self_signed_ssl_subtitle": "NÃŖo validar o certificado SSL com o endereço do servidor. Isto Ê necessÃĄrio para certificados auto-assinados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto-assinados", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticamente eliminar ou restaurar um ficheiro neste dispositivo quando essa mesma aÃ§ÃŖo for efetuada na web", + "advanced_settings_sync_remote_deletions_title": "Sincronizar ficheiros eliminados remotamente [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "ConfiguraçÃĩes avançadas do usuÃĄrio", "advanced_settings_troubleshooting_subtitle": "Ativar funcionalidades adicionais para a resoluÃ§ÃŖo de problemas", "advanced_settings_troubleshooting_title": "ResoluÃ§ÃŖo de problemas", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", "album_share_no_users": "Parece que tem este ÃĄlbum partilhado com todos os utilizadores ou que nÃŖo existem utilizadores com quem o partilhar.", "album_thumbnail_card_item": "1 arquivo", - "album_thumbnail_card_items": "{} arquivos", + "album_thumbnail_card_items": "{count} ficheiros", "album_thumbnail_card_shared": " ¡ Compartilhado", - "album_thumbnail_shared_by": "Compartilhado por {}", + "album_thumbnail_shared_by": "Partilhado por {user}", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receber uma notificaÃ§ÃŖo por e-mail quando um ÃĄlbum partilhado tiver novos ficheiros", "album_user_left": "Saíu do {album}", @@ -440,7 +443,7 @@ "archive": "Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", - "archive_page_title": "Arquivado ({})", + "archive_page_title": "Arquivo ({count})", "archive_size": "Tamanho do arquivo", "archive_size_description": "Configure o tamanho do arquivo para transferÃĒncias (em GiB)", "archived": "Arquivado", @@ -477,18 +480,18 @@ "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao ÃĄlbum", "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {{name}} other {novo ÃĄlbum}}", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", - "assets_deleted_permanently": "{} arquivo(s) excluído permanentemente", - "assets_deleted_permanently_from_server": "{} arquivo(s) excluídos permanentemente do servidor", + "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", + "assets_deleted_permanently_from_server": "{count} ficheiro(s) eliminado(s) permanentemente do servidor Immich", "assets_moved_to_trash_count": "{count, plural, one {# ficheiro movido} other {# ficheiros movidos}} para a reciclagem", "assets_permanently_deleted_count": "{count, plural, one {# ficheiro} other {# ficheiros}} eliminados permanentemente", "assets_removed_count": "{count, plural, one {# ficheiro eliminado} other {# ficheiros eliminados}}", - "assets_removed_permanently_from_device": "{} arquivo(s) removidos permanentemente do seu dispositivo", + "assets_removed_permanently_from_device": "{count} ficheiro(s) removido(s) permanentemente do seu dispositivo", "assets_restore_confirmation": "Tem a certeza de que quer recuperar todos os ficheiros apagados? NÃŖo Ê possível anular esta aÃ§ÃŖo! Tenha em conta de que quaisquer ficheiros indisponíveis nÃŖo podem ser restaurados desta forma.", "assets_restored_count": "{count, plural, one {# ficheiro restaurado} other {# ficheiros restaurados}}", - "assets_restored_successfully": "{} arquivo(s) restaurados com sucesso", - "assets_trashed": "{} arquivo(s) enviados para a lixeira", + "assets_restored_successfully": "{count} ficheiro(s) restaurados com sucesso", + "assets_trashed": "{count} ficheiro(s) enviado(s) para a reciclagem", "assets_trashed_count": "{count, plural, one {# ficheiro enviado} other {# ficheiros enviados}} para a reciclagem", - "assets_trashed_from_server": "{} arquivo(s) do servidor foram enviados para a lixeira", + "assets_trashed_from_server": "{count} ficheiro(s) do servidor Immich foi/foram enviados para a reciclagem", "assets_were_part_of_album_count": "{count, plural, one {O ficheiro jÃĄ fazia} other {Os ficheiros jÃĄ faziam}} parte do ÃĄlbum", "authorized_devices": "Dispositivos Autorizados", "automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexÃĩes alternativas em outras redes", @@ -497,7 +500,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissÃŖo de localizaÃ§ÃŖo precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", - "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({})", + "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", "backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vÃĄrios ÃĄlbuns. Assim, os ÃĄlbuns podem ser incluídos ou excluídos durante o processo de backup.", "backup_album_selection_page_select_albums": "Selecione Álbuns", @@ -505,38 +508,35 @@ "backup_album_selection_page_total_assets": "Total de arquivos Ãēnicos", "backup_all": "Tudo", "backup_background_service_backup_failed_message": "Falha ao fazer backup dos arquivos. Tentando novamenteâ€Ļ", - "backup_background_service_connection_failed_message": "Falha na conexÃŖo com o servidor. Tentando novamente...", - "backup_background_service_current_upload_notification": "Enviando {}", + "backup_background_service_connection_failed_message": "Falha na ligaÃ§ÃŖo ao servidor. A tentar de novoâ€Ļ", + "backup_background_service_current_upload_notification": "A enviar {filename}", "backup_background_service_default_notification": "Verificando novos arquivosâ€Ļ", "backup_background_service_error_title": "Erro de backup", "backup_background_service_in_progress_notification": "Fazendo backup dos arquivosâ€Ļ", - "backup_background_service_upload_failure_notification": "Falha ao carregar {}", + "backup_background_service_upload_failure_notification": "Ocorreu um erro ao enviar {filename}", "backup_controller_page_albums": "Backup Álbuns", - "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano em ConfiguraçÃĩes > Geral > AtualizaÃ§ÃŖo do app em segundo plano ", + "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar a cÃŗpia de segurança em segundo plano, ative a atualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano em SefiniçÃĩes > Geral > AtualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano.", "backup_controller_page_background_app_refresh_disabled_title": "AtualizaÃ§ÃŖo do app em segundo plano desativada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configuraçÃĩes", "backup_controller_page_background_battery_info_link": "Mostre-me como", "backup_controller_page_background_battery_info_message": "Para obter a melhor experiÃĒncia de backup em segundo plano, desative todas as otimizaçÃĩes de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso Ê específico por dispositivo, consulte as informaçÃĩes de como fazer isso com o fabricante do dispositivo.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "OtimizaçÃĩes de bateria", "backup_controller_page_background_charging": "Apenas enquanto carrega a bateria", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", - "backup_controller_page_background_delay": "Atrasar o backup de novos arquivos: {}", + "backup_controller_page_background_delay": "Atraso da cÃŗpia de segurança de novos ficheiros: {duration}", "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automÃĄtico de novos arquivos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automÃĄtico em segundo plano estÃĄ desativado", "backup_controller_page_background_is_on": "O backup automÃĄtico em segundo plano estÃĄ ativado", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", - "backup_controller_page_background_wifi": "Apenas no WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado:", + "backup_controller_page_background_wifi": "Apenas em Wi-Fi", + "backup_controller_page_backup_selected": "Selecionado: ", "backup_controller_page_backup_sub": "Fotos e vídeos salvos em backup", - "backup_controller_page_created": "Criado em: {}", + "backup_controller_page_created": "Criado em: {date}", "backup_controller_page_desc_backup": "Ative o backup para enviar automÃĄticamente novos arquivos para o servidor.", - "backup_controller_page_excluded": "Excluídos:", - "backup_controller_page_failed": "Falhou ({})", - "backup_controller_page_filename": "Nome do arquivo: {} [{}]", - "backup_controller_page_id": "ID:{}", + "backup_controller_page_excluded": "Eliminado: ", + "backup_controller_page_failed": "Falhou ({count})", + "backup_controller_page_filename": "Nome do ficheiro: {filename} [{size}]", "backup_controller_page_info": "InformaçÃĩes do backup", "backup_controller_page_none_selected": "Nenhum selecionado", "backup_controller_page_remainder": "Restante", @@ -545,7 +545,7 @@ "backup_controller_page_start_backup": "Iniciar Backup", "backup_controller_page_status_off": "Backup automÃĄtico desativado", "backup_controller_page_status_on": "Backup automÃĄtico ativado", - "backup_controller_page_storage_format": "{} de {} usados", + "backup_controller_page_storage_format": "{used} de {total} utilizado", "backup_controller_page_to_backup": "Álbuns para fazer backup", "backup_controller_page_total_sub": "Todas as fotos e vídeos dos ÃĄlbuns selecionados", "backup_controller_page_turn_off": "Desativar backup", @@ -560,6 +560,10 @@ "backup_options_page_title": "OpçÃĩes de backup", "backup_setting_subtitle": "Gerenciar as configuraçÃĩes de envio em primeiro e segundo plano", "backward": "Para trÃĄs", + "biometric_auth_enabled": "AutenticaÃ§ÃŖo biomÊtrica ativada", + "biometric_locked_out": "EstÃĄ impedido de utilizar a autenticaÃ§ÃŖo biomÊtrica", + "biometric_no_options": "Sem opçÃĩes biomÊtricas disponíveis", + "biometric_not_available": "A autenticaÃ§ÃŖo biomÊtrica nÃŖo estÃĄ disponível neste dispositivo", "birthdate_saved": "Data de nascimento guardada com sucesso", "birthdate_set_description": "A data de nascimento Ê utilizada para calcular a idade desta pessoa no momento em que uma fotografia foi tirada.", "blurred_background": "Fundo desfocado", @@ -570,21 +574,21 @@ "bulk_keep_duplicates_confirmation": "Tem a certeza de que deseja manter {count, plural, one {# ficheiro duplicado} other {# ficheiros duplicados}}? Isto resolverÃĄ todos os grupos duplicados sem eliminar nada.", "bulk_trash_duplicates_confirmation": "Tem a certeza de que deseja mover para a reciclagem {count, plural, one {# ficheiro duplicado} other {# ficheiros duplicados}}? Isto manterÃĄ o maior ficheiro de cada grupo e irÃĄ mover para a reciclagem todos os outros duplicados.", "buy": "Comprar Immich", - "cache_settings_album_thumbnails": "Miniaturas da pÃĄgina da biblioteca ({} arquivos)", + "cache_settings_album_thumbnails": "Miniaturas da pÃĄgina da biblioteca ({count} ficheiros)", "cache_settings_clear_cache_button": "Limpar cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetarÃĄ significativamente o desempenho do aplicativo atÊ que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estÃŖo na lista negra da aplicaÃ§ÃŖo", - "cache_settings_duplicated_assets_title": "Arquivos duplicados ({})", - "cache_settings_image_cache_size": "Tamanho do cache de imagem ({} arquivos)", + "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({count})", + "cache_settings_image_cache_size": "Tamanho da cache de imagem ({count} ficheiros)", "cache_settings_statistics_album": "Miniaturas da biblioteca", - "cache_settings_statistics_assets": "{} arquivos ({})", + "cache_settings_statistics_assets": "{count} ficheiros ({size})", "cache_settings_statistics_full": "Imagens completas", "cache_settings_statistics_shared": "Miniaturas de ÃĄlbuns compartilhados", "cache_settings_statistics_thumbnail": "Miniaturas", "cache_settings_statistics_title": "Uso de cache", "cache_settings_subtitle": "Controle o comportamento de cache do aplicativo Immich", - "cache_settings_thumbnail_size": "Tamanho do cache de miniaturas ({} arquivos)", + "cache_settings_thumbnail_size": "Tamanho da cache das miniaturas ({count} ficheiros)", "cache_settings_tile_subtitle": "Controlar o comportamento do armazenamento local", "cache_settings_tile_title": "Armazenamento local", "cache_settings_title": "ConfiguraçÃĩes de cache", @@ -597,7 +601,9 @@ "cannot_merge_people": "NÃŖo foi possível unir pessoas", "cannot_undo_this_action": "NÃŖo Ê possível anular esta aÃ§ÃŖo!", "cannot_update_the_description": "NÃŖo foi possível atualizar a descriÃ§ÃŖo", + "cast": "Reproduzir em", "change_date": "Alterar data", + "change_description": "Alterar descriÃ§ÃŖo", "change_display_order": "Mudar ordem de exibiÃ§ÃŖo", "change_expiration_time": "Alterar o prazo de validade", "change_location": "Alterar localizaÃ§ÃŖo", @@ -606,10 +612,11 @@ "change_password": "Alterar a palavra-passe", "change_password_description": "Esta Ê a primeira vez que estÃĄ a entrar no sistema ou um pedido foi feito para alterar a sua palavra-passe. Insira a nova palavra-passe abaixo.", "change_password_form_confirm_password": "Confirme a senha", - "change_password_form_description": "Esta Ê a primeira vez que vocÃĒ estÃĄ acessando o sistema ou foi feita uma solicitaÃ§ÃŖo para alterar sua senha. Por favor, insira a nova senha abaixo.", + "change_password_form_description": "OlÃĄ, {name}\n\nEsta Ê a primeira vez que estÃĄ a aceder ao sistema, ou entÃŖo foi feito um pedido para alterar a palavra-passe. Por favor insira uma nova palavra-passe abaixo.", "change_password_form_new_password": "Nova senha", "change_password_form_password_mismatch": "As senhas nÃŖo estÃŖo iguais", "change_password_form_reenter_new_password": "Confirme a nova senha", + "change_pin_code": "Alterar cÃŗdigo PIN", "change_your_password": "Alterar a sua palavra-passe", "changed_visibility_successfully": "Visibilidade alterada com sucesso", "check_all": "Verificar tudo", @@ -624,7 +631,6 @@ "clear_all_recent_searches": "Limpar todas as pesquisas recentes", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Digite a senha", "client_cert_import": "Importar", "client_cert_import_success_msg": "Certificado do cliente foi importado", @@ -650,17 +656,19 @@ "confirm_delete_face": "Tem a certeza de que deseja remover o rosto de {name} deste ficheiro?", "confirm_delete_shared_link": "Tem a certeza de que deseja eliminar este link partilhado?", "confirm_keep_this_delete_others": "Todos os outros ficheiros na pilha serÃŖo eliminados, exceto este ficheiro. Tem a certeza de que deseja continuar?", + "confirm_new_pin_code": "Confirmar novo cÃŗdigo PIN", "confirm_password": "Confirmar a palavra-passe", + "connected_to": "Ligado a", "contain": "Ajustar", "context": "Contexto", "continue": "Continuar", - "control_bottom_app_bar_album_info_shared": "{} arquivos ¡ Compartilhado", + "control_bottom_app_bar_album_info_shared": "{count} ficheiros ¡ Partilhado", "control_bottom_app_bar_create_new_album": "Criar novo ÃĄlbum", "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", "control_bottom_app_bar_edit_location": "Editar LocalizaÃ§ÃŖo", "control_bottom_app_bar_edit_time": "Editar Data & Hora", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "Partilhar ligaÃ§ÃŖo", "control_bottom_app_bar_share_to": "Compartilhar com", "control_bottom_app_bar_trash_from_immich": "Mover para a lixeira", "copied_image_to_clipboard": "Imagem copiada para a ÃĄrea de transferÃĒncia.", @@ -692,9 +700,11 @@ "create_tag_description": "Criar uma nova etiqueta. Para etiquetas compostas, introduza o caminho completo, incluindo as barras.", "create_user": "Criar utilizador", "created": "Criado", + "created_at": "Criado a", "crop": "Cortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo atual", + "current_pin_code": "CÃŗdigo PIN atual", "current_server_address": "Endereço atual do servidor", "custom_locale": "LocalizaÃ§ÃŖo Personalizada", "custom_locale_description": "Formatar datas e nÃēmeros baseados na língua e na regiÃŖo", @@ -746,7 +756,6 @@ "direction": "DireÃ§ÃŖo", "disabled": "Desativado", "disallow_edits": "NÃŖo permitir ediçÃĩes", - "discord": "Discord", "discover": "Descobrir", "dismiss_all_errors": "Dispensar todos os erros", "dismiss_error": "Dispensar erro", @@ -763,7 +772,7 @@ "download_enqueue": "Na fila", "download_error": "Erro ao baixar", "download_failed": "Falha", - "download_filename": "arquivo: {}", + "download_filename": "ficheiro: {filename}", "download_finished": "Concluído", "download_include_embedded_motion_videos": "Vídeos incorporados", "download_include_embedded_motion_videos_description": "Incluir vídeos incorporados em fotos em movimento como um ficheiro separado", @@ -787,6 +796,8 @@ "edit_avatar": "Editar imagem de perfil", "edit_date": "Editar data", "edit_date_and_time": "Editar data e hora", + "edit_description": "Editar descriÃ§ÃŖo", + "edit_description_prompt": "Por favor selecione uma nova descriÃ§ÃŖo:", "edit_exclusion_pattern": "Editar o padrÃŖo de exclusÃŖo", "edit_faces": "Editar rostos", "edit_import_path": "Editar caminho de importaÃ§ÃŖo", @@ -801,25 +812,28 @@ "edit_title": "Editar Título", "edit_user": "Editar utilizador", "edited": "Editado", - "editor": "Editor", "editor_close_without_save_prompt": "As alteraçÃĩes nÃŖo serÃŖo guardadas", "editor_close_without_save_title": "Fechar editor?", "editor_crop_tool_h2_aspect_ratios": "RelaÃ§ÃŖo de aspeto", "editor_crop_tool_h2_rotation": "RotaÃ§ÃŖo", "email": "E-mail", - "empty_folder": "This folder is empty", + "email_notifications": "NotificaçÃĩes por e-mail", + "empty_folder": "Esta pasta estÃĄ vazia", "empty_trash": "Esvaziar reciclagem", "empty_trash_confirmation": "Tem a certeza de que deseja esvaziar a reciclagem? Isto removerÃĄ todos os ficheiros da reciclagem do Immich permanentemente.\nNÃŖo Ê possível anular esta aÃ§ÃŖo!", "enable": "Ativar", + "enable_biometric_auth_description": "Insira o cÃŗdigo PIN para ativar a autenticaÃ§ÃŖo biomÊtrica", "enabled": "Ativado", "end_date": "Data final", "enqueued": "Na fila", - "enter_wifi_name": "Digite o nome do Wi-Fi", + "enter_wifi_name": "Escreva o nome da rede Wi-Fi", + "enter_your_pin_code": "Insira o cÃŗdigo PIN", + "enter_your_pin_code_subtitle": "Insira o cÃŗdigo PIN para aceder à pasta trancada", "error": "Erro", "error_change_sort_album": "Falha ao mudar a ordem de exibiÃ§ÃŖo", "error_delete_face": "Falha ao remover rosto do ficheiro", "error_loading_image": "Erro ao carregar a imagem", - "error_saving_image": "Erro: {}", + "error_saving_image": "Erro: {error}", "error_title": "Erro - Algo correu mal", "errors": { "cannot_navigate_next_asset": "NÃŖo foi possível navegar para o prÃŗximo ficheiro", @@ -849,10 +863,12 @@ "failed_to_keep_this_delete_others": "Ocorreu um erro ao manter este ficheiro e eliminar os outros", "failed_to_load_asset": "NÃŖo foi possível ler o ficheiro", "failed_to_load_assets": "NÃŖo foi possível ler ficheiros", + "failed_to_load_notifications": "Ocorreu um erro ao carregar notificaçÃĩes", "failed_to_load_people": "NÃŖo foi possível carregar pessoas", "failed_to_remove_product_key": "NÃŖo foi possível remover chave de produto", "failed_to_stack_assets": "NÃŖo foi possível empilhar os ficheiros", "failed_to_unstack_assets": "NÃŖo foi possível desempilhar ficheiros", + "failed_to_update_notification_status": "Ocorreu um erro ao atualizar o estado das notificaçÃĩes", "import_path_already_exists": "Este caminho de importaÃ§ÃŖo jÃĄ existe.", "incorrect_email_or_password": "Email ou palavra-passe incorretos", "paths_validation_failed": "A validaÃ§ÃŖo de {paths, plural, one {# caminho falhou} other {# caminhos falharam}}", @@ -870,6 +886,7 @@ "unable_to_archive_unarchive": "NÃŖo foi possível {archived, select, true {arquivar} other {desarquivar}}", "unable_to_change_album_user_role": "NÃŖo foi possível alterar a permissÃŖo do utilizador no ÃĄlbum", "unable_to_change_date": "NÃŖo foi possível alterar a data", + "unable_to_change_description": "NÃŖo foi possível alterar a descriÃ§ÃŖo", "unable_to_change_favorite": "NÃŖo foi possível mudar o favorito do ficheiro", "unable_to_change_location": "NÃŖo foi possível alterar a localizaÃ§ÃŖo", "unable_to_change_password": "NÃŖo foi possível alterar a palavra-passe", @@ -907,6 +924,7 @@ "unable_to_log_out_all_devices": "NÃŖo foi possível terminar a sessÃŖo em todos os dispositivos", "unable_to_log_out_device": "NÃŖo foi possível terminar a sessÃŖo no dispositivo", "unable_to_login_with_oauth": "NÃŖo foi possível iniciar sessÃŖo com OAuth", + "unable_to_move_to_locked_folder": "NÃŖo foi possível mover para a pasta trancada", "unable_to_play_video": "NÃŖo foi possível reproduzir o vídeo", "unable_to_reassign_assets_existing_person": "NÃŖo foi possível reatribuir ficheiros para {name, select, null {uma pessoa existente} other {{name}}}", "unable_to_reassign_assets_new_person": "NÃŖo foi possível reatribuir os ficheiros a uma nova pessoa", @@ -920,6 +938,7 @@ "unable_to_remove_reaction": "NÃŖo foi possível remover a reaÃ§ÃŖo", "unable_to_repair_items": "NÃŖo foi possível reparar os itens", "unable_to_reset_password": "NÃŖo foi possível redefinir a palavra-passe", + "unable_to_reset_pin_code": "NÃŖo foi possível repor o cÃŗdigo PIN", "unable_to_resolve_duplicate": "NÃŖo foi possível resolver as duplicidades", "unable_to_restore_assets": "NÃŖo foi possível restaurar ficheiros", "unable_to_restore_trash": "NÃŖo foi possível restaurar itens da reciclagem", @@ -947,25 +966,23 @@ "unable_to_update_user": "NÃŖo foi possível atualizar o utilizador", "unable_to_upload_file": "NÃŖo foi possível carregar o ficheiro" }, - "exif": "Exif", "exif_bottom_sheet_description": "Adicionar DescriÃ§ÃŖo...", "exif_bottom_sheet_details": "DETALHES", "exif_bottom_sheet_location": "LOCALIZAÇÃO", "exif_bottom_sheet_people": "PESSOAS", "exif_bottom_sheet_person_add_person": "Adicionar nome", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Idade {age}", + "exif_bottom_sheet_person_age_months": "Idade {months} meses", + "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", + "exif_bottom_sheet_person_age_years": "Idade {years}", "exit_slideshow": "Sair da apresentaÃ§ÃŖo", "expand_all": "Expandir tudo", "experimental_settings_new_asset_list_subtitle": "Trabalho em andamento", "experimental_settings_new_asset_list_title": "Ativar visualizaÃ§ÃŖo de grade experimental", "experimental_settings_subtitle": "Use por sua conta e risco!", - "experimental_settings_title": "Experimental", - "expire_after": "Expira depois de", + "expire_after": "Expira apÃŗs", "expired": "Expirou", - "expires_date": "Expira em {date}", + "expires_date": "Expira a {date}", "explore": "Explorar", "explorer": "Explorador", "export": "Exportar", @@ -974,11 +991,12 @@ "external": "Externo", "external_libraries": "Bibliotecas externas", "external_network": "Rede externa", - "external_network_sheet_info": "Quando nÃŖo estiver na rede Wi-Fi especificada, o aplicativo irÃĄ se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo.", + "external_network_sheet_info": "Quando nÃŖo estiver ligado à rede Wi-Fi especificada, a aplicaÃ§ÃŖo irÃĄ ligar-se utilizando o primeiro URL abaixo que conseguir aceder, a começar do topo da lista para baixo", "face_unassigned": "Sem atribuiÃ§ÃŖo", "failed": "Falhou", + "failed_to_authenticate": "NÃŖo foi possível autenticar", "failed_to_load_assets": "Falha ao carregar ficheiros", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", "favorites": "Favoritos", @@ -992,10 +1010,11 @@ "filetype": "Tipo de ficheiro", "filter": "Filtro", "filter_people": "Filtrar pessoas", + "filter_places": "Filtrar lugares", "find_them_fast": "Encontre-as mais rapidamente pelo nome numa pesquisa", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Pasta", + "folder_not_found": "Pasta nÃŖo encontrada", "folders": "Pastas", "folders_feature_description": "Navegar na vista de pastas por fotos e vídeos no sistema de ficheiros", "forward": "Para a frente", @@ -1040,10 +1059,11 @@ "home_page_delete_remote_err_local": "Foram selecionados arquivos locais para excluir remotamente, ignorando", "home_page_favorite_err_local": "Ainda nÃŖo Ê possível adicionar recursos locais favoritos, ignorando", "home_page_favorite_err_partner": "Ainda nÃŖo Ê possível marcar arquivos do parceiro como favoritos, ignorando", - "home_page_first_time_notice": "Se Ê a primeira vez que utiliza o aplicativo, certifique-se de marcar um ou mais ÃĄlbuns do dispositivo para backup, assim a linha do tempo serÃĄ preenchida com as fotos e vídeos.", + "home_page_first_time_notice": "Se Ê a primeira vez que utiliza a aplicaÃ§ÃŖo, certifique-se de que marca pelo menos um ÃĄlbum do dispositivo para cÃŗpia de segurança, para a linha do tempo poder ser preenchida com fotos e vídeos", + "home_page_locked_error_local": "NÃŖo foi possível mover ficheiros locais para a pasta trancada, a continuar", + "home_page_locked_error_partner": "NÃŖo foi possível mover ficheiros do parceiro para a pasta trancada, a continuar", "home_page_share_err_local": "NÃŖo Ê possível compartilhar arquivos locais com um link, ignorando", "home_page_upload_err_limit": "SÃŗ Ê possível enviar 30 arquivos por vez, ignorando", - "host": "Host", "hour": "Hora", "ignore_icloud_photos": "ignorar fotos no iCloud", "ignore_icloud_photos_description": "Fotos que estÃŖo armazenadas no iCloud nÃŖo serÃŖo carregadas para o servidor do Immich", @@ -1095,7 +1115,6 @@ "language_setting_description": "Selecione o seu Idioma preferido", "last_seen": "Visto pela ultima vez", "latest_version": "VersÃŖo mais recente", - "latitude": "Latitude", "leave": "Sair", "lens_model": "Modelo de lente", "let_others_respond": "Permitir respostas", @@ -1120,12 +1139,14 @@ "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irÃĄ se conectar ao servidor atravÊs desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "PermissÃŖo de localizaÃ§ÃŖo", - "location_permission_content": "Para utilizar a funÃ§ÃŖo de troca automÃĄtica de URL, Ê necessÃĄrio a permissÃŖo de localizaÃ§ÃŖo precisa, para que seja possível ler o nome da rede Wi-Fi.", + "location_permission_content": "Para utilizar a funÃ§ÃŖo de troca automÃĄtica de URL, o Immich necessita da permissÃŖo de localizaÃ§ÃŖo exata, para que seja possível ler o nome da rede Wi-Fi atual", "location_picker_choose_on_map": "Escolha no mapa", "location_picker_latitude_error": "Digite uma latitude vÃĄlida", "location_picker_latitude_hint": "Digite a latitude", "location_picker_longitude_error": "Digite uma longitude vÃĄlida", "location_picker_longitude_hint": "Digite a longitude", + "lock": "Trancar", + "locked_folder": "Pasta trancada", "log_out": "Sair", "log_out_all_devices": "Terminar a sessÃŖo de todos os dispositivos", "logged_out_all_devices": "SessÃŖo terminada em todos os dispositivos", @@ -1155,7 +1176,6 @@ "login_password_changed_success": "Senha atualizada com sucesso", "logout_all_device_confirmation": "Tem a certeza de que deseja terminar a sessÃŖo em todos os dispositivos?", "logout_this_device_confirmation": "Tem a certeza de que deseja terminar a sessÃŖo deste dispositivo?", - "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ativar para repetir os vídeos automaticamente durante a exibiÃ§ÃŖo.", @@ -1170,8 +1190,8 @@ "manage_your_devices": "Gerir os seus dispositivos com sessÃŖo iniciada", "manage_your_oauth_connection": "Gerir a sua ligaÃ§ÃŖo ao OAuth", "map": "Mapa", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} fotos", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "Impossível obter a sua localizaÃ§ÃŖo", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localizaÃ§ÃŖo", @@ -1185,28 +1205,28 @@ "map_settings": "DefiniçÃĩes do mapa", "map_settings_dark_mode": "Modo escuro", "map_settings_date_range_option_day": "Últimas 24 horas", - "map_settings_date_range_option_days": "Últimos {} dias", + "map_settings_date_range_option_days": "Últimos {days} dias", "map_settings_date_range_option_year": "Último ano", - "map_settings_date_range_option_years": "Últimos {} anos", + "map_settings_date_range_option_years": "Últimos {years} anos", "map_settings_dialog_title": "ConfiguraçÃĩes do mapa", "map_settings_include_show_archived": "Incluir arquivados", "map_settings_include_show_partners": "Incluir parceiros", "map_settings_only_show_favorites": "Mostrar apenas favoritos", "map_settings_theme_settings": "Tema do mapa", "map_zoom_to_see_photos": "Diminua o zoom para ver mais fotos", + "mark_all_as_read": "Marcar tudo como lido", + "mark_as_read": "Marcar como lido", + "marked_all_as_read": "Tudo marcado como lido", "matches": "CorrespondÃĒncias", "media_type": "Tipo de mÊdia", "memories": "MemÃŗrias", "memories_all_caught_up": "Finalizamos por hoje", - "memories_check_back_tomorrow": "Volte amanhÃŖ para ver mais lembranças ", + "memories_check_back_tomorrow": "Volte amanhÃŖ para ver mais memÃŗrias", "memories_setting_description": "Gerir o que vÃĒ nas suas memÃŗrias", "memories_start_over": "Ver de novo", "memories_swipe_to_close": "Deslize para cima para fechar", - "memories_year_ago": "Um ano atrÃĄs", - "memories_years_ago": "{} anos atrÃĄs", "memory": "MemÃŗria", "memory_lane_title": "MemÃŗrias {title}", - "menu": "Menu", "merge": "Unir", "merge_people": "Unir pessoas", "merge_people_limit": "SÃŗ Ê possível unir atÊ 5 rostos de cada vez", @@ -1218,8 +1238,13 @@ "missing": "Em falta", "model": "Modelo", "month": "MÃĒs", - "monthly_title_text_date_format": "MMMM y", "more": "Mais", + "move": "Mover", + "move_off_locked_folder": "Mover para fora da pasta trancada", + "move_to_locked_folder": "Mover para a pasta trancada", + "move_to_locked_folder_confirmation": "Estas fotos e vídeos serÃŖo removidas de todos os ÃĄlbuns, e sÃŗ serÃŖo visíveis na pasta trancada", + "moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo", + "moved_to_library": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para a biblioteca", "moved_to_trash": "Enviado para a reciclagem", "multiselect_grid_edit_date_time_err_read_only": "NÃŖo Ê possível editar a data de arquivo sÃŗ leitura, ignorando", "multiselect_grid_edit_gps_err_read_only": "NÃŖo Ê possível editar a localizaÃ§ÃŖo de arquivo sÃŗ leitura, ignorando", @@ -1234,6 +1259,8 @@ "new_api_key": "Nova Chave de API", "new_password": "Nova palavra-passe", "new_person": "Nova Pessoa", + "new_pin_code": "Novo cÃŗdigo PIN", + "new_pin_code_subtitle": "Esta Ê a primeira vez que acede à pasta trancada. Crie um cÃŗdigo PIN para aceder a esta pÃĄgina de forma segura", "new_user_created": "Novo utilizador criado", "new_version_available": "NOVA VERSÃO DISPONÍVEL", "newest_first": "Mais recente primeiro", @@ -1251,7 +1278,10 @@ "no_explore_results_message": "Carregue mais fotos para explorar a sua coleÃ§ÃŖo.", "no_favorites_message": "Adicione aos favoritos para encontrar as suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver as suas fotos e vídeos", + "no_locked_photos_message": "Fotos e vídeos na pasta trancada estÃŖo ocultos e nÃŖo serÃŖo exibidos enquanto explora ou pesquisa na biblioteca.", "no_name": "Sem nome", + "no_notifications": "Sem notificaçÃĩes", + "no_people_found": "Nenhuma pessoa encontrada", "no_places": "Sem lugares", "no_results": "Sem resultados", "no_results_description": "Tente um sinÃŗnimo ou uma palavra-chave mais comum", @@ -1260,19 +1290,17 @@ "not_selected": "NÃŖo selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o RÃŗtulo de Armazenamento a ficheiros carregados anteriormente, execute o", "notes": "Notas", + "nothing_here_yet": "Ainda nÃŖo existe nada aqui", "notification_permission_dialog_content": "Para ativar as notificaçÃĩes, vÃĄ em ConfiguraçÃĩes e selecione permitir.", - "notification_permission_list_tile_content": "Dar permissÃĩes para ativar notificaçÃĩes", + "notification_permission_list_tile_content": "Conceder permissÃĩes para ativar notificaçÃĩes.", "notification_permission_list_tile_enable_button": "Ativar notificaçÃĩes", "notification_permission_list_tile_title": "PermissÃŖo de notificaçÃĩes", "notification_toggle_setting_description": "Ativar notificaçÃĩes por e-mail", "notifications": "NotificaçÃĩes", "notifications_setting_description": "Gerir notificaçÃĩes", - "oauth": "OAuth", "official_immich_resources": "Recursos oficiais do Immich", - "offline": "Offline", "offline_paths": "Caminhos offline", "offline_paths_description": "Estes resultados podem ser devidos a ficheiros eliminados manualmente e que nÃŖo fazem parte de uma biblioteca externa.", - "ok": "Ok", "oldest_first": "Mais antigo primeiro", "on_this_device": "Neste dispositivo", "onboarding": "IntegraÃ§ÃŖo", @@ -1280,15 +1308,14 @@ "onboarding_theme_description": "Escolha um tema de cor para sua instÃĸncia. Pode alterar isto mais tarde nas suas definiçÃĩes.", "onboarding_welcome_description": "Vamos configurar a sua instÃĸncia com algumas definiçÃĩes comuns.", "onboarding_welcome_user": "Bem-vindo(a), {user}", - "online": "Online", "only_favorites": "Apenas favoritos", + "open": "Abrir", "open_in_map_view": "Abrir na visualizaÃ§ÃŖo de mapa", "open_in_openstreetmap": "Abrir no OpenStreetMap", "open_the_search_filters": "Abrir os filtros de pesquisa", "options": "OpçÃĩes", "or": "ou", "organize_your_library": "Organizar a sua biblioteca", - "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", "other_variables": "Outras variÃĄveis", @@ -1305,7 +1332,7 @@ "partner_page_partner_add_failed": "Falha ao adicionar parceiro", "partner_page_select_partner": "Selecionar parceiro", "partner_page_shared_to_title": "Compartilhar com", - "partner_page_stop_sharing_content": "{} nÃŖo poderÃĄ mais acessar as suas fotos.", + "partner_page_stop_sharing_content": "{partner} irÃĄ deixar de ter acesso às suas fotos.", "partner_sharing": "Partilha com Parceiro", "partners": "Parceiros", "password": "Palavra-passe", @@ -1351,6 +1378,10 @@ "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos de anos anteriores", "pick_a_location": "Selecione uma localizaÃ§ÃŖo", + "pin_code_changed_successfully": "CÃŗdigo PIN alterado com sucesso", + "pin_code_reset_successfully": "CÃŗdigo PIN reposto com sucesso", + "pin_code_setup_successfully": "CÃŗdigo PIN configurado com sucesso", + "pin_verification": "VerificaÃ§ÃŖo do cÃŗdigo PIN", "place": "Lugar", "places": "Lugares", "places_count": "{count, plural, one {{count, number} Lugar} other {{count, number} Lugares}}", @@ -1358,6 +1389,7 @@ "play_memories": "Reproduzir memÃŗrias", "play_motion_photo": "Reproduzir foto em movimento", "play_or_pause_video": "Reproduzir ou Pausar vídeo", + "please_auth_to_access": "Por favor autentique-se para ter acesso", "port": "Porta", "preferences_settings_subtitle": "Gerenciar preferÃĒncias do aplicativo", "preferences_settings_title": "PreferÃĒncias", @@ -1368,11 +1400,11 @@ "previous_or_next_photo": "Foto anterior ou prÃŗxima", "primary": "PrimÃĄrio", "privacy": "Privacidade", - "profile_drawer_app_logs": "Logs", + "profile": "Perfil", + "profile_drawer_app_logs": "Registo", "profile_drawer_client_out_of_date_major": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", "profile_drawer_client_out_of_date_minor": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", "profile_drawer_client_server_up_to_date": "Cliente e Servidor atualizados", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo principal mais recente.", "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo mais recente.", "profile_image_of_user": "Imagem de perfil de {user}", @@ -1381,7 +1413,7 @@ "public_share": "Partilhar Publicamente", "purchase_account_info": "Apoiante", "purchase_activated_subtitle": "Agradecemos por apoiar o Immich e software de cÃŗdigo aberto", - "purchase_activated_time": "Ativado em {date, date}", + "purchase_activated_time": "Ativado em {date}", "purchase_activated_title": "A sua chave foi ativada com sucesso", "purchase_button_activate": "Ativar", "purchase_button_buy": "Comprar", @@ -1426,6 +1458,8 @@ "recent_searches": "Pesquisas recentes", "recently_added": "Adicionados Recentemente", "recently_added_page_title": "Adicionado recentemente", + "recently_taken": "Tirada recentemente", + "recently_taken_page_title": "Tiradas recentemente", "refresh": "Atualizar", "refresh_encoded_videos": "Atualizar vídeos codificados", "refresh_faces": "Atualizar rostos", @@ -1445,6 +1479,8 @@ "remove_deleted_assets": "Remover ficheiros indisponíveis", "remove_from_album": "Remover do ÃĄlbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_locked_folder": "Remover da pasta trancada", + "remove_from_locked_folder_confirmation": "Tem a certeza de que quer mover estas fotos e vídeos para fora da pasta trancada? PassarÃŖo a ser visíveis na biblioteca.", "remove_from_shared_link": "Remover do link partilhado", "remove_memory": "Remover memÃŗria", "remove_photo_from_memory": "Remover foto desta memÃŗria", @@ -1468,6 +1504,7 @@ "reset": "Redefinir", "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", + "reset_pin_code": "Repor cÃŗdigo PIN", "reset_to_default": "Repor predefiniçÃĩes", "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todos os itens duplicados resolvidos", @@ -1479,7 +1516,6 @@ "retry_upload": "Tentar carregar novamente", "review_duplicates": "Rever itens duplicados", "role": "FunÃ§ÃŖo", - "role_editor": "Editor", "role_viewer": "Visualizador", "save": "Guardar", "save_to_gallery": "Salvar na galeria", @@ -1529,7 +1565,7 @@ "search_page_no_places": "Nenhuma informaÃ§ÃŖo de local disponível", "search_page_screenshots": "Capturas de tela", "search_page_search_photos_videos": "Pesquise suas fotos e vídeos", - "search_page_selfies": "Selfies", + "search_page_selfies": "Auto-retratos (Selfies)", "search_page_things": "Objetos", "search_page_view_all_button": "Ver tudo", "search_page_your_activity": "A sua atividade", @@ -1540,7 +1576,7 @@ "search_result_page_new_search_hint": "Nova Pesquisa", "search_settings": "DefiniçÃĩes de pesquisa", "search_state": "Pesquisar estado/distrito...", - "search_suggestion_list_smart_search_hint_1": "A pesquisa inteligente estÃĄ ativada por padrÃŖo. Para pesquisar metadados, utilize a sintaxe", + "search_suggestion_list_smart_search_hint_1": "A pesquisa inteligente estÃĄ ativada por omissÃŖo. Para pesquisar por metadados, utilize a sintaxe ", "search_suggestion_list_smart_search_hint_2": "m:a-sua-pesquisa", "search_tags": "Pesquisar etiquetas...", "search_timezone": "Pesquisar fuso horÃĄrio...", @@ -1560,6 +1596,7 @@ "select_keep_all": "Selecionar manter todos", "select_library_owner": "Selecionar o dono da biblioteca", "select_new_face": "Selecionar novo rosto", + "select_person_to_tag": "Selecione uma pessoa para etiquetar", "select_photos": "Selecionar fotos", "select_trash_all": "Selecionar todos para reciclagem", "select_user_for_sharing_page_err_album": "Falha ao criar o ÃĄlbum", @@ -1590,12 +1627,12 @@ "setting_languages_apply": "Aplicar", "setting_languages_subtitle": "Alterar o idioma do aplicativo", "setting_languages_title": "Idioma", - "setting_notifications_notify_failures_grace_period": "Notifique falhas de backup em segundo plano: {}", - "setting_notifications_notify_hours": "{} horas", + "setting_notifications_notify_failures_grace_period": "Notificar erros da cÃŗpia de segurança em segundo plano: {duration}", + "setting_notifications_notify_hours": "{count} horas", "setting_notifications_notify_immediately": "imediatamente", - "setting_notifications_notify_minutes": "{} minutos", + "setting_notifications_notify_minutes": "{count} minutos", "setting_notifications_notify_never": "Nunca", - "setting_notifications_notify_seconds": "{} segundos", + "setting_notifications_notify_seconds": "{count} segundos", "setting_notifications_single_progress_subtitle": "InformaçÃĩes detalhadas sobre o progresso do envio por arquivo", "setting_notifications_single_progress_title": "Mostrar progresso detalhado do backup em segundo plano", "setting_notifications_subtitle": "Ajuste as preferÃĒncias de notificaÃ§ÃŖo", @@ -1607,10 +1644,12 @@ "settings": "DefiniçÃĩes", "settings_require_restart": "Reinicie o Immich para aplicar essa configuraÃ§ÃŖo", "settings_saved": "DefiniçÃĩes guardadas", + "setup_pin_code": "Configurar um cÃŗdigo PIN", "share": "Partilhar", "share_add_photos": "Adicionar fotos", - "share_assets_selected": "{} selecionado", + "share_assets_selected": "{count} selecionados", "share_dialog_preparing": "Preparando...", + "share_link": "Partilhar ligaÃ§ÃŖo", "shared": "Partilhado", "shared_album_activities_input_disable": "ComentÃĄrios desativados", "shared_album_activity_remove_content": "Deseja apagar esta atividade?", @@ -1623,34 +1662,33 @@ "shared_by_user": "Partilhado por {user}", "shared_by_you": "Partilhado por si", "shared_from_partner": "Fotos de {partner}", - "shared_intent_upload_button_progress_text": "Enviados {} de {}", + "shared_intent_upload_button_progress_text": "Enviados {current} de {total}", "shared_link_app_bar_title": "Links compartilhados", "shared_link_clipboard_copied_massage": "Copiado para a ÃĄrea de transferÃĒncia", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", + "shared_link_clipboard_text": "LigaÃ§ÃŖo: {link}\nPalavra-passe: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", "shared_link_edit_description_hint": "Digite a descriÃ§ÃŖo do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", - "shared_link_edit_expire_after_option_days": "{} dias", + "shared_link_edit_expire_after_option_days": "{count} dias", "shared_link_edit_expire_after_option_hour": "1 hora", - "shared_link_edit_expire_after_option_hours": "{} horas", + "shared_link_edit_expire_after_option_hours": "{count} horas", "shared_link_edit_expire_after_option_minute": "1 minuto", - "shared_link_edit_expire_after_option_minutes": "{} minutos", - "shared_link_edit_expire_after_option_months": "{} MÃĒses", - "shared_link_edit_expire_after_option_year": "{} ano", + "shared_link_edit_expire_after_option_minutes": "{count} minutos", + "shared_link_edit_expire_after_option_months": "{count} meses", + "shared_link_edit_expire_after_option_year": "{count} ano", "shared_link_edit_password_hint": "Digite uma senha para proteger este link", "shared_link_edit_submit_button": "Atualizar link", "shared_link_error_server_url_fetch": "Erro ao abrir a URL do servidor", - "shared_link_expires_day": "Expira em {} dia", - "shared_link_expires_days": "Expira em {} dias", - "shared_link_expires_hour": "Expira em {} hora", - "shared_link_expires_hours": "Expira em {} horas", - "shared_link_expires_minute": "Expira em {} minuto", - "shared_link_expires_minutes": "Expira em {} minutos", + "shared_link_expires_day": "Expira {count} dia", + "shared_link_expires_days": "Expira {count} dias", + "shared_link_expires_hour": "Expira {count} hora", + "shared_link_expires_hours": "Expira {count} horas", + "shared_link_expires_minute": "Expira {count} minuto", + "shared_link_expires_minutes": "Expira {count} minutos", "shared_link_expires_never": "Expira ∞", - "shared_link_expires_second": "Expira em {} segundo", - "shared_link_expires_seconds": "Expira em {} segundos", + "shared_link_expires_second": "Expira {count} segundo", + "shared_link_expires_seconds": "Expira {count} segundos", "shared_link_individual_shared": "Compartilhamento Ãēnico", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "OpçÃĩes de link partilhado", "shared_links": "Links partilhados", @@ -1712,7 +1750,6 @@ "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", "stacked_assets_count": "Empilhado {count, plural, one {# ficheiro} other {# ficheiros}}", - "stacktrace": "Stacktrace", "start": "Iniciar", "start_date": "Data de início", "state": "Estado/Distrito", @@ -1723,6 +1760,7 @@ "stop_sharing_photos_with_user": "Deixar de partilhar as fotos com este utilizador", "storage": "Espaço de armazenamento", "storage_label": "RÃŗtulo de Armazenamento", + "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", "suggestions": "SugestÃĩes", @@ -1749,12 +1787,12 @@ "theme_selection": "Selecionar tema", "theme_selection_description": "Definir automaticamente o tema como claro ou escuro com base na preferÃĒncia do sistema do seu navegador", "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de armazenamento na grade de fotos", - "theme_setting_asset_list_tiles_per_row_title": "Quantidade de arquivos por linha ({})", - "theme_setting_colorful_interface_subtitle": "Aplica a cor primÃĄria ao fundo", + "theme_setting_asset_list_tiles_per_row_title": "Quantidade de ficheiros por linha ({count})", + "theme_setting_colorful_interface_subtitle": "Aplica a cor primÃĄria ao fundo.", "theme_setting_colorful_interface_title": "Interface colorida", "theme_setting_image_viewer_quality_subtitle": "Ajuste a qualidade do visualizador de imagens detalhadas", "theme_setting_image_viewer_quality_title": "Qualidade do visualizador de imagens", - "theme_setting_primary_color_subtitle": "Selecione a cor primÃĄria, usada nas açÃĩes principais e realces", + "theme_setting_primary_color_subtitle": "Selecione a cor primÃĄria, utilizada nas açÃĩes principais e nos realces.", "theme_setting_primary_color_title": "Cor primÃĄria", "theme_setting_system_primary_color_title": "Use a cor do sistema", "theme_setting_system_theme_switch": "AutomÃĄtico (Siga a configuraÃ§ÃŖo do sistema)", @@ -1774,7 +1812,6 @@ "to_trash": "Reciclagem", "toggle_settings": "Alternar configuraçÃĩes", "toggle_theme": "Ativar modo escuro", - "total": "Total", "total_usage": "Total utilizado", "trash": "Reciclagem", "trash_all": "Mover todos para a reciclagem", @@ -1784,13 +1821,15 @@ "trash_no_results_message": "Fotos e vídeos enviados para a reciclagem aparecem aqui.", "trash_page_delete_all": "Excluir tudo", "trash_page_empty_trash_dialog_content": "Deseja esvaziar a lixera? Estes arquivos serÃŖo apagados de forma permanente do Immich", - "trash_page_info": "Arquivos na lixeira sÃŖo excluídos de forma permanente apÃŗs {} dias", + "trash_page_info": "Ficheiros na reciclagem irÃŖo ser eliminados permanentemente apÃŗs {days} dias", "trash_page_no_assets": "Lixeira vazia", "trash_page_restore_all": "Restaurar tudo", "trash_page_select_assets_btn": "Selecionar arquivos", - "trash_page_title": "Lixeira ({})", + "trash_page_title": "Reciclagem ({count})", "trashed_items_will_be_permanently_deleted_after": "Os itens da reciclagem sÃŖo eliminados permanentemente apÃŗs {days, plural, one {# dia} other {# dias}}.", "type": "Tipo", + "unable_to_change_pin_code": "NÃŖo foi possível alterar o cÃŗdigo PIN", + "unable_to_setup_pin_code": "NÃŖo foi possível configurar o cÃŗdigo PIN", "unarchive": "Desarquivar", "unarchived_count": "{count, plural, other {NÃŖo arquivado #}}", "unfavorite": "Remover favorito", @@ -1814,6 +1853,7 @@ "untracked_files": "Ficheiros nÃŖo monitorizados", "untracked_files_decription": "Estes ficheiros nÃŖo sÃŖo monitorizados pela aplicaÃ§ÃŖo. Podem ser resultados de falhas numa movimentaÃ§ÃŖo, carregamentos interrompidos, ou deixados para trÃĄs por causa de um problema", "up_next": "A seguir", + "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", "upload_concurrency": "Carregamentos em simultÃĸneo", @@ -1826,15 +1866,18 @@ "upload_status_errors": "Erros", "upload_status_uploaded": "Enviado", "upload_success": "Carregamento realizado com sucesso, atualize a pÃĄgina para ver os novos ficheiros carregados.", - "upload_to_immich": "Enviar para o Immich ({})", + "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", - "url": "URL", "usage": "UtilizaÃ§ÃŖo", + "use_biometric": "Utilizar dados biomÊtricos", "use_current_connection": "usar conexÃŖo atual", "use_custom_date_range": "Utilizar um intervalo de datas personalizado", "user": "Utilizador", + "user_has_been_deleted": "Este utilizador for eliminado.", "user_id": "ID do utilizador", "user_liked": "{user} gostou {type, select, photo {desta fotografia} video {deste video} asset {deste ficheiro} other {disto}}", + "user_pin_code_settings": "CÃŗdigo PIN", + "user_pin_code_settings_description": "Gerir o seu cÃŗdigo PIN", "user_purchase_settings": "Comprar", "user_purchase_settings_description": "Gerir a sua compra", "user_role_set": "Definir {user} como {role}", @@ -1853,7 +1896,7 @@ "version_announcement_overlay_release_notes": "notas da versÃŖo", "version_announcement_overlay_text_1": "OlÃĄ, hÃĄ um novo lançamento de", "version_announcement_overlay_text_2": "por favor, Verifique com calma as ", - "version_announcement_overlay_text_3": "e certifique-se de que a configuraÃ§ÃŖo do docker-compose e do arquivo .env estejam atualizadas para evitar configuraçÃĩes incorretas, especialmente se utiliza o WatchTower ou qualquer outro mecanismo que faça atualizaÃ§ÃŖo automÃĄtica do servidor.", + "version_announcement_overlay_text_3": " e certifique-se de que a configuraÃ§ÃŖo do docker-compose e do ficheiro .env estejam atualizadas para evitar configuraçÃĩes incorretas, especialmente se utilizar o WatchTower ou qualquer outro mecanismo que faça atualizaÃ§ÃŖo automÃĄtica do servidor.", "version_announcement_overlay_title": "Nova versÃŖo do servidor disponível 🎉", "version_history": "HistÃŗrico de versÃĩes", "version_history_item": "Instalado {version} em {date}", @@ -1883,11 +1926,12 @@ "week": "Semana", "welcome": "Bem-vindo(a)", "welcome_to_immich": "Bem-vindo(a) ao Immich", - "wifi_name": "Nome do Wi-Fi", + "wifi_name": "Nome da rede Wi-Fi", + "wrong_pin_code": "CÃŗdigo PIN errado", "year": "Ano", "years_ago": "HÃĄ {years, plural, one {# ano} other {# anos}} atrÃĄs", "yes": "Sim", "you_dont_have_any_shared_links": "NÃŖo tem links partilhados", - "your_wifi_name": "Nome do seu Wi-Fi", + "your_wifi_name": "Nome da sua rede Wi-Fi", "zoom_image": "Ampliar/Reduzir imagem" } diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 982455a697..79f6cdbbc8 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -4,6 +4,7 @@ "account_settings": "ConfiguraçÃĩes da Conta", "acknowledge": "Entendi", "action": "AÃ§ÃŖo", + "action_common_update": "Atualizar", "actions": "AçÃĩes", "active": "Em execuÃ§ÃŖo", "activity": "Atividade", @@ -13,6 +14,7 @@ "add_a_location": "Adicionar uma localizaÃ§ÃŖo", "add_a_name": "Adicionar um nome", "add_a_title": "Adicionar um título", + "add_endpoint": "Adicionar URL", "add_exclusion_pattern": "Adicionar padrÃŖo de exclusÃŖo", "add_import_path": "Adicionar caminho de importaÃ§ÃŖo", "add_location": "Adicionar local", @@ -22,6 +24,8 @@ "add_photos": "Adicionar fotos", "add_to": "Adicionar aâ€Ļ", "add_to_album": "Adicionar ao ÃĄlbum", + "add_to_album_bottom_sheet_added": "Adicionado ao {album}", + "add_to_album_bottom_sheet_already_exists": "JÃĄ existe em {album}", "add_to_shared_album": "Adicionar ao ÃĄlbum compartilhado", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", @@ -35,11 +39,11 @@ "authentication_settings_disable_all": "Tem certeza de que deseja desativar todos os mÊtodos de login? O login serÃĄ completamente desativado.", "authentication_settings_reenable": "Para reabilitar, use um Comando do Servidor.", "background_task_job": "Tarefas em segundo plano", - "backup_database": "Backup do banco de dados", + "backup_database": "Criar backup do banco de dados", "backup_database_enable_description": "Ativar backup do banco de dados", "backup_keep_last_amount": "Quantidade de backups anteriores para manter salvo", "backup_settings": "ConfiguraçÃĩes de backup", - "backup_settings_description": "Gerenciar configuraçÃĩes de backup do banco de dados", + "backup_settings_description": "Gerenciar configuraçÃĩes de backup do banco de dados. Nota: essas tarefas nÃŖo sÃŖo monitoradas e vocÃĒ nÃŖo serÃĄ notificado em caso de falhas.", "check_all": "Selecionar Tudo", "cleanup": "Limpeza", "cleared_jobs": "Tarefas removidas de: {job}", @@ -106,9 +110,9 @@ "library_watching_enable_description": "Observe bibliotecas externas para alteraçÃĩes de arquivos", "library_watching_settings": "ObservaÃ§ÃŖo de biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Observe automaticamente os arquivos alterados", - "logging_enable_description": "Habilitar registro", + "logging_enable_description": "Habilitar logs", "logging_level_description": "Quando ativado, qual nível de log usar.", - "logging_settings": "Registros", + "logging_settings": "Logs", "machine_learning_clip_model": "Modelo CLIP", "machine_learning_clip_model_description": "O nome de um modelo CLIP listado aqui. Lembre-se de executar novamente a tarefa de 'Pesquisa Inteligente' para todas as imagens apÃŗs alterar o modelo.", "machine_learning_duplicate_detection": "DetecÃ§ÃŖo de duplicidade", @@ -139,7 +143,7 @@ "machine_learning_smart_search_enabled_description": "Se desativado, as imagens nÃŖo serÃŖo codificadas para pesquisa inteligente.", "machine_learning_url_description": "A URL do servidor de aprendizado de mÃĄquina. Se mais de uma URL for fornecida, elas serÃŖo tentadas, uma de cada vez e na ordem indicada, atÊ que uma responda com sucesso. Servidores que nÃŖo responderem serÃŖo ignorados temporariamente atÊ voltarem a estar conectados.", "manage_concurrency": "Gerenciar simultaneidade", - "manage_log_settings": "Gerenciar configuraçÃĩes de registro", + "manage_log_settings": "Gerenciar configuraçÃĩes de log", "map_dark_style": "Tema Escuro", "map_enable_description": "Ativar recursos do mapa", "map_gps_settings": "Mapa e ConfiguraçÃĩes de GPS", @@ -188,26 +192,22 @@ "oauth_auto_register": "Registro automÃĄtico", "oauth_auto_register_description": "Registre automaticamente novos usuÃĄrios apÃŗs fazer login com OAuth", "oauth_button_text": "BotÃŖo de texto", - "oauth_client_id": "ID do Cliente", - "oauth_client_secret": "Segredo do cliente", + "oauth_client_secret_description": "ObrigatÃŗrio se PKCE (Proof Key for Code Exchange) nÃŖo for suportado pelo provedor OAuth", "oauth_enable_description": "Faça login com OAuth", - "oauth_issuer_url": "URL do emissor", "oauth_mobile_redirect_uri": "URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override": "SubstituiÃ§ÃŖo de URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo suportar uma URI de aplicativo, por exemplo '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmo de assinatura de perfis", - "oauth_profile_signing_algorithm_description": "Algoritmo usado para assinar o perfil do usuÃĄrio.", - "oauth_scope": "Escopo", "oauth_settings": "OAuth", "oauth_settings_description": "Gerenciar configuraçÃĩes de login do OAuth", "oauth_settings_more_details": "Para mais detalhes sobre este recurso, consulte a documentaÃ§ÃŖo.", - "oauth_signing_algorithm": "Algoritmo de assinatura", "oauth_storage_label_claim": "ReivindicaÃ§ÃŖo de rÃŗtulo de armazenamento", "oauth_storage_label_claim_description": "Defina automaticamente o rÃŗtulo de armazenamento do usuÃĄrio para o valor desta declaraÃ§ÃŖo.", "oauth_storage_quota_claim": "ReivindicaÃ§ÃŖo de cota de armazenamento", "oauth_storage_quota_claim_description": "Defina automaticamente a cota de armazenamento do usuÃĄrio para o valor desta declaraÃ§ÃŖo.", "oauth_storage_quota_default": "Cota de armazenamento padrÃŖo (GiB)", "oauth_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma reivindicaÃ§ÃŖo for fornecida (insira 0 para cota ilimitada).", + "oauth_timeout": "Tempo Limite de RequisiÃ§ÃŖo", + "oauth_timeout_description": "Tempo limite para requisiçÃĩes, em milissegundos", "offline_paths": "Caminhos off-line", "offline_paths_description": "Esses resultados podem ser devidos à exclusÃŖo manual de arquivos que nÃŖo fazem parte de uma biblioteca externa.", "password_enable_description": "Login com e-mail e senha", @@ -367,6 +367,20 @@ "admin_password": "Senha do administrador", "administration": "AdministraÃ§ÃŖo", "advanced": "Avançado", + "advanced_settings_enable_alternate_media_filter_subtitle": "Use esta opÃ§ÃŖo para filtrar mídias durante a sincronizaÃ§ÃŖo com base em critÊrios alternativos. Tente esta opÃ§ÃŖo somente se o aplicativo estiver com problemas para detectar todos os ÃĄlbuns.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar filtro alternativo de sincronizaÃ§ÃŖo de ÃĄlbum de dispositivo", + "advanced_settings_log_level_title": "Nível de log: {level}", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos sÃŖo extremamente lentos para carregar as miniaturas locais. Ative esta opÃ§ÃŖo para preferir imagens do servidor.", + "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", + "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicaçÃĩes com a rede", + "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", + "advanced_settings_self_signed_ssl_subtitle": "Ignora a verificaÃ§ÃŖo do certificado SSL do servidor. ObrigatÃŗrio para certificados auto assinados.", + "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto assinados", + "advanced_settings_sync_remote_deletions_subtitle": "Excluir ou restaurar os arquivos automaticamente neste dispositivo quando essas açÃĩes forem realizada na interface web", + "advanced_settings_sync_remote_deletions_title": "Sincronizar exclusÃĩes remotas [EXPERIMENTAL]", + "advanced_settings_tile_subtitle": "ConfiguraçÃĩes avançadas do usuÃĄrio", + "advanced_settings_troubleshooting_subtitle": "Ativar recursos adicionais para soluÃ§ÃŖo de problemas", + "advanced_settings_troubleshooting_title": "SoluÃ§ÃŖo de problemas", "age_months": "Idade {months, plural, one {# mÃĒs} other {# meses}}", "age_year_months": "Idade 1 ano e {months, plural, one {# mÃĒs} other {# meses}}", "age_years": "{years, plural, other {Idade #}}", @@ -385,18 +399,20 @@ "album_remove_user": "Remover usuÃĄrio?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", "album_share_no_users": "Parece que vocÃĒ jÃĄ compartilhou este ÃĄlbum com todos os usuÃĄrios ou nÃŖo hÃĄ nenhum usuÃĄrio para compartilhar.", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", + "album_thumbnail_card_items": "{count} itens", "album_thumbnail_card_shared": " ¡ Compartilhado", + "album_thumbnail_shared_by": "Compartilhado por {user}", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificaÃ§ÃŖo por e-mail quando um ÃĄlbum compartilhado tiver novos recursos", "album_user_left": "Saiu do ÃĄlbum {album}", "album_user_removed": "UsuÃĄrio {user} foi removido", + "album_viewer_appbar_delete_confirm": "Tem certeza de que deseja excluir este ÃĄlbum da sua conta?", "album_viewer_appbar_share_err_delete": "Falha ao excluir ÃĄlbum", "album_viewer_appbar_share_err_leave": "Falha ao sair do ÃĄlbum", "album_viewer_appbar_share_err_remove": "HÃĄ problemas ao remover recursos do ÃĄlbum", "album_viewer_appbar_share_err_title": "Falha ao alterar o título do ÃĄlbum", "album_viewer_appbar_share_leave": "Sair do ÃĄlbum", + "album_viewer_appbar_share_to": "Compartilhar", "album_viewer_page_share_add_users": "Adicionar usuÃĄrios", "album_with_link_access": "Permitir que qualquer pessoa com o link veja as fotos e as pessoas neste ÃĄlbum.", "albums": "Álbuns", @@ -415,71 +431,107 @@ "api_key_description": "Este valor serÃĄ mostrado apenas uma vez. Por favor, certifique-se de copiÃĄ-lo antes de fechar a janela.", "api_key_empty": "O nome da sua chave de API nÃŖo deve estar vazio", "api_keys": "Chaves de API", + "app_bar_signout_dialog_content": "Tem certeza de que deseja sair?", + "app_bar_signout_dialog_ok": "Sim", + "app_bar_signout_dialog_title": "Sair", "app_settings": "ConfiguraçÃĩes do Aplicativo", "appears_in": "Aparece em", "archive": "Arquivados", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", + "archive_page_no_archived_assets": "Nenhum arquivo encontrado", + "archive_page_title": "Arquivados ({count})", "archive_size": "Tamanho do arquivo", "archive_size_description": "Configure o tamanho do arquivo para baixar (em GiB)", + "archived": "Arquivado", "archived_count": "{count, plural, one {# Arquivado} other {# Arquivados}}", "are_these_the_same_person": "Essas pessoas sÃŖo a mesma pessoa?", "are_you_sure_to_do_this": "Tem certeza de que deseja fazer isso?", + "asset_action_delete_err_read_only": "NÃŖo Ê possível excluir arquivo sÃŗ leitura, ignorando", + "asset_action_share_err_offline": "NÃŖo foi possível obter os arquivos offline, ignorando", "asset_added_to_album": "Adicionado ao ÃĄlbum", "asset_adding_to_album": "Adicionando ao ÃĄlbumâ€Ļ", "asset_description_updated": "A descriÃ§ÃŖo do ativo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} nÃŖo estÃĄ disponível", "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processandoâ€Ļ", + "asset_list_group_by_sub_title": "Agrupar por", + "asset_list_layout_settings_dynamic_layout_title": "Layout dinÃĸmico", + "asset_list_layout_settings_group_automatically": "AutomÃĄtico", + "asset_list_layout_settings_group_by": "Agrupar arquivos por", + "asset_list_layout_settings_group_by_month_day": "MÃĒs + dia", + "asset_list_settings_subtitle": "ConfiguraçÃĩes de layout da grade de fotos", + "asset_list_settings_title": "Grade de Fotos", "asset_offline": "Arquivo indisponível", "asset_offline_description": "Este arquivo externo nÃŖo estÃĄ mais disponível. Contate seu administrador do Immich para obter ajuda.", + "asset_restored_successfully": "Arquivo restaurado", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na lixeira", "asset_uploaded": "Carregado", "asset_uploading": "Carregandoâ€Ļ", + "asset_viewer_settings_subtitle": "Gerenciar as configuraçÃĩes do visualizador da galeria", + "asset_viewer_settings_title": "Visualizador de Mídia", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao ÃĄlbum", "assets_added_to_name_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} {hasName, select, true {ao ÃĄlbum {name}} other {em um novo ÃĄlbum}}", "assets_count": "{count, plural, one {# arquivo} other {# arquivos}}", + "assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente", + "assets_deleted_permanently_from_server": "{count} arquivo(s) deletado(s) permanentemente do servidor Immich", "assets_moved_to_trash_count": "{count, plural, one {# arquivo movido} other {# arquivos movidos}} para a lixeira", "assets_permanently_deleted_count": "{count, plural, one {# arquivo excluído permanentemente} other {# arquivos excluídos permanentemente}}", "assets_removed_count": "{count, plural, one {# arquivo removido} other {# arquivos removidos}}", + "assets_removed_permanently_from_device": "{count} arquivo(s) removido(s) permanentemente do seu dispositivo", "assets_restore_confirmation": "Tem certeza de que deseja restaurar todos os seus arquivos na lixeira? Esta aÃ§ÃŖo nÃŖo pode ser desfeita! Nota: Arquivos externos nÃŖo podem ser restaurados desta maneira.", "assets_restored_count": "{count, plural, one {# arquivo restaurado} other {# arquivos restaurados}}", + "assets_restored_successfully": "{count} arquivo(s) restaurado(s)", + "assets_trashed": "{count} arquivo enviado para a lixeira", "assets_trashed_count": "{count, plural, one {# arquivo movido para a lixeira} other {# arquivos movidos para a lixeira}}", + "assets_trashed_from_server": "{count} arquivos foram enviados para a lixeira", "assets_were_part_of_album_count": "{count, plural, one {O arquivo jÃĄ faz} other {Os arquivos jÃĄ fazem}} parte do ÃĄlbum", "authorized_devices": "Dispositivos Autorizados", + "automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexÃĩes alternativas em outras redes", + "automatic_endpoint_switching_title": "Troca automÃĄtica de URL", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", - "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({})", + "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", + "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissÃŖo de localizaÃ§ÃŖo precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vÃĄrios ÃĄlbuns. Assim, os ÃĄlbuns podem ser incluídos ou excluídos durante o processo de backup.", "backup_album_selection_page_select_albums": "Selecionar ÃĄlbuns", "backup_album_selection_page_selection_info": "InformaçÃĩes da SeleÃ§ÃŖo", "backup_album_selection_page_total_assets": "Total de recursos exclusivos", "backup_all": "Todos", - "backup_background_service_current_upload_notification": "Enviando {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", + "backup_background_service_backup_failed_message": "Falha ao fazer backup. Tentando novamenteâ€Ļ", + "backup_background_service_connection_failed_message": "Falha na conexÃŖo com o servidor. Tentando novamenteâ€Ļ", + "backup_background_service_current_upload_notification": "Enviando {filename}", + "backup_background_service_default_notification": "Verificando se hÃĄ novos arquivosâ€Ļ", + "backup_background_service_error_title": "Erro no backup", "backup_background_service_in_progress_notification": "Fazendo backup de seus ativosâ€Ļ", - "backup_background_service_upload_failure_notification": "Falha ao carregar {}", + "backup_background_service_upload_failure_notification": "Falha ao enviar {filename}", "backup_controller_page_albums": "Álbuns de backup", + "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualizaÃ§ÃŖo da aplicaÃ§ÃŖo em segundo plano em ConfiguraçÃĩes > Geral > AtualizaÃ§ÃŖo em 2Âē plano.", + "backup_controller_page_background_app_refresh_disabled_title": "AtualizaÃ§ÃŖo em 2Âē plano desativada", + "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configuraçÃĩes", + "backup_controller_page_background_battery_info_link": "Mostre-me como", + "backup_controller_page_background_battery_info_message": "Para uma melhor experiÃĒncia de backup em segundo plano, desative todas as otimizaçÃĩes de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso Ê específico por dispositivo, consulte as informaçÃĩes de como fazer isso com o fabricante do seu dispositivo.", + "backup_controller_page_background_battery_info_title": "OtimizaçÃĩes de bateria", "backup_controller_page_background_charging": "Apenas durante o carregamento", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", + "backup_controller_page_background_delay": "Adiar backup de novos arquivos: {duration}", "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automÃĄtico de novos ativos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automÃĄtico em segundo plano estÃĄ desativado", "backup_controller_page_background_is_on": "O backup automÃĄtico em segundo plano estÃĄ ativado", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", - "backup_controller_page_background_wifi": "Apenas em Wi-Fi", - "backup_controller_page_backup": "Backup", + "backup_controller_page_background_wifi": "Apenas no Wi-Fi", "backup_controller_page_backup_selected": "Selecionado: ", "backup_controller_page_backup_sub": "Backup de fotos e vídeos", - "backup_controller_page_created": "Criado em: {}", + "backup_controller_page_created": "Criado em: {date}", "backup_controller_page_desc_backup": "Ative o backup para carregar automaticamente novos ativos no servidor.", "backup_controller_page_excluded": "Excluído: ", - "backup_controller_page_failed": "Falhou ({})", - "backup_controller_page_filename": "Nome do arquivo: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Falhou ({count})", + "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", "backup_controller_page_info": "InformaçÃĩes de backup", "backup_controller_page_none_selected": "Nenhum selecionado", "backup_controller_page_remainder": "Restante", @@ -488,7 +540,7 @@ "backup_controller_page_start_backup": "Iniciar backup", "backup_controller_page_status_off": "O backup estÃĄ desativado", "backup_controller_page_status_on": "O backup estÃĄ ativado", - "backup_controller_page_storage_format": "{} de {} usado", + "backup_controller_page_storage_format": "{used} de {total} usados", "backup_controller_page_to_backup": "Álbuns para backup", "backup_controller_page_total_sub": "Todas as fotos e vídeos Ãēnicos dos ÃĄlbuns selecionados", "backup_controller_page_turn_off": "Desativar o backup", @@ -496,6 +548,12 @@ "backup_controller_page_uploading_file_info": "Carregando informaçÃĩes do arquivo", "backup_err_only_album": "NÃŖo Ê possível remover o Ãēnico ÃĄlbum", "backup_info_card_assets": "ativos", + "backup_manual_cancelled": "Cancelado", + "backup_manual_in_progress": "Envio jÃĄ estÃĄ em progresso. Tente novamente mais tarde", + "backup_manual_success": "Sucesso", + "backup_manual_title": "Estado do envio", + "backup_options_page_title": "OpçÃĩes de backup", + "backup_setting_subtitle": "Gerenciar as configuraçÃĩes de envio em primeiro e segundo plano", "backward": "Para trÃĄs", "birthdate_saved": "Data de nascimento salva com sucesso", "birthdate_set_description": "A data de nascimento Ê usada para calcular a idade da pessoa no momento em que a foto foi tirada.", @@ -507,25 +565,53 @@ "bulk_keep_duplicates_confirmation": "Tem certeza de que deseja manter {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso resolverÃĄ todos os grupos duplicados sem excluir nada.", "bulk_trash_duplicates_confirmation": "Tem a certeza de que deseja mover para a lixeira {count, plural, one {# arquivo duplicado} other {# arquivos duplicados}}? Isso manterÃĄ o maior arquivo de cada grupo e moverÃĄ para a lixeira todas as outras duplicidades.", "buy": "Comprar o Immich", + "cache_settings_album_thumbnails": "Miniaturas da biblioteca ({count} arquivos)", + "cache_settings_clear_cache_button": "Limpar o cache", + "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetarÃĄ significativamente o desempenho do aplicativo atÊ que o cache seja reconstruído.", + "cache_settings_duplicated_assets_clear_button": "LIMPAR", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que sÃŖo bloqueados pelo app", + "cache_settings_duplicated_assets_title": "Arquivos duplicados ({count})", + "cache_settings_image_cache_size": "Tamanho do cache de imagens ({count} arquivos)", + "cache_settings_statistics_album": "Miniaturas da biblioteca", + "cache_settings_statistics_assets": "{count} arquivos ({size})", + "cache_settings_statistics_full": "Imagens completas", + "cache_settings_statistics_shared": "Miniaturas de ÃĄlbuns compartilhados", + "cache_settings_statistics_thumbnail": "Miniaturas", + "cache_settings_statistics_title": "Uso do cache", + "cache_settings_subtitle": "Controle o comportamento de cache do aplicativo Immich", + "cache_settings_thumbnail_size": "Tamanho do cache de miniaturas ({count} arquivos)", + "cache_settings_tile_subtitle": "Controle o comportamento do armazenamento local", + "cache_settings_tile_title": "Armazenamento Local", + "cache_settings_title": "ConfiguraçÃĩes de cache", "camera": "CÃĸmera", "camera_brand": "Marca da cÃĸmera", "camera_model": "Modelo da cÃĸmera", "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", + "canceled": "Cancelado", "cannot_merge_people": "NÃŖo Ê possível mesclar pessoas", "cannot_undo_this_action": "VocÃĒ nÃŖo pode desfazer esta aÃ§ÃŖo!", "cannot_update_the_description": "NÃŖo Ê possível atualizar a descriÃ§ÃŖo", "change_date": "Alterar data", + "change_display_order": "Alterar ordem de exibiÃ§ÃŖo", "change_expiration_time": "Alterar o prazo de validade", "change_location": "Alterar localizaÃ§ÃŖo", "change_name": "Alterar nome", "change_name_successfully": "Nome alterado com sucesso", "change_password": "Mudar a senha", "change_password_description": "Esta Ê a primeira vez que vocÃĒ estÃĄ acessando o sistema ou foi feita uma solicitaÃ§ÃŖo para alterar sua senha. Por favor, insira a nova senha abaixo.", + "change_password_form_confirm_password": "Confirme a senha", + "change_password_form_description": "OlÃĄ {name},\n\nEsta Ê a primeira vez que vocÃĒ estÃĄ acessando o sistema ou foi feita uma solicitaÃ§ÃŖo para alterar sua senha. Por favor, insira a nova senha abaixo.", + "change_password_form_new_password": "Nova Senha", + "change_password_form_password_mismatch": "As senhas nÃŖo estÃŖo iguais", + "change_password_form_reenter_new_password": "Confirme a nova senha", "change_your_password": "Alterar sua senha", "changed_visibility_successfully": "Visibilidade alterada com sucesso", "check_all": "Verificar tudo", - "check_logs": "Verificar registros", + "check_corrupt_asset_backup": "Verifique se hÃĄ backups corrompidos", + "check_corrupt_asset_backup_button": "Verificar", + "check_corrupt_asset_backup_description": "Execute esta verificaÃ§ÃŖo somente em uma rede Wi-Fi e quando o backup de todos os arquivos jÃĄ estiver concluído. O processo demora alguns minutos.", + "check_logs": "Ver logs", "choose_matching_people_to_merge": "Escolha pessoas correspondentes para mesclar", "city": "Cidade", "clear": "Limpar", @@ -533,6 +619,13 @@ "clear_all_recent_searches": "Limpar todas as buscas recentes", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", + "client_cert_enter_password": "Digite a senha", + "client_cert_import": "Importar", + "client_cert_import_success_msg": "Certificado do cliente importado", + "client_cert_invalid_msg": "Arquivo de certificado invÃĄlido ou senha errada", + "client_cert_remove_msg": "Certificado do cliente removido", + "client_cert_subtitle": "Suporta apenas o formato PKCS12 (.p12, .pfx). A importaÃ§ÃŖo/remoÃ§ÃŖo de certificados estÃĄ disponível somente antes do login", + "client_cert_title": "Certificado de Cliente SSL", "clockwise": "HorÃĄrio", "close": "Fechar", "collapse": "Recolher", @@ -543,15 +636,27 @@ "comment_options": "OpçÃĩes de comentÃĄrio", "comments_and_likes": "ComentÃĄrios e curtidas", "comments_are_disabled": "ComentÃĄrios estÃŖo desativados", + "common_create_new_album": "Criar novo ÃĄlbum", + "common_server_error": "Verifique a sua conexÃŖo de rede, certifique-se de que o servidor estÃĄ acessível e de que as versÃĩes do aplicativo e servidor sÃŖo compatíveis.", + "completed": "Sucesso", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", "confirm_delete_face": "Tem certeza que deseja remover a face de {name} deste arquivo?", "confirm_delete_shared_link": "Tem certeza de que deseja excluir este link compartilhado?", - "confirm_keep_this_delete_others": "Todos os outros arquivos da pilha serÃŖo excluídos, exceto este arquivo. Tem certeza de que deseja continuar?", + "confirm_keep_this_delete_others": "Todos os outros arquivos do grupo serÃŖo excluídos, exceto este arquivo. Tem certeza de que deseja continuar?", "confirm_password": "Confirme a senha", "contain": "Caber", "context": "Contexto", "continue": "Continuar", + "control_bottom_app_bar_album_info_shared": "{count} arquivos ¡ Compartilhado", + "control_bottom_app_bar_create_new_album": "Criar novo ÃĄlbum", + "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", + "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", + "control_bottom_app_bar_edit_location": "Editar LocalizaÃ§ÃŖo", + "control_bottom_app_bar_edit_time": "Editar data e hora", + "control_bottom_app_bar_share_link": "Compartilhar Link", + "control_bottom_app_bar_share_to": "Compartilhar", + "control_bottom_app_bar_trash_from_immich": "Mover para a Lixeira", "copied_image_to_clipboard": "Imagem copiada para a ÃĄrea de transferÃĒncia.", "copied_to_clipboard": "Copiado para a ÃĄrea de transferÃĒncia!", "copy_error": "Copiar erro", @@ -571,6 +676,7 @@ "create_link": "Criar link", "create_link_to_share": "Criar link para partilhar", "create_link_to_share_description": "Permitir que qualquer pessoa com o link veja a(s) foto(s) selecionada(s)", + "create_new": "CRIAR NOVO", "create_new_person": "Criar nova pessoa", "create_new_person_hint": "Atribuir arquivos selecionados a uma nova pessoa", "create_new_user": "Criar novo usuÃĄrio", @@ -580,16 +686,19 @@ "create_tag_description": "Cria um novo marcador. Para marcadores multi nível, digite o caminho completo do marcador, inclusive as barras.", "create_user": "Criar usuÃĄrio", "created": "Criado", + "crop": "Cortar", + "curated_object_page_title": "Objetos", "current_device": "Dispositivo atual", + "current_server_address": "Endereço atual do servidor", "custom_locale": "LocalizaÃ§ÃŖo Customizada", "custom_locale_description": "Formatar datas e nÃēmeros baseados na linguagem e regiÃŖo", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "E, dd MMM", + "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", "date_after": "Data apÃŗs", "date_and_time": "Data e Hora", "date_before": "Data antes", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "E, d LLL, y â€ĸ h:mm a", "date_of_birth_saved": "Data de nascimento salvo com sucesso", "date_range": "Intervalo de datas", "day": "Dia", @@ -604,25 +713,33 @@ "delete_album": "Excluir ÃĄlbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serÃŖo excluídos permanentemente do Immich e do seu dispositivo", + "delete_dialog_alert_local": "Estes arquivos serÃŖo permanentemente excluídos do seu dispositivo, mas continuarÃŖo disponíveis no servidor Immich", + "delete_dialog_alert_local_non_backed_up": "NÃŖo hÃĄ backup de alguns dos arquivos no servidor e eles serÃŖo excluídos permanentemente do seu dispositivo", + "delete_dialog_alert_remote": "Esses arquivos serÃŖo excluídos permanentemente do servidor Immich", + "delete_dialog_ok_force": "Excluir mesmo assim", "delete_dialog_title": "Excluir permanentemente", "delete_duplicates_confirmation": "Tem certeza de que deseja excluir permanentemente estas duplicidades?", "delete_face": "Remover face", "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", + "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup feito", + "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir restante", "delete_shared_link": "Excluir link de compartilhamento", + "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Remover marcador", "delete_tag_confirmation_prompt": "Tem certeza que deseja excluir o marcador {tagName} ?", "delete_user": "Excluir usuÃĄrio", "deleted_shared_link": "Link de compartilhamento excluído", "deletes_missing_assets": "Excluir arquivos nÃŖo encontrados", "description": "DescriÃ§ÃŖo", + "description_input_hint_text": "Adicionar descriÃ§ÃŖo...", + "description_input_submit_error": "Erro ao atualizar a descriÃ§ÃŖo, verifique o log para mais detalhes", "details": "Detalhes", "direction": "DireÃ§ÃŖo", "disabled": "Desativado", "disallow_edits": "NÃŖo permitir ediçÃĩes", - "discord": "Discord", "discover": "Descobrir", "dismiss_all_errors": "Dispensar todos os erros", "dismiss_error": "Dispensar erro", @@ -634,12 +751,26 @@ "documentation": "DocumentaÃ§ÃŖo", "done": "Feito", "download": "Baixar", + "download_canceled": "Cancelado", + "download_complete": "Sucesso", + "download_enqueue": "Na fila", + "download_error": "Erro ao baixar", + "download_failed": "Falha", + "download_filename": "arquivo: {filename}", + "download_finished": "Concluído", "download_include_embedded_motion_videos": "Vídeos inclusos", "download_include_embedded_motion_videos_description": "Baixar os vídeos inclusos de uma foto em movimento em um arquivo separado", + "download_notfound": "NÃŖo encontrado", + "download_paused": "Pausado", "download_settings": "Baixar", "download_settings_description": "Gerenciar configuraçÃĩes relacionadas a transferÃĒncia de arquivos", + "download_started": "Baixando", + "download_sucess": "Baixado com sucesso", + "download_sucess_android": "O arquivo foi salvo em DCIM/Immich", + "download_waiting_to_retry": "Aguardando para tentar novamente", "downloading": "Baixando", "downloading_asset_filename": "Baixando arquivo {filename}", + "downloading_media": "Baixando mídia", "drop_files_to_upload": "Solte arquivos em qualquer lugar para carregar", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, sÃŖo duplicados", @@ -656,6 +787,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar LocalizaÃ§ÃŖo", + "edit_location_dialog_title": "LocalizaÃ§ÃŖo", "edit_name": "Editar nome", "edit_people": "Editar pessoas", "edit_tag": "Editar marcador", @@ -668,14 +800,19 @@ "editor_crop_tool_h2_aspect_ratios": "ProporçÃĩes", "editor_crop_tool_h2_rotation": "RotaÃ§ÃŖo", "email": "E-mail", + "empty_folder": "A pasta estÃĄ vazia", "empty_trash": "Esvaziar lixo", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerÃĄ permanentemente do Immich todos os arquivos que estÃŖo na lixeira.\nVocÃĒ nÃŖo pode desfazer esta aÃ§ÃŖo!", "enable": "Habilitar", "enabled": "Habilitado", "end_date": "Data final", + "enqueued": "Na fila", + "enter_wifi_name": "Digite o nome do Wi-Fi", "error": "Erro", + "error_change_sort_album": "Falha ao alterar a ordem de exibiÃ§ÃŖo", "error_delete_face": "Erro ao remover face do arquivo", "error_loading_image": "Erro ao carregar a pÃĄgina", + "error_saving_image": "Erro: {error}", "error_title": "Erro - Algo deu errado", "errors": { "cannot_navigate_next_asset": "NÃŖo foi possível navegar para o prÃŗximo arquivo", @@ -705,10 +842,12 @@ "failed_to_keep_this_delete_others": "Falha ao manter este arquivo e excluir os outros", "failed_to_load_asset": "NÃŖo foi possível carregar o ativo", "failed_to_load_assets": "NÃŖo foi possível carregar os ativos", + "failed_to_load_notifications": "Falha ao carregar notificaçÃĩes", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover a chave do produto", - "failed_to_stack_assets": "Falha ao empilhar arquivos", - "failed_to_unstack_assets": "Falha ao desempilhar arquivos", + "failed_to_stack_assets": "Falha ao agrupar arquivos", + "failed_to_unstack_assets": "Falha ao remover arquivos do grupo", + "failed_to_update_notification_status": "Falha ao atualizar o status da notificaÃ§ÃŖo", "import_path_already_exists": "Este caminho de importaÃ§ÃŖo jÃĄ existe.", "incorrect_email_or_password": "E-mail ou senha incorretos", "paths_validation_failed": "A validaÃ§ÃŖo de {paths, plural, one {# caminho falhou} other {# caminhos falharam}}", @@ -803,12 +942,20 @@ "unable_to_update_user": "NÃŖo foi possível atualizar o usuÃĄrio", "unable_to_upload_file": "NÃŖo foi possível carregar o arquivo" }, - "exif": "Exif", "exif_bottom_sheet_description": "Adicionar descriÃ§ÃŖo...", "exif_bottom_sheet_details": "DETALHES", "exif_bottom_sheet_location": "LOCALIZAÇÃO", + "exif_bottom_sheet_people": "PESSOAS", + "exif_bottom_sheet_person_add_person": "Adicionar nome", + "exif_bottom_sheet_person_age": "Idade {age}", + "exif_bottom_sheet_person_age_months": "Idade {months} meses", + "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", + "exif_bottom_sheet_person_age_years": "Idade {years}", "exit_slideshow": "Sair da apresentaÃ§ÃŖo", "expand_all": "Expandir tudo", + "experimental_settings_new_asset_list_subtitle": "Em andamento", + "experimental_settings_new_asset_list_title": "Ativar grade de fotos experimental", + "experimental_settings_subtitle": "Use por sua conta e risco!", "expire_after": "Expira depois", "expired": "Expirou", "expires_date": "Expira em {date}", @@ -819,11 +966,16 @@ "extension": "ExtensÃŖo", "external": "Externo", "external_libraries": "Bibliotecas externas", + "external_network": "Rede externa", + "external_network_sheet_info": "Quando nÃŖo estiver na rede Wi-Fi especificada, o aplicativo irÃĄ se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo", "face_unassigned": "Sem nome", + "failed": "Falhou", "failed_to_load_assets": "Falha ao carregar arquivos", + "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", "favorite_or_unfavorite_photo": "Marque ou desmarque a foto como favorita", "favorites": "Favoritos", + "favorites_page_no_favorites": "Nenhuma mídia favorita encontrada", "feature_photo_updated": "Foto principal atualizada", "features": "Funcionalidades", "features_setting_description": "Gerenciar as funcionalidades da aplicaÃ§ÃŖo", @@ -831,25 +983,39 @@ "file_name_or_extension": "Nome do arquivo ou extensÃŖo", "filename": "Nome do arquivo", "filetype": "Tipo de arquivo", + "filter": "Filtro", "filter_people": "Filtrar pessoas", + "filter_places": "Filtrar lugares", "find_them_fast": "Encontre pelo nome em uma pesquisa", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", + "folder": "Pasta", + "folder_not_found": "Pasta nÃŖo encontrada", "folders": "Pastas", "folders_feature_description": "Navegar pelas pastas das fotos e vídeos no sistema de arquivos", "forward": "Para frente", "general": "Geral", "get_help": "Obter Ajuda", + "get_wifiname_error": "NÃŖo foi possível obter o nome do Wi-Fi. Verifique se concedeu as permissÃĩes necessÃĄrias e se estÃĄ conectado a uma rede Wi-Fi", "getting_started": "Primeiros passos", "go_back": "Voltar", "go_to_folder": "Ir para a pasta", "go_to_search": "Ir para a pesquisa", + "grant_permission": "Conceder permissÃŖo", "group_albums_by": "Agrupar ÃĄlbuns por...", "group_country": "Agrupar por país", "group_no": "Sem agrupamento", "group_owner": "Agrupar por dono", "group_places_by": "Agrupar lugares por...", "group_year": "Agrupar por ano", + "haptic_feedback_switch": "Ativar vibraÃ§ÃŖo", + "haptic_feedback_title": "VibraÃ§ÃŖo", "has_quota": "Cota", + "header_settings_add_header_tip": "Adicionar Cabeçalho", + "header_settings_field_validator_msg": "O valor nÃŖo pode estar vazio", + "header_settings_header_name_input": "Nome do cabeçalho", + "header_settings_header_value_input": "Valor do cabeçalho", + "headers_settings_tile_subtitle": "Defina cabeçalhos de proxy que o aplicativo deve enviar com cada solicitaÃ§ÃŖo de rede", + "headers_settings_tile_title": "Cabeçalhos de proxy personalizados", "hi_user": "OlÃĄ {name} ({email})", "hide_all_people": "Esconder todas as pessoas", "hide_gallery": "Ocultar galeria", @@ -857,8 +1023,23 @@ "hide_password": "Ocultar senha", "hide_person": "Ocultar pessoa", "hide_unnamed_people": "Esconder pessoas sem nome", - "host": "Host", + "home_page_add_to_album_conflicts": "{added} arquivos adicionados ao ÃĄlbum {album}. {failed} arquivos jÃĄ estÃŖo no ÃĄlbum.", + "home_page_add_to_album_err_local": "Ainda nÃŖo Ê possível adicionar arquivos locais aos ÃĄlbuns, ignorando", + "home_page_add_to_album_success": "{added} arquivos adicionados ao ÃĄlbum {album}.", + "home_page_album_err_partner": "Ainda nÃŖo Ê possível adicionar arquivos de parceiros a um ÃĄlbum, ignorando", + "home_page_archive_err_local": "Ainda nÃŖo Ê possível arquivar mídias locais, ignorando", + "home_page_archive_err_partner": "NÃŖo Ê possível arquivar as mídias do parceiro, ignorando", + "home_page_building_timeline": "Construindo a linha do tempo", + "home_page_delete_err_partner": "NÃŖo Ê possível excluir arquivos do parceiro, ignorando", + "home_page_delete_remote_err_local": "Foram selecionados arquivos locais para excluir remotamente, ignorando", + "home_page_favorite_err_local": "Ainda nÃŖo Ê possível adicionar arquivos locais aos favoritos, ignorando", + "home_page_favorite_err_partner": "Ainda nÃŖo Ê possível marcar arquivos do parceiro como favoritos, ignorando", + "home_page_first_time_notice": "Se Ê a primeira vez que utiliza o aplicativo, certifique-se de marcar um ou mais ÃĄlbuns do dispositivo para backup, assim a linha do tempo serÃĄ preenchida com as fotos e vídeos", + "home_page_share_err_local": "NÃŖo Ê possível compartilhar arquivos locais com um link, ignorando", + "home_page_upload_err_limit": "SÃŗ Ê possível enviar 30 arquivos de cada vez, ignorando", "hour": "Hora", + "ignore_icloud_photos": "Ignorar fotos do iCloud", + "ignore_icloud_photos_description": "Fotos que estÃŖo armazenadas no iCloud nÃŖo serÃŖo enviadas para o servidor do Immich", "image": "Imagem", "image_alt_text_date": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} com {person1} em {date}", @@ -870,6 +1051,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1} e {person2} em {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e {person3} em {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e {additionalCount, number} outros em {date}", + "image_saved_successfully": "Imagem salva", + "image_viewer_page_state_provider_download_started": "Baixando arquivo", + "image_viewer_page_state_provider_download_success": "Baixado com sucesso", + "image_viewer_page_state_provider_share_error": "Erro ao compartilhar", "immich_logo": "Logo do Immich", "immich_web_interface": "Interface Web do Immich", "import_from_json": "Importar do JSON", @@ -888,6 +1073,8 @@ "night_at_midnight": "Toda noite, meia noite", "night_at_twoam": "Toda noite, 2am" }, + "invalid_date": "Data invÃĄlida", + "invalid_date_format": "Formato de data invÃĄlido", "invite_people": "Convidar Pessoas", "invite_to_album": "Convidar para o ÃĄlbum", "items_count": "{count, plural, one {# item} other {# itens}}", @@ -901,14 +1088,18 @@ "language_setting_description": "Selecione seu Idioma preferido", "last_seen": "Visto pela ultima vez", "latest_version": "VersÃŖo mais recente", - "latitude": "Latitude", "leave": "Sair", "lens_model": "Modelo da lente", "let_others_respond": "Permitir respostas", "level": "Nível", "library": "Biblioteca", "library_options": "OpçÃĩes da biblioteca", + "library_page_device_albums": "Álbuns no Dispositivo", "library_page_new_album": "Novo album", + "library_page_sort_asset_count": "Quantidade de arquivos", + "library_page_sort_created": "Data de criaÃ§ÃŖo", + "library_page_sort_last_modified": "Última modificaÃ§ÃŖo", + "library_page_sort_title": "Título do ÃĄlbum", "light": "Claro", "like_deleted": "Curtida excluída", "link_motion_video": "Relacionar video animado", @@ -918,25 +1109,42 @@ "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local_network": "Rede local", + "local_network_sheet_info": "O aplicativo irÃĄ se conectar ao servidor atravÊs desta URL quando estiver na rede Wi-Fi especificada", + "location_permission": "PermissÃŖo de localizaÃ§ÃŖo", + "location_permission_content": "Para utilizar a funÃ§ÃŖo de troca automÃĄtica de URL Ê necessÃĄrio a permissÃŖo de localizaÃ§ÃŖo precisa, para que seja possível ler o nome da rede Wi-Fi", + "location_picker_choose_on_map": "Escolha no mapa", + "location_picker_latitude_error": "Digite uma latitude vÃĄlida", + "location_picker_latitude_hint": "Digite a latitude", + "location_picker_longitude_error": "Digite uma longitude vÃĄlida", + "location_picker_longitude_hint": "Digite a longitude", "log_out": "Sair", "log_out_all_devices": "Sair de todos dispositivos", "logged_out_all_devices": "Saiu de todos os dispositivos", "logged_out_device": "Dispositivo desconectado", "login": "Iniciar sessÃŖo", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", + "login_disabled": "Login desativado", + "login_form_api_exception": "Erro de API. Verifique a URL do servidor e tente novamente.", + "login_form_back_button_text": "Voltar", + "login_form_endpoint_hint": "http://ip-do-seu-servidor:porta", + "login_form_endpoint_url": "URL do servidor", + "login_form_err_http": "Por favor especifique http:// ou https://", "login_form_err_invalid_email": "E-mail invÃĄlido", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", + "login_form_err_invalid_url": "URL InvÃĄlida", + "login_form_err_leading_whitespace": "HÃĄ um espaço em branco no início", + "login_form_err_trailing_whitespace": "HÃĄ um espaço em branco no fim", + "login_form_failed_get_oauth_server_config": "Erro de login com OAuth, verifique a URL do servidor", + "login_form_failed_get_oauth_server_disable": "O recurso OAuth nÃŖo estÃĄ disponível neste servidor", "login_form_failed_login": "Erro ao fazer login, verifique a url do servidor, e-mail e senha", - "login_form_password_hint": "password", + "login_form_handshake_exception": "Houve um erro de autorizaÃ§ÃŖo com o servidor. Se estiver utilizando um certificado auto assinado, ative o suporte a isso nas configuraçÃĩes.", "login_form_save_login": "Permaneçer conectado", + "login_form_server_empty": "Digite a URL do servidor.", + "login_form_server_error": "NÃŖo foi possível conectar ao servidor.", "login_has_been_disabled": "Login foi desativado.", + "login_password_changed_error": "Erro ao atualizar a sua senha", + "login_password_changed_success": "Senha atualizada com sucesso", "logout_all_device_confirmation": "Tem certeza de que deseja sair de todos os dispositivos?", "logout_this_device_confirmation": "Tem certeza de que deseja sair deste dispositivo?", - "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ative para repetir os vídeos automaticamente durante a exibiÃ§ÃŖo.", @@ -951,16 +1159,43 @@ "manage_your_devices": "Gerenciar seus dispositivos logados", "manage_your_oauth_connection": "Gerenciar sua conexÃŖo OAuth", "map": "Mapa", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} fotos", + "map_cannot_get_user_location": "NÃŖo foi possível obter a sua localizaÃ§ÃŖo", + "map_location_dialog_yes": "Sim", + "map_location_picker_page_use_location": "Use esta localizaÃ§ÃŖo", + "map_location_service_disabled_content": "O serviço de localizaÃ§ÃŖo precisa estar ativado para exibir os arquivos da sua localizaÃ§ÃŖo atual. Deseja ativar agora?", + "map_location_service_disabled_title": "Serviço de localizaÃ§ÃŖo desativado", "map_marker_for_images": "Marcador de mapa para imagens tiradas em {city}, {country}", "map_marker_with_image": "Marcador de mapa com imagem", + "map_no_assets_in_bounds": "NÃŖo hÃĄ fotos nesta ÃĄrea", + "map_no_location_permission_content": "É necessÃĄria a permissÃŖo de localizaÃ§ÃŖo para exibir os arquivos da sua localizaÃ§ÃŖo atual. Deseja conceder a permissÃŖo agora?", + "map_no_location_permission_title": "PermissÃŖo de localizaÃ§ÃŖo foi negada", "map_settings": "DefiniçÃĩes do mapa", + "map_settings_dark_mode": "Modo escuro", + "map_settings_date_range_option_day": "Últimas 24 horas", + "map_settings_date_range_option_days": "Últimos {days} dias", + "map_settings_date_range_option_year": "Último ano", + "map_settings_date_range_option_years": "Últimos {years} anos", + "map_settings_dialog_title": "ConfiguraçÃĩes do mapa", + "map_settings_include_show_archived": "Incluir arquivados", + "map_settings_include_show_partners": "Incluir parceiros", + "map_settings_only_show_favorites": "Mostrar apenas favoritos", + "map_settings_theme_settings": "Tema do mapa", + "map_zoom_to_see_photos": "Diminua o zoom para ver mais fotos", + "mark_all_as_read": "Marcar tudo como lido", + "mark_as_read": "Marcar como lido", + "marked_all_as_read": "Tudo marcado como lido", "matches": "CorrespondÃĒncias", "media_type": "Tipo de mídia", "memories": "MemÃŗrias", + "memories_all_caught_up": "Finalizamos por hoje", + "memories_check_back_tomorrow": "Volte amanhÃŖ para ver mais lembranças", "memories_setting_description": "Gerencie o que vÃĒ em suas memÃŗrias", + "memories_start_over": "Ver de novo", + "memories_swipe_to_close": "Deslize para cima para fechar", "memory": "MemÃŗria", "memory_lane_title": "Trilha das RecordaçÃĩes {title}", - "menu": "Menu", "merge": "Mesclar", "merge_people": "Mesclar pessoas", "merge_people_limit": "SÃŗ Ê possível mesclar atÊ 5 pessoas de uma sÃŗ vez", @@ -972,13 +1207,18 @@ "missing": "Faltando", "model": "Modelo", "month": "MÃĒs", - "monthly_title_text_date_format": "MMMM y", "more": "Mais", + "moved_to_archive": "{count, plural, one {# mídia foi arquivada} other {# mídias foram arquivadas}}", + "moved_to_library": "{count, plural, one {# arquivo foi enviado} other {# arquivos foram enviados}} à biblioteca", "moved_to_trash": "Enviado para a lixeira", + "multiselect_grid_edit_date_time_err_read_only": "NÃŖo Ê possível editar a data do(s) arquivo(s) somente leitura, pulando", + "multiselect_grid_edit_gps_err_read_only": "NÃŖo Ê possível editar a localizaÃ§ÃŖo dos arquivos somente leitura, ignorando", "mute_memories": "Silenciar memÃŗrias", "my_albums": "Meus Álbuns", "name": "Nome", "name_or_nickname": "Nome ou apelido", + "networking_settings": "ConexÃĩes", + "networking_subtitle": "Gerencie as conexÃĩes ao servidor", "never": "Nunca", "new_album": "Novo Álbum", "new_api_key": "Nova Chave de API", @@ -995,43 +1235,48 @@ "no_albums_yet": "Parece que vocÃĒ ainda nÃŖo tem nenhum ÃĄlbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualizaÃ§ÃŖo de fotos", "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", + "no_assets_to_show": "NÃŖo hÃĄ arquivos para exibir", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", "no_exif_info_available": "Sem informaçÃĩes exif disponíveis", "no_explore_results_message": "Carregue mais fotos para explorar sua coleÃ§ÃŖo.", "no_favorites_message": "Adicione aos favoritos para encontrar suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver suas fotos e vídeos", "no_name": "Sem Nome", + "no_notifications": "Nenhuma notificaÃ§ÃŖo", + "no_people_found": "Nenhuma pessoa encontrada", "no_places": "Sem lugares", "no_results": "Sem resultados", "no_results_description": "Tente um sinônimo ou uma palavra-chave mais geral", "no_shared_albums_message": "Crie um ÃĄlbum para compartilhar fotos e vídeos com pessoas em sua rede", "not_in_any_album": "Fora de ÃĄlbum", + "not_selected": "NÃŖo selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rÃŗtulo de armazenamento a arquivos carregados anteriormente, execute o", "notes": "Notas", + "notification_permission_dialog_content": "Para ativar as notificaçÃĩes, vÃĄ em ConfiguraçÃĩes e selecione permitir.", + "notification_permission_list_tile_content": "Conceda permissÃŖo para ativar notificaçÃĩes.", + "notification_permission_list_tile_enable_button": "Ativar notificaçÃĩes", + "notification_permission_list_tile_title": "PermissÃŖo de notificaçÃĩes", "notification_toggle_setting_description": "Habilitar notificaçÃĩes por e-mail", "notifications": "NotificaçÃĩes", "notifications_setting_description": "Gerenciar notificaçÃĩes", - "oauth": "OAuth", "official_immich_resources": "Recursos oficiais do Immich", - "offline": "Offline", "offline_paths": "Caminhos offline", "offline_paths_description": "Estes resultados podem ser devidos a arquivos deletados manualmente e que nÃŖo sÃŖo parte de uma biblioteca externa.", - "ok": "Ok", "oldest_first": "Mais antigo primeiro", + "on_this_device": "Neste dispositivo", "onboarding": "IntegraÃ§ÃŖo", "onboarding_privacy_description": "As seguintes funçÃĩes opcionais dependem de serviços externos e podem ser desabilitadas a qualquer momento nas configuraçÃĩes de administraÃ§ÃŖo.", "onboarding_theme_description": "Escolha um tema de cores para sua instÃĸncia. VocÃĒ pode alterar isso posteriormente em suas configuraçÃĩes.", "onboarding_welcome_description": "Vamos configurar sua instÃĸncia com algumas configuraçÃĩes comuns.", "onboarding_welcome_user": "Bem-vindo, {user}", - "online": "Online", "only_favorites": "Somente favoritos", + "open": "Abrir", "open_in_map_view": "Mostrar no mapa", "open_in_openstreetmap": "Abrir no OpenStreetMap", "open_the_search_filters": "Abre os filtros de pesquisa", "options": "OpçÃĩes", "or": "ou", "organize_your_library": "Organize sua biblioteca", - "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", "other_variables": "Outras variÃĄveis", @@ -1041,6 +1286,14 @@ "partner_can_access": "{partner} pode acessar", "partner_can_access_assets": "Todas suas fotos e vídeos, excetos os Arquivados ou Excluídos", "partner_can_access_location": "A localizaÃ§ÃŖo onde as fotos foram tiradas", + "partner_list_user_photos": "Fotos de {user}", + "partner_list_view_all": "Ver tudo", + "partner_page_empty_message": "As suas fotos ainda nÃŖo foram compartilhadas com nenhum parceiro.", + "partner_page_no_more_users": "NÃŖo hÃĄ mais usuÃĄrios para adicionar", + "partner_page_partner_add_failed": "Falha ao adicionar parceiro", + "partner_page_select_partner": "Selecione o parceiro", + "partner_page_shared_to_title": "Compartilhado com", + "partner_page_stop_sharing_content": "{partner} nÃŖo poderÃĄ mais acessar as suas fotos.", "partner_sharing": "Compartilhamento com Parceiro", "partners": "Parceiros", "password": "Senha", @@ -1069,6 +1322,14 @@ "permanently_delete_assets_prompt": "VocÃĒ tem certeza de que deseja excluir permanentemente {count, plural, one {este ativo?} other {estes # ativos?}} Esta aÃ§ÃŖo tambÊm removerÃĄ {count, plural, one {o ativo} other {os ativos}} de um ou mais ÃĄlbuns.", "permanently_deleted_asset": "Arquivo deletado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo permanentemente excluído} other {# arquivos permanentemente excluídos}}", + "permission_onboarding_back": "Voltar", + "permission_onboarding_continue_anyway": "Continuar assim mesmo", + "permission_onboarding_get_started": "Começar", + "permission_onboarding_go_to_settings": "Ir para as configuraçÃĩes", + "permission_onboarding_permission_denied": "PermissÃŖo negada. Para utilizar o Immich, conceda permissÃĩes de fotos e vídeo nas configuraçÃĩes.", + "permission_onboarding_permission_granted": "PermissÃŖo concedida! Tudo pronto.", + "permission_onboarding_permission_limited": "PermissÃŖo limitada. Para permitir que o Immich faça backups e gerencie sua galeria, conceda permissÃĩes para fotos e vídeos nas configuraçÃĩes.", + "permission_onboarding_request": "Immich requer permissÃŖo para visualizar suas fotos e vídeos.", "person": "Pessoa", "person_birthdate": "Nasceu em {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", @@ -1086,6 +1347,8 @@ "play_motion_photo": "Reproduzir foto em movimento", "play_or_pause_video": "Reproduzir ou Pausar vídeo", "port": "Porta", + "preferences_settings_subtitle": "Gerenciar as preferÃĒncias do aplicativo", + "preferences_settings_title": "PreferÃĒncias", "preset": "PredefiniÃ§ÃŖo", "preview": "PrÊ-visualizar", "previous": "Anterior", @@ -1093,14 +1356,18 @@ "previous_or_next_photo": "Foto anterior ou prÃŗxima", "primary": "PrimÃĄrio", "privacy": "Privacidade", + "profile_drawer_client_out_of_date_major": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", + "profile_drawer_client_out_of_date_minor": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", "profile_drawer_client_server_up_to_date": "Cliente e Servidor estÃŖo atualizados", + "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo principal mais recente.", + "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo mais recente.", "profile_image_of_user": "Imagem do perfil de {user}", "profile_picture_set": "Foto de perfil definida.", "public_album": "Álbum pÃēblico", "public_share": "Compartilhar Publicamente", "purchase_account_info": "Contribuidor", "purchase_activated_subtitle": "Obrigado(a) por apoiar o Immich e programas de cÃŗdigo aberto", - "purchase_activated_time": "Ativado em {date, date}", + "purchase_activated_time": "Ativado em {date}", "purchase_activated_title": "Sua chave foi ativada com sucesso", "purchase_button_activate": "Ativar", "purchase_button_buy": "Comprar", @@ -1143,6 +1410,10 @@ "recent": "Recente", "recent-albums": "Álbuns recentes", "recent_searches": "Pesquisas recentes", + "recently_added": "Adicionado recentemente", + "recently_added_page_title": "Adicionados recentemente", + "recently_taken": "Tirada recentemente", + "recently_taken_page_title": "Tiradas recentemente", "refresh": "Atualizar", "refresh_encoded_videos": "Atualizar vídeos codificados", "refresh_faces": "Atualizar rostos", @@ -1196,13 +1467,14 @@ "retry_upload": "Tentar carregar novamente", "review_duplicates": "Revisar duplicidade", "role": "FunÃ§ÃŖo", - "role_editor": "Editor", "role_viewer": "Visualizador", "save": "Salvar", + "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API salva", "saved_profile": "Perfil Salvo", "saved_settings": "ConfiguraçÃĩes salvas", "say_something": "Diga algo", + "scaffold_body_error_occurred": "Ocorreu um erro", "scan_all_libraries": "Escanear Todas Bibliotecas", "scan_library": "Escanear", "scan_settings": "OpçÃĩes de escanear", @@ -1218,20 +1490,44 @@ "search_camera_model": "Pesquisar cÃĸmera do modelo...", "search_city": "Pesquisar cidade...", "search_country": "Pesquisar país...", + "search_filter_apply": "Aplicar filtro", + "search_filter_camera_title": "Selecione o tipo de cÃĸmera", + "search_filter_date": "Data", + "search_filter_date_interval": "de {start} atÊ {end}", + "search_filter_date_title": "Selecione o intervalo de datas", + "search_filter_display_option_not_in_album": "NÃŖo estÃĄ em nenhum ÃĄlbum", + "search_filter_display_options": "OpçÃĩes de exibiÃ§ÃŖo", + "search_filter_filename": "Pesquisar por nome de arquivo", + "search_filter_location": "LocalizaÃ§ÃŖo", + "search_filter_location_title": "Selecione a localizaÃ§ÃŖo", + "search_filter_media_type": "Tipo de mídia", + "search_filter_media_type_title": "Selecione o tipo de mídia", + "search_filter_people_title": "Selecione pessoas", "search_for": "Pesquisar por", "search_for_existing_person": "Pesquisar por pessoas", + "search_no_more_result": "NÃŖo hÃĄ mais resultados", "search_no_people": "Nenhuma pessoa", "search_no_people_named": "Nenhuma pessoa chamada \"{name}\"", + "search_no_result": "Nenhum resultado encontrado, tente pesquisar por algo diferente", "search_options": "OpçÃĩes de pesquisa", + "search_page_categories": "Categorias", + "search_page_motion_photos": "Fotos com movimento", "search_page_no_objects": "Nenhuma informaÃ§ÃŖo de objeto disponível", "search_page_no_places": "Nenhuma informaÃ§ÃŖo de lugares disponível", + "search_page_screenshots": "Capturas de tela", + "search_page_search_photos_videos": "Pesquise suas fotos e vídeos", "search_page_things": "Coisas", + "search_page_view_all_button": "Ver tudo", + "search_page_your_activity": "Sua atividade", + "search_page_your_map": "Seu mapa", "search_people": "Pesquisar pessoas", "search_places": "Pesquisar lugares", "search_rating": "Pesquisar por classificaÃ§ÃŖo...", "search_result_page_new_search_hint": "Nova pesquisa", "search_settings": "ConfiguraçÃĩes de pesquisa", "search_state": "Pesquisar estado...", + "search_suggestion_list_smart_search_hint_1": "A pesquisa inteligente Ê utilizada por padrÃŖo, para pesquisar por metadados, use a sintaxe ", + "search_suggestion_list_smart_search_hint_2": "m:seu-termo-de-pesquisa", "search_tags": "Procurar marcadores...", "search_timezone": "Pesquisar fuso horÃĄrio...", "search_type": "Pesquisar tipo", @@ -1250,6 +1546,7 @@ "select_keep_all": "Marcar manter em todos", "select_library_owner": "Selecione o dono da biblioteca", "select_new_face": "Selecionar novo rosto", + "select_person_to_tag": "Selecione uma pessoa para marcar", "select_photos": "Selecionar fotos", "select_trash_all": "Marcar lixo em todos", "select_user_for_sharing_page_err_album": "Falha ao criar ÃĄlbum", @@ -1257,6 +1554,9 @@ "selected_count": "{count, plural, one {# selecionado} other {# selecionados}}", "send_message": "Enviar mensagem", "send_welcome_email": "Enviar E-mail de boas vindas", + "server_endpoint": "URL do servidor", + "server_info_box_app_version": "VersÃŖo do aplicativo", + "server_info_box_server_url": "URL do servidor", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", "server_stats": "Status do servidor", @@ -1268,20 +1568,81 @@ "set_date_of_birth": "Definir data de nascimento", "set_profile_picture": "Definir foto de perfil", "set_slideshow_to_fullscreen": "ApresentaÃ§ÃŖo em tela cheia", + "setting_image_viewer_help": "O visualizador de imagens carrega primeiro a miniatura pequena, depois carrega a imagem de tamanho mÊdio (se ativado) e, por fim, carrega o original (se ativado).", + "setting_image_viewer_original_subtitle": "Ative para carregar a imagem original em resoluÃ§ÃŖo mÃĄxima (grande!). Desative para reduzir o uso de dados (tanto na rede quanto no cache do dispositivo).", + "setting_image_viewer_original_title": "Carregar imagem original", + "setting_image_viewer_preview_subtitle": "Ative para carregar uma imagem de resoluÃ§ÃŖo mÊdia. Desative para carregar diretamente o original ou usar apenas a miniatura.", + "setting_image_viewer_preview_title": "Carregar imagem de prÊ-visualizaÃ§ÃŖo", + "setting_image_viewer_title": "Imagens", + "setting_languages_apply": "Aplicar", + "setting_languages_subtitle": "Alterar o idioma do aplicativo", + "setting_languages_title": "Idiomas", + "setting_notifications_notify_failures_grace_period": "Notifique falhas de backup em segundo plano: {duration}", + "setting_notifications_notify_hours": "{count} horas", + "setting_notifications_notify_immediately": "imediatamente", + "setting_notifications_notify_minutes": "{count} minutos", + "setting_notifications_notify_never": "nunca", + "setting_notifications_notify_seconds": "{count} segundos", + "setting_notifications_single_progress_subtitle": "InformaçÃĩes detalhadas sobre o progresso do envio por arquivo", + "setting_notifications_single_progress_title": "Mostrar detalhes do progresso do backup em segundo plano", + "setting_notifications_subtitle": "Ajuste suas preferÃĒncias de notificaÃ§ÃŖo", + "setting_notifications_total_progress_subtitle": "Progresso do envio de arquivos (concluídos/total)", + "setting_notifications_total_progress_title": "Mostrar o progresso total do backup em segundo plano", + "setting_video_viewer_looping_title": "Repetir", + "setting_video_viewer_original_video_subtitle": "Ao transmitir um vídeo do servidor, usar o arquivo original, mesmo quando uma versÃŖo transcodificada esteja disponível. Pode fazer com que o vídeo demore para carregar. Vídeos disponíveis localmente sÃŖo exibidos na qualidade original independente desta configuraÃ§ÃŖo.", + "setting_video_viewer_original_video_title": "Forçar vídeo original", "settings": "ConfiguraçÃĩes", + "settings_require_restart": "Reinicie o Immich para aplicar esta configuraÃ§ÃŖo", "settings_saved": "ConfiguraçÃĩes salvas", "share": "Compartilhar", "share_add_photos": "Adicionar fotos", + "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", "shared": "Compartilhado", + "shared_album_activities_input_disable": "ComentÃĄrios desativados", + "shared_album_activity_remove_content": "Deseja excluir esta atividade?", + "shared_album_activity_remove_title": "Excluir atividade", + "shared_album_section_people_action_error": "Erro ao sair/remover do ÃĄlbum", + "shared_album_section_people_action_leave": "Sair do ÃĄlbum", + "shared_album_section_people_action_remove_user": "Remover usuÃĄrio do ÃĄlbum", + "shared_album_section_people_title": "PESSOAS", "shared_by": "Compartilhado por", "shared_by_user": "Compartilhado por {user}", "shared_by_you": "Compartilhado por vocÃĒ", "shared_from_partner": "Fotos de {partner}", + "shared_intent_upload_button_progress_text": "Enviados {current} de {total}", + "shared_link_app_bar_title": "Links compartilhados", + "shared_link_clipboard_copied_massage": "Copiado para a ÃĄrea de transferÃĒncia", + "shared_link_clipboard_text": "Link: {link}\nSenha: {password}", + "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_edit_description_hint": "Digite a descriÃ§ÃŖo do compartilhamento", + "shared_link_edit_expire_after_option_day": "1 dia", + "shared_link_edit_expire_after_option_days": "{count} dias", + "shared_link_edit_expire_after_option_hour": "1 hora", + "shared_link_edit_expire_after_option_hours": "{count} horas", + "shared_link_edit_expire_after_option_minute": "1 minuto", + "shared_link_edit_expire_after_option_minutes": "{count} minutos", + "shared_link_edit_expire_after_option_months": "{count} meses", + "shared_link_edit_expire_after_option_year": "{count} ano", + "shared_link_edit_password_hint": "Digite uma senha para proteger este link", + "shared_link_edit_submit_button": "Atualizar link", + "shared_link_error_server_url_fetch": "Erro ao abrir a URL do servidor", + "shared_link_expires_day": "Expira em {count} dia", + "shared_link_expires_days": "Expira em {count} dias", + "shared_link_expires_hour": "Expira em {count} hora", + "shared_link_expires_hours": "Expira em {count} horas", + "shared_link_expires_minute": "Expira em {count} minuto", + "shared_link_expires_minutes": "Expira em {count} minutos", + "shared_link_expires_never": "Expira em ∞", + "shared_link_expires_second": "Expira em {count} segundo", + "shared_link_expires_seconds": "Expira em {count} segundos", + "shared_link_individual_shared": "Compartilhado Individualmente", + "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "OpçÃĩes de link compartilhado", "shared_links": "Links compartilhados", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", + "shared_with_me": "Compartilhado comigo", "shared_with_partner": "Compartilhado com {partner}", "sharing": "Compartilhamento", "sharing_enter_password": "Digite a senha para visualizar esta pÃĄgina.", @@ -1332,16 +1693,14 @@ "sort_recent": "Foto mais recente", "sort_title": "Título", "source": "Fonte", - "stack": "Empilhar", - "stack_duplicates": "Empilhar duplicados", - "stack_select_one_photo": "Selecione uma foto principal para a pilha", - "stack_selected_photos": "Empilhar fotos selecionadas", - "stacked_assets_count": "{count, plural, one {# Arquivo empilhado} other {# Arquivos empilhados}}", - "stacktrace": "Rastreamento de pilha", + "stack": "Agrupar", + "stack_duplicates": "Agrupar duplicados", + "stack_select_one_photo": "Selecione uma foto principal para o grupo", + "stack_selected_photos": "Agrupar fotos selecionadas", + "stacked_assets_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao grupo", "start": "Início", "start_date": "Data inicial", "state": "Estado", - "status": "Status", "stop_motion_photo": "Parar foto em movimento", "stop_photo_sharing": "Parar de compartilhar suas fotos?", "stop_photo_sharing_description": "{partner} nÃŖo terÃĄ mais acesso às suas fotos.", @@ -1357,6 +1716,9 @@ "support_third_party_description": "Sua instalaÃ§ÃŖo do Immich Ê fornecida por terceiros. É possível que problemas sejam causados por eles, por isso, se tiver problemas, procure primeiro ajuda com eles utilizando os links abaixo.", "swap_merge_direction": "Alternar direÃ§ÃŖo da mesclagem", "sync": "Sincronizar", + "sync_albums": "Sincronizar ÃĄlbuns", + "sync_albums_manual_subtitle": "Sincronize todos as fotos e vídeos enviados para os ÃĄlbuns de backup selecionados", + "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o ÃĄlbum selecionado no Immich", "tag": "Marcador", "tag_assets": "Marcar arquivos", "tag_created": "Marcador criado: {tag}", @@ -1370,8 +1732,15 @@ "theme": "Tema", "theme_selection": "Selecionar tema", "theme_selection_description": "Defina automaticamente o tema como claro ou escuro com base na preferÃĒncia do sistema do seu navegador", + "theme_setting_asset_list_storage_indicator_title": "Mostrar indicador de armazenamento na grade de fotos", + "theme_setting_asset_list_tiles_per_row_title": "Quantidade de arquivos por linha ({count})", + "theme_setting_colorful_interface_subtitle": "Aplica a cor primÃĄria ao fundo.", + "theme_setting_colorful_interface_title": "Interface colorida", "theme_setting_image_viewer_quality_subtitle": "Ajuste a qualidade de imagens detalhadas do visualizador", "theme_setting_image_viewer_quality_title": "Qualidade das imagens do visualizador", + "theme_setting_primary_color_subtitle": "Selecione uma cor para açÃĩes principais e realces.", + "theme_setting_primary_color_title": "Cor primÃĄria", + "theme_setting_system_primary_color_title": "Usar a cor do sistema", "theme_setting_system_theme_switch": "AutomÃĄtico (seguir a configuraÃ§ÃŖo do sistema)", "theme_setting_theme_subtitle": "Escolha a configuraÃ§ÃŖo de tema do app", "theme_setting_three_stage_loading_subtitle": "O carregamento em trÃĒs estÃĄgios oferece a imagem de melhor qualidade em troca de uma velocidade de carregamento mais lenta", @@ -1389,13 +1758,20 @@ "to_trash": "Mover para a lixeira", "toggle_settings": "Alternar configuraçÃĩes", "toggle_theme": "Alternar tema escuro", - "total": "Total", "total_usage": "UtilizaÃ§ÃŖo total", "trash": "Lixeira", "trash_all": "Mover todos para o lixo", "trash_count": "Lixo {count, number}", "trash_delete_asset": "Jogar na lixeira/Excluir Arquivo", + "trash_emptied": "Lixeira esvaziada", "trash_no_results_message": "Fotos e vídeos enviados para o lixo aparecem aqui.", + "trash_page_delete_all": "Excluir tudo", + "trash_page_empty_trash_dialog_content": "Deseja esvaziar a lixera? Estes arquivos serÃŖo apagados de forma permanente do Immich", + "trash_page_info": "Os itens da lixeira sÃŖo excluídos de forma permanente apÃŗs {days} dias", + "trash_page_no_assets": "Lixeira vazia", + "trash_page_restore_all": "Restaurar tudo", + "trash_page_select_assets_btn": "Selecionar arquivos", + "trash_page_title": "Lixeira ({count})", "trashed_items_will_be_permanently_deleted_after": "Os itens da lixeira serÃŖo deletados permanentemente apÃŗs {days, plural, one {# dia} other {# dias}}.", "type": "Tipo", "unarchive": "Desarquivar", @@ -1416,14 +1792,16 @@ "unsaved_change": "AlteraÃ§ÃŖo nÃŖo salva", "unselect_all": "Desselecionar todos", "unselect_all_duplicates": "Desselecionar todas as duplicatas", - "unstack": "Desempilhar", - "unstacked_assets_count": "{count, plural, one {# Arquivo desempilhado} other {# Arquivos desempilhados}}", + "unstack": "Retirar do grupo", + "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", "untracked_files": "Arquivos nÃŖo monitorados", "untracked_files_decription": "Estes arquivos nÃŖo sÃŖo monitorados pela aplicaÃ§ÃŖo. Podem ser resultados de falhas em uma movimentaÃ§ÃŖo, carregamentos interrompidos, ou deixados para trÃĄs por causa de um problema", "up_next": "A seguir", "updated_password": "Senha atualizada", "upload": "Carregar", "upload_concurrency": "Envios simultÃĸneos", + "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", + "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a pÃĄgina para ver os novos arquivos carregados.", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} jÃĄ processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", @@ -1431,8 +1809,10 @@ "upload_status_errors": "Erros", "upload_status_uploaded": "Carregado", "upload_success": "Carregado com sucesso, atualize a pÃĄgina para ver os novos arquivos.", - "url": "URL", + "upload_to_immich": "Enviar para o Immich ({count})", + "uploading": "Enviando", "usage": "Uso", + "use_current_connection": "usar conexÃŖo atual", "use_custom_date_range": "Usar intervalo de datas personalizado", "user": "UsuÃĄrio", "user_id": "ID do usuÃĄrio", @@ -1447,6 +1827,7 @@ "users": "UsuÃĄrios", "utilities": "Ferramentas", "validate": "Validar", + "validate_endpoint_error": "Digite uma URL vÃĄlida", "variables": "VariÃĄveis", "version": "VersÃŖo", "version_announcement_closing": "De seu amigo, Alex", @@ -1474,16 +1855,21 @@ "view_next_asset": "Ver prÃŗximo arquivo", "view_previous_asset": "Ver arquivo anterior", "view_qr_code": "Ver QR Code", - "view_stack": "Ver Pilha", + "view_stack": "Ver grupo", + "viewer_remove_from_stack": "Remover do grupo", + "viewer_stack_use_as_main_asset": "Usar como foto principal", + "viewer_unstack": "Desagrupar", "visibility_changed": "A visibilidade de {count, plural, one {# pessoa foi alterada} other {# pessoas foram alteradas}}", "waiting": "Na fila", "warning": "Aviso", "week": "Semana", - "welcome": "Bem-vindo", - "welcome_to_immich": "Bem-vindo ao Immich", + "welcome": "Bem-vindo(a)", + "welcome_to_immich": "Bem-vindo(a) ao Immich", + "wifi_name": "Nome do Wi-Fi", "year": "Ano", "years_ago": "{years, plural, one {# ano} other {# anos}} atrÃĄs", "yes": "Sim", - "you_dont_have_any_shared_links": "VocÃĒ nÃŖo possui links compartilhados", + "you_dont_have_any_shared_links": "NÃŖo hÃĄ links compartilhados", + "your_wifi_name": "Nome do seu Wi-Fi", "zoom_image": "Ampliar imagem" } diff --git a/i18n/ro.json b/i18n/ro.json index 7b4913e348..9f1bc03cb2 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -14,7 +14,6 @@ "add_a_location": "Adaugă locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", - "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", "add_location": "Adaugă o locație", @@ -192,20 +191,13 @@ "oauth_auto_register": "Auto ÃŽnregistrare", "oauth_auto_register_description": "Înregistrează automat utilizatori noi după autentificarea cu OAuth", "oauth_button_text": "Text buton", - "oauth_client_id": "ID Client", - "oauth_client_secret": "Secret client", "oauth_enable_description": "Autentifică-te cu OAuth", - "oauth_issuer_url": "Emitentul URL", "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override_description": "Activați atunci cÃĸnd furnizorul OAuth nu permite un URI mobil, precum '{callback}'", - "oauth_profile_signing_algorithm": "Algoritm de semnare a profilului", - "oauth_profile_signing_algorithm_description": "Algoritm folosit pentru a semna profilul utilizatorului.", - "oauth_scope": "Domeniul de aplicare", "oauth_settings": "OAuth", "oauth_settings_description": "Gestionați setările de conectare OAuth", "oauth_settings_more_details": "Pentru mai multe detalii despre aceastĮŽ funcționalitate, verificĮŽ documentația.", - "oauth_signing_algorithm": "Algoritm de semnare", "oauth_storage_label_claim": "Revendicare eticheta de stocare", "oauth_storage_label_claim_description": "Setați automat eticheta de stocare a utilizatorului la valoarea acestei revendicări.", "oauth_storage_quota_claim": "Revendicare spațiu de stocare", @@ -371,13 +363,15 @@ "admin_password": "Parolă Administrator", "administration": "Administrare", "advanced": "Avansat", - "advanced_settings_log_level_title": "Nivel log: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media ÃŽn timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă ÃŽntÃĸmpinați probleme cu aplicația la detectarea tuturor albumelor.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizați filtrul alternativ de sincronizare a albumelor de pe dispozitiv", + "advanced_settings_log_level_title": "Nivel log: {level}", "advanced_settings_prefer_remote_subtitle": "Unele dispozitive ÃŽntÃĸmpină dificultăți ÃŽn ÃŽncărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a ÃŽncărca imaginile de la distanță ÃŽn schimb.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", + "advanced_settings_sync_remote_deletions_subtitle": "Ștergeți sau restaurați automat un element de pe acest dispozitiv atunci cÃĸnd acțiunea este efectuată pe web", + "advanced_settings_sync_remote_deletions_title": "Sincronizează stergerile efectuate la distanță [EXPERIMENTAL]", "advanced_settings_tile_subtitle": "Setări avansate pentru utilizator", "advanced_settings_troubleshooting_subtitle": "Activează funcționalități suplimentare pentru depanare", "advanced_settings_troubleshooting_title": "Depanare", @@ -400,14 +394,13 @@ "album_remove_user_confirmation": "Ești sigur că dorești eliminarea {user}?", "album_share_no_users": "Se pare că ai partajat acest album cu toți utilizatorii sau nu ai niciun utilizator cu care să-l partajezi.", "album_thumbnail_card_item": "1 element", - "album_thumbnail_card_items": "{} elemente", - "album_thumbnail_card_shared": "Distribuit", - "album_thumbnail_shared_by": "Distribuit de {}", + "album_thumbnail_card_items": "{count} elemente", + "album_thumbnail_card_shared": " ¡ Distribuit", + "album_thumbnail_shared_by": "Distribuit de {user}", "album_updated": "Album actualizat", "album_updated_setting_description": "Primiți o notificare prin e-mail cÃĸnd un album partajat are elemente noi", "album_user_left": "A părăsit {album}", "album_user_removed": "{user} eliminat", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", "album_viewer_appbar_share_err_delete": "Ștergere album eșuată", "album_viewer_appbar_share_err_leave": "Părăsire album eșuată", "album_viewer_appbar_share_err_remove": "Probleme la ștergerea resurselor din album", @@ -440,10 +433,9 @@ "archive": "Arhivă", "archive_or_unarchive_photo": "ArhiveazĮŽ sau dezarhiveazĮŽ fotografia", "archive_page_no_archived_assets": "Nu au fost găsite resurse favorite", - "archive_page_title": "Arhivă ({})", + "archive_page_title": "Arhivă ({count})", "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (ÃŽn GiB)", - "archived": "Archived", "archived_count": "{count, plural, other {Arhivat/e#}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", @@ -454,89 +446,70 @@ "asset_description_updated": "Descrierea resursei a fost actualizată", "asset_filename_is_offline": "Resursa {filename} este offline", "asset_has_unassigned_faces": "Resursa are fețe neatribuite", - "asset_hashing": "Hashingâ€Ļ", - "asset_list_group_by_sub_title": "Group by", "asset_list_layout_settings_dynamic_layout_title": "Aspect dinamic", "asset_list_layout_settings_group_automatically": "Automat", "asset_list_layout_settings_group_by": "Grupează resurse după", "asset_list_layout_settings_group_by_month_day": "Lună + zi", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Setări format grilă fotografii", "asset_list_settings_title": "Grilă fotografii", "asset_offline": "Resursă Offline", "asset_offline_description": "Această resursă externă nu mai este găsită pe disc. Contactează te rog administratorul tău Immich pentru ajutor.", - "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "Sărit", "asset_skipped_in_trash": "În coșul de gunoi", "asset_uploaded": "Încărcat", "asset_uploading": "Se incarcăâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", "assets": "Resurse", "assets_added_count": "Adăugat {count, plural, one {# resursă} other {# resurse}}", "assets_added_to_album_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} ÃŽn album", "assets_added_to_name_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} ÃŽn {hasName, select, true {{name}} other {albumul nou}}", "assets_count": "{count, plural, one {# resursă} other {# resurse}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "Am mutat {count, plural, one {# resursă} other {# resurse}} ÃŽn coșul de gunoi", "assets_permanently_deleted_count": "Șters permanent {count, plural, one {# resursă} other {# resurse}}", "assets_removed_count": "Eliminat {count, plural, one {# resursă} other {# resurse}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", "assets_restore_confirmation": "Ești sigur că vrei să restaurezi toate resursele tale din coșul de gunoi? Nu poți anula această acțiune! Ține minte că resursele offline nu se restaurează astfel.", "assets_restored_count": "Restaurat {count, plural, one {# resursă} other {# resurse}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", "assets_trashed_count": "Mutat ÃŽn coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", "authorized_devices": "Dispozitive Autorizate", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "Înapoi", "back_close_deselect": "Înapoi, ÃŽnchidere sau deselectare", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albume ÃŽn dispozitiv ({})", + "backup_album_selection_page_albums_device": "Albume ÃŽn dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi ÃŽmprăștiate ÃŽn mai multe albume. Prin urmare, albumele pot fi incluse sau excluse ÃŽn timpul procesului de backup.", "backup_album_selection_page_select_albums": "Selectează albume", "backup_album_selection_page_selection_info": "Informații selecție", "backup_album_selection_page_total_assets": "Total resurse unice", "backup_all": "Toate", - "backup_background_service_backup_failed_message": "Eșuare backup resurse. Re-ÃŽncercare...", - "backup_background_service_connection_failed_message": "Conectare la server eșuată. ReÃŽncercare...", - "backup_background_service_current_upload_notification": "Încărcare {}", - "backup_background_service_default_notification": "Verificare resurse noi...", + "backup_background_service_backup_failed_message": "Eșuare backup resurse. ReÃŽncercareâ€Ļ", + "backup_background_service_connection_failed_message": "Conectare la server eșuată. ReÃŽncercareâ€Ļ", + "backup_background_service_current_upload_notification": "Încărcare {filename}", + "backup_background_service_default_notification": "Verificare resurse noiâ€Ļ", "backup_background_service_error_title": "Eroare backup", - "backup_background_service_in_progress_notification": "Se face backup al resurselor tale...", - "backup_background_service_upload_failure_notification": "Încărcare eșuată {}", + "backup_background_service_in_progress_notification": "Se face backup al resurselor taleâ€Ļ", + "backup_background_service_upload_failure_notification": "Încărcare eșuată {filename}", "backup_controller_page_albums": "Backup albume", "backup_controller_page_background_app_refresh_disabled_content": "Activează reÃŽmprospătarea aplicației ÃŽn fundal ÃŽn Setări > General > ReÃŽmprospătare aplicații ÃŽn fundal pentru a folosi copia de siguranță ÃŽn fundal.", "backup_controller_page_background_app_refresh_disabled_title": "ReÃŽmprospătarea aplicației ÃŽn fundal este dezactivată", "backup_controller_page_background_app_refresh_enable_button_text": "Mergi la setări", "backup_controller_page_background_battery_info_link": "Arată-mi cum", "backup_controller_page_background_battery_info_message": "Pentru cea mai bună experiență a backup-ului ÃŽn fundal, te rugăm să dezactivezi orice optimizare pentru baterie care restricționează activitatea ÃŽn fundal pentru Immich.\n\nDeoarece aceasta este specifică fiecărui dispozitiv, te rugăm verifică informațiile necesare tipului tău de dispozitiv.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizări baterie", "backup_controller_page_background_charging": "Doar ÃŽn timpul ÃŽncărcării", "backup_controller_page_background_configure_error": "Configurare serviciu ÃŽn fundal eșuată", - "backup_controller_page_background_delay": "AmÃĸnare copie de siguranță a resurselor noi: {}", + "backup_controller_page_background_delay": "AmÃĸnare copie de siguranță a resurselor noi: {duration}", "backup_controller_page_background_description": "Activează backup-ul ÃŽn fundal pentru a ÃŽncărca resursele automat pe server fără a fi nevoie să deschizi aplicația", "backup_controller_page_background_is_off": "Backup-ul automat ÃŽn fundal este dezactivat", "backup_controller_page_background_is_on": "Backup-ul automat ÃŽn fundal este activat", "backup_controller_page_background_turn_off": "Dezactivează serviciul ÃŽn fundal", "backup_controller_page_background_turn_on": "Activează serviciul ÃŽn fundal", "backup_controller_page_background_wifi": "Doar conectat la WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selectat(e):", + "backup_controller_page_backup_selected": "Selectat(e): ", "backup_controller_page_backup_sub": "S-a făcut backup pentru fotografii și videoclipuri", - "backup_controller_page_created": "Creat la: {}", - "backup_controller_page_desc_backup": "Activează backup-ul ÃŽn prim-plan pentru a ÃŽncărca resursele pe server cÃĸnd deschizi aplicația ", - "backup_controller_page_excluded": "Exclus(e):", - "backup_controller_page_failed": "Eșuate ({})", - "backup_controller_page_filename": "Nume fișier: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_created": "Creat la: {date}", + "backup_controller_page_desc_backup": "Activează backup-ul ÃŽn prim-plan pentru a ÃŽncărca resursele pe server cÃĸnd deschizi aplicația.", + "backup_controller_page_excluded": "Exclus(e): ", + "backup_controller_page_failed": "Eșuate ({count})", + "backup_controller_page_filename": "Nume fișier: {filename} [{size}]", "backup_controller_page_info": "Informații backup", "backup_controller_page_none_selected": "Nici o selecție", "backup_controller_page_remainder": "Rămas(e)", @@ -545,7 +518,7 @@ "backup_controller_page_start_backup": "Începe backup", "backup_controller_page_status_off": "Backup-ul automat ÃŽn prim-plan este oprit", "backup_controller_page_status_on": "Backup-ul automat ÃŽn prim-plan este pornit", - "backup_controller_page_storage_format": "{} din {} folosit", + "backup_controller_page_storage_format": "{used} din {total} folosit", "backup_controller_page_to_backup": "Albume pentru backup", "backup_controller_page_total_sub": "Toate fotografiile și videoclipurile unice din albumele selectate", "backup_controller_page_turn_off": "Dezactivează backup-ul ÃŽn prim-plan", @@ -557,8 +530,6 @@ "backup_manual_in_progress": "Încărcarea este deja ÃŽn curs. Încearcă din nou mai tÃĸrziu", "backup_manual_success": "Succes", "backup_manual_title": "Status ÃŽncărcare", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "În sens invers", "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vÃĸrsta acestei persoane la momentul realizării fotografiei.", @@ -570,21 +541,21 @@ "bulk_keep_duplicates_confirmation": "Ești sigur că vrei să păstrezi {count, plural, one {# resursă duplicată} other {# resurse duplicate}}? Aceasta va rezolva toate grupurile duplicate fără a șterge nimic.", "bulk_trash_duplicates_confirmation": "Ești sigur că vrei să muți ÃŽn coșul de gunoi {count, plural, one {# resursă duplicată} other {# resurse duplicate}}? Aceasta va păstra cea mai mare resursă din fiecare grup și va muta ÃŽn coșul de gunoi toate celelalte duplicate.", "buy": "Achiziționați Immich", - "cache_settings_album_thumbnails": "Miniaturi pagină galerie ({} resurse)", + "cache_settings_album_thumbnails": "Miniaturi pagină galerie ({count} resurse)", "cache_settings_clear_cache_button": "Șterge cache", "cache_settings_clear_cache_button_title": "Șterge memoria cache a aplicatiei. Performanța aplicației va fi semnificativ afectată pÃĸnă cÃĸnd va fi reconstruită.", "cache_settings_duplicated_assets_clear_button": "ȘTERGE", "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri care sunt pe lista neagră a aplicației", - "cache_settings_duplicated_assets_title": "Resurse duplicate ({})", - "cache_settings_image_cache_size": "Mărime cache imagine ({} resurse)", + "cache_settings_duplicated_assets_title": "Resurse duplicate ({count})", + "cache_settings_image_cache_size": "Mărime cache imagine ({count} resurse)", "cache_settings_statistics_album": "Miniaturi pentru librării", - "cache_settings_statistics_assets": "{} resurse ({})", + "cache_settings_statistics_assets": "{count} resurse ({size})", "cache_settings_statistics_full": "Fotografii complete", "cache_settings_statistics_shared": "Miniaturi pentru albumele distribuite", "cache_settings_statistics_thumbnail": "Miniaturi", "cache_settings_statistics_title": "Memorie cache utilizată", "cache_settings_subtitle": "Controlează modul de cache al aplicației Immich", - "cache_settings_thumbnail_size": "Mărime cache miniatura ({} resurse)", + "cache_settings_thumbnail_size": "Mărime cache miniatura ({count} resurse)", "cache_settings_tile_subtitle": "Controlează modul stocării locale", "cache_settings_tile_title": "Stocare locală", "cache_settings_title": "Setări pentru memoria cache", @@ -593,12 +564,10 @@ "camera_model": "Model cameră", "cancel": "Anulați", "cancel_search": "Anulați căutarea", - "canceled": "Canceled", "cannot_merge_people": "Nu se pot ÃŽmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", "change_date": "Schimbați data", - "change_display_order": "Change display order", "change_expiration_time": "Schimbați data expirare", "change_location": "Schimbați locația", "change_name": "Schimbați nume", @@ -613,9 +582,6 @@ "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", "check_all": "Selectați Tot", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", @@ -624,14 +590,6 @@ "clear_all_recent_searches": "Curățați toate căutările recente", "clear_message": "Ștergeți mesajul", "clear_value": "Ștergeți valoarea", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "clockwise": "În sensul acelor de ceas", "close": "Închideți", "collapse": "RestrÃĸngeți", @@ -644,7 +602,6 @@ "comments_are_disabled": "Comentariile sunt dezactivate", "common_create_new_album": "Creează album nou", "common_server_error": "Te rugăm să verifici conexiunea la rețea, asigura-te că server-ul este accesibil și că versiunile aplicației/server-ului sunt compatibile.", - "completed": "Completed", "confirm": "Confirmați", "confirm_admin_password": "Confirmați Parola de Administrator", "confirm_delete_face": "Ești sigur ca vrei sa ștergi {name} din activ?", @@ -652,15 +609,13 @@ "confirm_keep_this_delete_others": "Toate celelalte active din stivă vor fi șterse, cu excepția acestui material. Sunteți sigur că doriți să continuați?", "confirm_password": "Confirmați parola", "contain": "Încadrează", - "context": "Context", "continue": "Continuați", - "control_bottom_app_bar_album_info_shared": "{} elemente ¡ Distribuite", + "control_bottom_app_bar_album_info_shared": "{count} elemente ¡ Distribuite", "control_bottom_app_bar_create_new_album": "Creează album nou", "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", "control_bottom_app_bar_edit_time": "Editează Data și Ora", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută ÃŽn coș", "copied_image_to_clipboard": "Imagine copiată ÃŽn clipboard.", @@ -682,7 +637,6 @@ "create_link": "Creează link", "create_link_to_share": "Creează link pentru a distribui", "create_link_to_share_description": "Permiteți oricui are link-ul să vadă fotografia (fotografiile) selectată(e)", - "create_new": "CREATE NEW", "create_new_person": "Creați o persoană nouă", "create_new_person_hint": "Atribuiți resursele selectate unei persoane noi", "create_new_user": "Creează utilizator nou", @@ -692,19 +646,14 @@ "create_tag_description": "Creează o etichetă nouă. Pentru etichete imbricate, te rog să introduci calea completă a etichetei, inclusiv bare oblice (/).", "create_user": "Creează utilizator", "created": "Creat", - "crop": "Crop", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", - "current_server_address": "Current server address", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele ÃŽn funcție de limbă și regiune", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Întunecat", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", @@ -721,7 +670,7 @@ "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", "delete_dialog_alert_local": "Aceste fișiere vor fi șterse permanent din dispozitiv, dar vor fi disponibile pe server-ul Immich", "delete_dialog_alert_local_non_backed_up": "Pentru unele fișere nu s-a făcut backup ÃŽn Immich și vor fi șterse permanent din dispozitiv", - "delete_dialog_alert_remote": "Aceste fișiere vor fi șterse permanent de pe server-ul Immich.", + "delete_dialog_alert_remote": "Aceste fișiere vor fi șterse permanent de pe server-ul Immich", "delete_dialog_ok_force": "Șterge oricum", "delete_dialog_title": "Șterge permanent", "delete_duplicates_confirmation": "Sunteți sigur că doriți să ștergeți permanent aceste duplicate?", @@ -758,26 +707,12 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", "download_include_embedded_motion_videos": "Videoclipuri ÃŽncorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile ÃŽncorporate ÃŽn fotografiile ÃŽn mișcare ca fișier separat", - "download_notfound": "Download not found", - "download_paused": "Download paused", "download_settings": "Descărcați", "download_settings_description": "Gestionați setările legate de descărcarea resurselor", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", "downloading": "Se descarcă", "downloading_asset_filename": "Se descarcă resursa {filename}", - "downloading_media": "Downloading media", "drop_files_to_upload": "Trageți fișierele aici pentru a le ÃŽncărca", "duplicates": "Duplicate", "duplicates_description": "Rezolvați fiecare grup indicÃĸnd care sunt duplicate, dacă există", @@ -801,25 +736,19 @@ "edit_title": "Editare Titlu", "edit_user": "Editare utilizator", "edited": "Editat", - "editor": "Editor", "editor_close_without_save_prompt": "Schimbările nu vor fi salvate", "editor_close_without_save_title": "Închideți editorul?", "editor_crop_tool_h2_aspect_ratios": "Raporturi de aspect", "editor_crop_tool_h2_rotation": "Rotire", - "email": "Email", - "empty_folder": "This folder is empty", "empty_trash": "Goliți coșul de gunoi", "empty_trash_confirmation": "Sunteți sigur că doriți să goliți coșul de gunoi? Acest lucru va elimina definitiv din Immich toate resursele din coșul de gunoi.\nNu puteți anula această acțiune!", "enable": "Permite", "enabled": "Activat", "end_date": "Data de ÃŽncheiere", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "Eroare", - "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Eroare la ștergerea feței din activ", "error_loading_image": "Eroare la ÃŽncărcarea imaginii", - "error_saving_image": "Error: {}", "error_title": "Eroare - ceva nu a mers", "errors": { "cannot_navigate_next_asset": "Nu se poate naviga către următoarea resursă", @@ -952,17 +881,11 @@ "exif_bottom_sheet_details": "DETALII", "exif_bottom_sheet_location": "LOCAȚIE", "exif_bottom_sheet_people": "PERSOANE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "Ieșire din Prezentare", "expand_all": "Extindeți-le pe toate", "experimental_settings_new_asset_list_subtitle": "Acțiune ÃŽn desfășurare", - "experimental_settings_new_asset_list_title": "Activează grila experimentală de fotografii.", + "experimental_settings_new_asset_list_title": "Activează grila experimentală de fotografii", "experimental_settings_subtitle": "Folosește pe propria răspundere!", - "experimental_settings_title": "Experimental", "expire_after": "Expiră după", "expired": "Expirat", "expires_date": "Expiră la {date}", @@ -973,12 +896,9 @@ "extension": "Extensie", "external": "Extern", "external_libraries": "Biblioteci Externe", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "Nealocat", - "failed": "Failed", "failed_to_load_assets": "Nu s-au ÃŽncărcat activele", - "failed_to_load_folder": "Failed to load folder", "favorite": "Favorit", "favorite_or_unfavorite_photo": "Fotografie preferată sau nepreferată", "favorites": "Favorite", @@ -990,38 +910,25 @@ "file_name_or_extension": "Numele sau extensia fișierului", "filename": "Numele fișierului", "filetype": "Tipul fișierului", - "filter": "Filter", "filter_people": "Filtrați persoanele", + "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", "fix_incorrect_match": "Remediați potrivirea incorectă", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Foldere", "folders_feature_description": "Răsfoire ÃŽn conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", "forward": "Redirecționare", - "general": "General", "get_help": "Obțineți Ajutor", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", - "grant_permission": "Grant permission", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", "group_no": "Fără grupare", "group_owner": "Grupați după proprietar", "group_places_by": "Grupare locuri după...", "group_year": "Grupați după an", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", "has_quota": "Are spațiu de stocare", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "Bună {name} ({email})", "hide_all_people": "Ascundeți toate persoanele", "hide_gallery": "Ascundeți galeria", @@ -1040,13 +947,11 @@ "home_page_delete_remote_err_local": "Resursele locale sunt ÃŽn selecția pentru ștergere la distanță, omitere", "home_page_favorite_err_local": "Resursele locale nu pot fi adăugate la favorite ÃŽncă, omitere", "home_page_favorite_err_partner": "Momentan nu se pot adăuga fișierele partenerului la favorite, omitere", - "home_page_first_time_notice": "Dacă este prima dată cÃĸnd utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel ÃŽncÃĸt cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume.", + "home_page_first_time_notice": "Dacă este prima dată cÃĸnd utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel ÃŽncÃĸt cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot ÃŽncărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Imagine", "image_alt_text_date": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {imagine}} preluată cu {person1} ÃŽn {date}", @@ -1058,8 +963,6 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1} și {person2} ÃŽn {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1}, {person2}, și {person3} ÃŽn {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1}, {person2}, și {additionalCount, number} alții ÃŽn {date}", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", "image_viewer_page_state_provider_download_success": "Descărcare cu succes", "image_viewer_page_state_provider_share_error": "Eroare distribuire", "immich_logo": "Logo Immich", @@ -1080,8 +983,6 @@ "night_at_midnight": "În fiecare noapte la miezul nopții", "night_at_twoam": "În fiecare noapte la 2 dimineața" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "Invitați Persoane", "invite_to_album": "Invitați ÃŽn album", "items_count": "{count, plural, one {# element} other{# elemente}}", @@ -1117,9 +1018,6 @@ "list": "Listă", "loading": "Încărcare", "loading_search_results_failed": "Încărcarea rezultatelor căutării nu a reușit", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", "location_picker_choose_on_map": "Alege pe hartă", "location_picker_latitude_error": "Introdu o latitudine validă", @@ -1170,8 +1068,8 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bound": "{} fotografie", - "map_assets_in_bounds": "{} fotografii", + "map_assets_in_bound": "{count} fotografie", + "map_assets_in_bounds": "{count} fotografii", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1185,25 +1083,18 @@ "map_settings": "Setările hărții", "map_settings_dark_mode": "Mod ÃŽntunecat", "map_settings_date_range_option_day": "Ultimele 24 de ore", - "map_settings_date_range_option_days": "Ultimele {} zile", + "map_settings_date_range_option_days": "Ultimele {days} zile", "map_settings_date_range_option_year": "Ultimul an", - "map_settings_date_range_option_years": "Ultimii {} ani", + "map_settings_date_range_option_years": "Ultimii {years} ani", "map_settings_dialog_title": "Setările hărții", "map_settings_include_show_archived": "Include resursele arhivate", - "map_settings_include_show_partners": "Include Partners", "map_settings_only_show_favorites": "Arată doar favorite", "map_settings_theme_settings": "Stilul hărții", "map_zoom_to_see_photos": "Zoom out pentru a vedea fotografii", "matches": "Corespunde", "media_type": "Tip media", "memories": "Amintiri", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", "memories_setting_description": "Administrați ce vedeți ÃŽn amintiri", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "Amintire", "memory_lane_title": "Banda Memoriei {title}", "menu": "Meniu", @@ -1214,11 +1105,8 @@ "merge_people_successfully": "Persoane ÃŽmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", - "minute": "Minute", "missing": "Lipsă", - "model": "Model", "month": "Lună", - "monthly_title_text_date_format": "MMMM y", "more": "Mai mult", "moved_to_trash": "Mutat ÃŽn coșul de gunoi", "multiselect_grid_edit_date_time_err_read_only": "Nu se poate edita data fișierului(lor) cu permisiuni doar pentru citire, omitere", @@ -1227,8 +1115,6 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclĮŽ", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "Niciodată", "new_album": "Album Nou", "new_api_key": "Cheie API nouĮŽ", @@ -1245,7 +1131,6 @@ "no_albums_yet": "Se pare că nu aveți ÃŽncă niciun album.", "no_archived_assets_message": "Arhivați fotografii și videoclipuri pentru a le ascunde din vizualizarea fotografii", "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", - "no_assets_to_show": "No assets to show", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", @@ -1257,7 +1142,6 @@ "no_results_description": "Încercați un sinonim sau un cuvÃĸnt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", "not_in_any_album": "Nu există ÃŽn niciun album", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele ÃŽncărcate anterior, rulați", "notes": "Note", "notification_permission_dialog_content": "Pentru a activa notificările, mergi ÃŽn Setări > Immich și selectează permite.", @@ -1267,28 +1151,24 @@ "notification_toggle_setting_description": "Activați notificările prin email", "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", - "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", - "offline": "Offline", "offline_paths": "Căi offline", "offline_paths_description": "Aceste rezultate se pot datora ștergerii manuale a fișierelor care nu fac parte dintr-o bibliotecă externă.", "ok": "Bine", "oldest_first": "Cel mai vechi mai ÃŽntÃĸi", - "on_this_device": "On this device", "onboarding": "Integrare", "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate ÃŽn orice moment din setările de administrare.", "onboarding_theme_description": "Alegeți o temă de culoare pentru exemplul dvs. Puteți modifica acest lucru mai tÃĸrziu ÃŽn setări.", "onboarding_welcome_description": "Să vă setăm instanța cu cÃĸteva setări comune.", "onboarding_welcome_user": "Bun venit, {user}", - "online": "Online", "only_favorites": "Doar favorite", + "open": "Deschide", "open_in_map_view": "Deschideți ÃŽn vizualizarea hărții", "open_in_openstreetmap": "Deschideți ÃŽn OpenStreetMap", "open_the_search_filters": "Deschideți filtrele de căutare", "options": "Opțiuni", "or": "sau", "organize_your_library": "Organizează-ți biblioteca", - "original": "original", "other": "Alte", "other_devices": "Alte dispozitive", "other_variables": "Alte variabile", @@ -1298,14 +1178,12 @@ "partner_can_access": "{partner} poate accesa", "partner_can_access_assets": "Toate fotografiile și videoclipurile tale, cu excepția celor din arhivate și sterse", "partner_can_access_location": "Locația ÃŽn care au fost făcute fotografiile dvs", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", "partner_page_empty_message": "Fotografiile tale nu sunt ÃŽncă distribuite cu nici un partener.", "partner_page_no_more_users": "Nu mai sunt utilizatori de adăugat", "partner_page_partner_add_failed": "Eșuare adăugare partener", "partner_page_select_partner": "Selectează partener", "partner_page_shared_to_title": "Distribuit cu", - "partner_page_stop_sharing_content": "{} nu va mai putea accesa fotografiile tale.", + "partner_page_stop_sharing_content": "{partner} nu va mai putea accesa fotografiile tale.", "partner_sharing": "Partajarea Partenerilor", "partners": "Parteneri", "password": "Parolă", @@ -1339,7 +1217,7 @@ "permission_onboarding_get_started": "Începe", "permission_onboarding_go_to_settings": "Mergi la setări", "permission_onboarding_permission_denied": "Permisiune refuzată. Pentru a utiliza Immich, acordă permisiuni pentru fotografii și videoclipuri ÃŽn Setări.", - "permission_onboarding_permission_granted": "Permisiune acordată!", + "permission_onboarding_permission_granted": "Permisiune acordată! Sunteți gata.", "permission_onboarding_permission_limited": "Permisiune limitată. Pentru a permite Immich să facă copii de siguranță și să gestioneze ÃŽntreaga colecție de galerii, acordă permisiuni pentru fotografii și videoclipuri ÃŽn Setări.", "permission_onboarding_request": "Immich necesită permisiunea de a vizualiza fotografiile și videoclipurile tale.", "person": "PersoanĮŽ", @@ -1358,9 +1236,6 @@ "play_memories": "Redare amintiri", "play_motion_photo": "Redare Fotografie ÃŽn Mișcare", "play_or_pause_video": "Redați sau ÃŽntrerupeți videoclipul", - "port": "Port", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", @@ -1372,7 +1247,6 @@ "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", @@ -1381,7 +1255,7 @@ "public_share": "Distribuire Publică", "purchase_account_info": "Suporter", "purchase_activated_subtitle": "Vă mulțumim că susțineți Immich și software-ul open-source", - "purchase_activated_time": "Activat pe data de {date, date}", + "purchase_activated_time": "Activat pe data de {date}", "purchase_activated_title": "Cheia dvs. a fost activată cu succes", "purchase_button_activate": "Activați", "purchase_button_buy": "Cumpărați", @@ -1393,7 +1267,6 @@ "purchase_failed_activation": "Activare eșuată! Vă rugăm să vă verificați e-mailul pentru cheia de produs corectă!", "purchase_individual_description_1": "Pentru un individ", "purchase_individual_description_2": "Statutul de suporter", - "purchase_individual_title": "Individual", "purchase_input_suggestion": "Aveți o cheie de produs? Introduceți cheia mai jos", "purchase_license_subtitle": "Cumpărați Immich pentru a sprijini dezvoltarea continuă a serviciului", "purchase_lifetime_description": "Achiziție pe viață", @@ -1401,15 +1274,12 @@ "purchase_panel_info_1": "Dezvoltarea Immich necesită mult timp și efort și avem ingineri cu normă ÃŽntreagă care lucrează la ea pentru a o face cÃĸt se poate de bună. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea, cu alternative reale la serviciile cloud care exploatează.", "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară ÃŽn Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", "purchase_panel_title": "Susțineți proiectul", - "purchase_per_server": "Per server", - "purchase_per_user": "Per user", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", "purchase_remove_server_product_key": "Eliminați cheia de produs a Serverului", "purchase_remove_server_product_key_prompt": "Sigur doriți să eliminați cheia de produs a Serverului?", "purchase_server_description_1": "Pentru tot serverul", "purchase_server_description_2": "Statutul de suporter", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", "rating": "Evaluare cu stele", "rating_clear": "Anulați evaluare", @@ -1421,10 +1291,8 @@ "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", "reassing_hint": "Atribuiți resursele selectate unei persoane existente", - "recent": "Recent", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", - "recently_added": "Recently added", "recently_added_page_title": "Adăugate recent", "refresh": "ReÃŽmprospătare", "refresh_encoded_videos": "Actualizează videoclipurile codificate", @@ -1479,10 +1347,8 @@ "retry_upload": "ReÃŽncercați ÃŽncărcarea", "review_duplicates": "Examinați duplicatele", "role": "Rol", - "role_editor": "Editor", "role_viewer": "Vizualizator", "save": "Salvați", - "save_to_gallery": "Save to gallery", "saved_api_key": "Cheie API salvată", "saved_profile": "Profil salvat", "saved_settings": "Setări salvate", @@ -1503,32 +1369,16 @@ "search_camera_model": "Se caută modelul camerei...", "search_city": "Se caută orașul...", "search_country": "Se caută țara...", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", "search_for": "Căutare după", "search_for_existing_person": "Se caută o persoană existentă", - "search_no_more_result": "No more results", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_options": "Opțiuni de căutare", "search_page_categories": "Categorii", "search_page_motion_photos": "Fotografii ÃŽn mișcare", "search_page_no_objects": "Nu sunt informații disponibile despre obiecte", - "search_page_no_places": "Nici o informație disponibilă despre locuri ", + "search_page_no_places": "Nici o informație disponibilă despre locuri", "search_page_screenshots": "Capturi de ecran", - "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": "Selfie-uri", "search_page_things": "Obiecte", "search_page_view_all_button": "Vezi toate", @@ -1540,7 +1390,7 @@ "search_result_page_new_search_hint": "Căutare nouă", "search_settings": "Setări de căutare", "search_state": "Starea căutării...", - "search_suggestion_list_smart_search_hint_1": "Căutarea inteligentă este activată ÃŽn mod implicit, pentru a căuta metadata, utilizează sintaxa\n", + "search_suggestion_list_smart_search_hint_1": "Căutarea inteligentă este activată ÃŽn mod implicit, pentru a căuta metadata, utilizează sintaxa ", "search_suggestion_list_smart_search_hint_2": "m:termen-de-căutare", "search_tags": "Căutați etichete...", "search_timezone": "Căutați fusul orar...", @@ -1567,11 +1417,8 @@ "selected_count": "{count, plural, other {# selectat}}", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "Versiune Aplicatie", "server_info_box_server_url": "URL-ul server-ului", - "server_offline": "Server Offline", - "server_online": "Server Online", "server_stats": "Statistici Server", "server_version": "Versiune Server", "set": "Setați", @@ -1586,36 +1433,28 @@ "setting_image_viewer_original_title": "Încarcă fotografia originală", "setting_image_viewer_preview_subtitle": "Activează pentru a ÃŽncărca o imagine ÃŽn rezoluție medie. Dezactivează pentru a ÃŽncărca direct imaginea originală sau doar a utiliza miniatura.", "setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup ÃŽn fundal: {}", - "setting_notifications_notify_hours": "{} ore", + "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup ÃŽn fundal: {duration}", + "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "imediat", - "setting_notifications_notify_minutes": "{} minute", + "setting_notifications_notify_minutes": "{count} minute", "setting_notifications_notify_never": "niciodată", - "setting_notifications_notify_seconds": "{} secunde", + "setting_notifications_notify_seconds": "{count} secunde", "setting_notifications_single_progress_subtitle": "Informații detaliate despre progresul ÃŽncărcării pentru fiecare resursă", "setting_notifications_single_progress_title": "Afișează progresul detaliat al copiilor de siguranță ÃŽn fundal", "setting_notifications_subtitle": "Ajustează preferințele pentru notificări", "setting_notifications_total_progress_subtitle": "Progresul general al ÃŽncărcării (resurse finalizate/total)", "setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță ÃŽn fundal", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Setări", "settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare", "settings_saved": "Setările au fost salvate", "share": "Distribuiți", "share_add_photos": "Adaugă fotografii", - "share_assets_selected": "{} selected", "share_dialog_preparing": "Se pregătește...", "shared": "Partajat", "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "Dorești să ștergi această activitate?", "shared_album_activity_remove_title": "Șterge activitate", - "shared_album_section_people_action_error": "Eroare la părăsirea/ștergerea din album.", + "shared_album_section_people_action_error": "Eroare la părăsirea/ștergerea din album", "shared_album_section_people_action_leave": "Șterge utilizator din album", "shared_album_section_people_action_remove_user": "Șterge utilizator din album", "shared_album_section_people_title": "PERSOANE", @@ -1623,40 +1462,34 @@ "shared_by_user": "Partajat de {user}", "shared_by_you": "Partajat de tine", "shared_from_partner": "Fotografii de la {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "Link-uri distribuite", "shared_link_clipboard_copied_massage": "Copiat ÃŽn clipboard", - "shared_link_clipboard_text": "Link: {}\nParolă: {}", + "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", "shared_link_create_error": "Eroare ÃŽn timpul creării linkului de distribuire", "shared_link_edit_description_hint": "Introdu descrierea distribuirii", "shared_link_edit_expire_after_option_day": "1 zi", - "shared_link_edit_expire_after_option_days": "{} zile", + "shared_link_edit_expire_after_option_days": "{count} zile", "shared_link_edit_expire_after_option_hour": "1 oră", - "shared_link_edit_expire_after_option_hours": "{} ore", + "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minut", - "shared_link_edit_expire_after_option_minutes": "{} minute", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", + "shared_link_edit_expire_after_option_minutes": "{count} minute", "shared_link_edit_password_hint": "Introdu parola de distribuire", "shared_link_edit_submit_button": "Actualizează link", "shared_link_error_server_url_fetch": "Nu se poate accesa URL-ul serverului", - "shared_link_expires_day": "Expiră ÃŽn {} zi", - "shared_link_expires_days": "Expiră ÃŽn {} zile", - "shared_link_expires_hour": "Expiră ÃŽn {} ore", - "shared_link_expires_hours": "Expiră ÃŽn {} ore", - "shared_link_expires_minute": "Expiră ÃŽn {} minute", - "shared_link_expires_minutes": "Expiră ÃŽn {} minute", + "shared_link_expires_day": "Expiră ÃŽn {count} zi", + "shared_link_expires_days": "Expiră ÃŽn {count} zile", + "shared_link_expires_hour": "Expiră ÃŽn {count} ore", + "shared_link_expires_hours": "Expiră ÃŽn {count} ore", + "shared_link_expires_minute": "Expiră ÃŽn {count} minute", + "shared_link_expires_minutes": "Expiră ÃŽn {count} minute", "shared_link_expires_never": "Expiră ∞", - "shared_link_expires_second": "Expiră ÃŽn {} secunde", - "shared_link_expires_seconds": "Expiră ÃŽn {} secunde", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", + "shared_link_expires_second": "Expiră ÃŽn {count} secunde", + "shared_link_expires_seconds": "Expiră ÃŽn {count} secunde", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", - "shared_with_me": "Shared with me", "shared_with_partner": "Partajat cu {partner}", "sharing": "Distribuire", "sharing_enter_password": "Vă rugăm să introduceți parola pentru a vizualiza această pagină.", @@ -1732,9 +1565,6 @@ "support_third_party_description": "Instalarea dvs. Immich a fost pregătită de o terță parte. Problemele pe care le ÃŽntÃĸmpinați pot fi cauzate de acel pachet, așa că vă rugăm să ridicați probleme cu ei ÃŽn primă instanță utilizÃĸnd linkurile de mai jos.", "swap_merge_direction": "Schimbați direcția de ÃŽmbinare", "sync": "Sincronizare", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", "tag_created": "Etichetă creată: {tag}", @@ -1749,14 +1579,9 @@ "theme_selection": "Selectarea temei", "theme_selection_description": "Setați automat tema la mod luminos sau ÃŽntunecată, ÃŽn funcție de preferințele de sistem ale browserului dvs", "theme_setting_asset_list_storage_indicator_title": "Arată indicator stocare", - "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rÃĸnd ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rÃĸnd ({count})", "theme_setting_image_viewer_quality_subtitle": "Ajustează calitatea detaliilor vizualizatorului de imagine", "theme_setting_image_viewer_quality_title": "Calitate vizualizator de imagine", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "Automat (La fel ca setarea sistemului)", "theme_setting_theme_subtitle": "Alege tema aplicației", "theme_setting_three_stage_loading_subtitle": "Încărcarea ÃŽn trei etape are putea crește performanța ÃŽncărcării dar generează un volum semnificativ mai mare de trafic pe rețea", @@ -1774,21 +1599,19 @@ "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", "toggle_theme": "Activați tema ÃŽntunecată", - "total": "Total", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", "trash_all": "Ștergeți Tot", "trash_count": "Ștergeți {count, number}", "trash_delete_asset": "Coș de gunoi/Ștergeți resursa", - "trash_emptied": "Emptied trash", "trash_no_results_message": "Fotografiile și videoclipurile mutate ÃŽn coșul de gunoi vor apărea aici.", "trash_page_delete_all": "Șterge tot", "trash_page_empty_trash_dialog_content": "Dorești să golești coșul? Aceste fișiere vor fi șterse permanent din Immich", - "trash_page_info": "Resursele din coș vor fi șterse permanent după {} zile", + "trash_page_info": "Resursele din coș vor fi șterse permanent după {days} zile", "trash_page_no_assets": "Nici o resursă in coș", "trash_page_restore_all": "Restaurează toate fișierele", "trash_page_select_assets_btn": "Selectează resurse", - "trash_page_title": "Coș ({})", + "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", "type": "Tip", "unarchive": "Dezarhivați", @@ -1826,11 +1649,7 @@ "upload_status_errors": "Erori", "upload_status_uploaded": "Încărcat", "upload_success": "Încărcare reușită, reÃŽmprospătați pagina pentru a vedea resursele noi ÃŽncărcate.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", "usage": "Utilizare", - "use_current_connection": "use current connection", "use_custom_date_range": "Utilizați ÃŽn schimb un interval de date personalizat", "user": "Utilizator", "user_id": "ID utilizator", @@ -1845,16 +1664,15 @@ "users": "Utilizatori", "utilities": "UtilitĮŽČ›i", "validate": "Validați", - "validate_endpoint_error": "Please enter a valid URL", "variables": "Variabile", "version": "Versiune", "version_announcement_closing": "Prietenul tĮŽu, Alex", "version_announcement_message": "Bună! Este disponibilă o nouă versiune de Immich. Vă rugăm să vă faceți timp să citiți notele de lansare pentru a vă asigura că configurația dvs. este actualizată pentru a preveni orice configurare greșită, mai ales dacă utilizați WatchTower sau orice mecanism care se ocupă de actualizarea automată a instanței dvs. Immich.", "version_announcement_overlay_release_notes": "informații update", "version_announcement_overlay_text_1": "Salut, există un update nou pentru", - "version_announcement_overlay_text_2": "te rugăm verifică", - "version_announcement_overlay_text_3": "și asigură-te că fișierul .env și configurația ta docker-compose sunt actualizate pentru a preveni orice erori de configurație, ÃŽn special dacă folosești WatchTower sau orice mecanism care gestionează actualizarea automată a aplicației server-ului tău.", - "version_announcement_overlay_title": "O nouă versiune pentru server este disponibilă 🎉", + "version_announcement_overlay_text_2": "te rugăm verifică ", + "version_announcement_overlay_text_3": " și asigură-te că fișierul .env și configurația ta docker-compose sunt actualizate pentru a preveni orice erori de configurație, ÃŽn special dacă folosești WatchTower sau orice mecanism care gestionează actualizarea automată a aplicației server-ului tău.", + "version_announcement_overlay_title": "O nouă versiune pentru server este disponibilă 🎉", "version_history": "Istoric Versiuni", "version_history_item": "Instalat {version} pe data de {date}", "video": "Videoclip", diff --git a/i18n/ru.json b/i18n/ru.json index ab883f9aec..fd846b088d 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -26,6 +26,7 @@ "add_to_album": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧ", "add_to_album_bottom_sheet_added": "ДобавĐģĐĩĐŊĐž в {album}", "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐĩ в {album}", + "add_to_locked_folder": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", "add_to_shared_album": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ URL", "added_to_archive": "ДобавĐģĐĩĐŊĐž в Đ°Ņ€Ņ…Đ¸Đ˛", @@ -39,24 +40,25 @@ "authentication_settings_disable_all": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐŧĐĩŅ‚ĐžĐ´Ņ‹ Đ˛Ņ…ĐžĐ´Đ°? Đ’Ņ…ĐžĐ´ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐģĐŊĐžŅŅ‚ŅŒŅŽ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ.", "authentication_settings_reenable": "Đ§Ņ‚ĐžĐąŅ‹ ҁĐŊОва вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ КоĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "background_task_job": "ФОĐŊĐžĐ˛Ņ‹Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸", - "backup_database": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", - "backup_database_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", - "backup_keep_last_amount": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Ņ…Ņ€Đ°ĐŊиĐŧҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК", - "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", - "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_database": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅƒŅŽ ĐēĐžĐŋĐ¸ŅŽ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_database_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ даĐŧĐŋŅ‹ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_keep_last_amount": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Ņ…Ņ€Đ°ĐŊиĐŧҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅. ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊиĐĩ ĐŊĐĩ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐ¸Ņ€ŅƒĐĩŅ‚ŅŅ, Đ˛Ņ‹ ĐŊĐĩ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ в ҁĐģŅƒŅ‡Đ°Đĩ ŅĐąĐžŅ.", "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "cleanup": "ĐžŅ‡Đ¸ŅŅ‚Đēа", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ‹ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Đ´ĐģŅ: {job}", "config_set_by_file": "ĐĐ°ŅŅ‚Ņ€ĐžĐĩĐŊĐž ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ Ņ„Đ°ĐšĐģа ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Đ¸", "confirm_delete_library": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ \"{library}\"?", - "confirm_delete_library_assets": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃? Đ­Ņ‚Đž ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {# ŅĐžĐ´ĐĩŅ€ĐļиĐŧŅ‹Đš ĐžĐąŅŠĐĩĐēŅ‚} few {# ŅĐžĐ´ĐĩŅ€ĐļиĐŧҋ҅ ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {all # ŅĐžĐ´ĐĩŅ€ĐļиĐŧҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} иС Immich. ФаКĐģŅ‹ ĐžŅŅ‚Đ°ĐŊŅƒŅ‚ŅŅ ĐŊа Đ´Đ¸ŅĐēĐĩ.", + "confirm_delete_library_assets": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃? Đ­Ņ‚Đž ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС Immich. ФаКĐģŅ‹ ĐžŅŅ‚Đ°ĐŊŅƒŅ‚ŅŅ ĐŊа Đ´Đ¸ŅĐēĐĩ.", "confirm_email_below": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ, ввĐĩĐ´Đ¸Ņ‚Đĩ \"{email}\" ĐŊиĐļĐĩ", "confirm_reprocess_all_faces": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐģĐ¸Ņ†Đ°? Đ‘ŅƒĐ´ŅƒŅ‚ Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иĐŧĐĩĐŊа ŅĐž Đ˛ŅĐĩŅ… ĐģĐ¸Ņ†.", - "confirm_user_password_reset": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", + "confirm_user_password_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", + "confirm_user_pin_code_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", "create_job": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ СадаĐŊиĐĩ", - "cron_expression": "Đ’Ņ‹Ņ€Đ°ĐļĐĩĐŊиĐĩ cron", - "cron_expression_description": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ иĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиК в Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ cron. ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸, ОСĐŊаĐēĐžĐŧŅŒŅ‚ĐĩҁҌ ҁ Crontab Guru", - "cron_expression_presets": "ĐŸŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐēи Đ˛Ņ‹Ņ€Đ°ĐļĐĩĐŊиК cron", + "cron_expression": "Đ Đ°ŅĐŋĐ¸ŅĐ°ĐŊиĐĩ (Đ˛Ņ‹Ņ€Đ°ĐļĐĩĐŊиĐĩ ĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đēа cron)", + "cron_expression_description": "Đ§Đ°ŅŅ‚ĐžŅ‚Đ° и Đ˛Ņ€ĐĩĐŧŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ СадаĐŊĐ¸Ņ в Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đēа cron. Đ’ĐžŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚ĐĩҁҌ ĐŋŅ€Đ¸ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ Đ˛Đ¸ĐˇŅƒĐ°ĐģҌĐŊŅ‹Đŧ Ņ€ĐĩдаĐēŅ‚ĐžŅ€ĐžĐŧ Crontab Guru", + "cron_expression_presets": "Đ Đ°ŅĐŋĐ¸ŅĐ°ĐŊиĐĩ (ĐŋŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ)", "disable_login": "ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛Ņ…ĐžĐ´", "duplicate_detection_job_description": "ЗаĐŋ҃ҁĐēаĐĩŅ‚ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŋĐžŅ…ĐžĐļĐ¸Ņ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ĐŋŅ€Đ¸ ĐŋĐžĐŧĐžŅ‰Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐˇŅ€ĐĩĐŊĐ¸Ņ (ĐˇĐ°Đ˛Đ¸ŅĐ¸Ņ‚ ĐžŅ‚ ҃ĐŧĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа)", "exclusion_pattern_description": "ШайĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ ĐŋОСвОĐģŅŅŽŅ‚ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ и ĐŋаĐŋĐēи ĐŋŅ€Đ¸ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊии Đ˛Đ°ŅˆĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи. Đ­Ņ‚Đž ĐŋĐžĐģĐĩСĐŊĐž, ĐĩҁĐģи ҃ Đ˛Đ°Ņ ĐĩŅŅ‚ŅŒ ĐŋаĐŋĐēи, ŅĐžĐ´ĐĩŅ€ĐļĐ°Ņ‰Đ¸Đĩ Ņ„Đ°ĐšĐģŅ‹, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Đ˛Ņ‹ ĐŊĐĩ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, RAW-Ņ„Đ°ĐšĐģŅ‹.", @@ -192,26 +194,22 @@ "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ", "oauth_auto_register_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐŋŅ€Đ¸ Đ˛Ņ…ĐžĐ´Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ OAuth", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐēĐŊĐžĐŋĐēи", - "oauth_client_id": "ID КĐģиĐĩĐŊŅ‚Đ°", - "oauth_client_secret": "ĐĄĐĩĐēŅ€ĐĩŅ‚ КĐģиĐĩĐŊŅ‚Đ°", + "oauth_client_secret_description": "ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐĩҁĐģи PKCE (Proof Key for Code Exchange) ĐŊĐĩ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ OAuth ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ĐžĐŧ", "oauth_enable_description": "Đ’Ņ…ĐžĐ´ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ OAuth", - "oauth_issuer_url": "URL-Đ°Đ´Ņ€Đĩҁ ŅĐŧĐ¸Ņ‚ĐĩĐŊŅ‚Đ°", "oauth_mobile_redirect_uri": "URI Ņ€ĐĩĐ´Đ¸Ņ€ĐĩĐēŅ‚Đ° Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ URI Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", "oauth_mobile_redirect_uri_override_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩҁĐģи ĐŋĐžŅŅ‚Đ°Đ˛Ņ‰Đ¸Đē OAuth ĐŊĐĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ°ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ ĐŧОйиĐģҌĐŊĐžĐŗĐž URI, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, '{callback}'", - "oauth_profile_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ ĐŋОдĐŋĐ¸ŅĐ¸ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", - "oauth_profile_signing_algorithm_description": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧŅ‹Đš Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ° в ĐŋŅ€ĐžŅ„Đ¸ĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ.", - "oauth_scope": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", "oauth_settings": "OAuth", "oauth_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ˛Ņ…ĐžĐ´Đ° ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đš Ой ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", - "oauth_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ ĐŋОдĐŋĐ¸ŅĐ¸", "oauth_storage_label_claim": "ĐĸĐĩĐŗ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Đ¸ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "oauth_storage_label_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐˇĐ°Đ´Đ°Đ˛Đ°Ņ‚ŅŒ Đ´ĐģŅ ĐŧĐĩŅ‚Đēи Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ ŅŅ‚ĐžĐŗĐž ŅƒŅ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ.", "oauth_storage_quota_claim": "Đ—Đ°ŅĐ˛Đēа ĐŊа ĐēĐ˛ĐžŅ‚Ņƒ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "oauth_storage_quota_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ ĐēĐ˛ĐžŅ‚Ņƒ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐŊа СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ, ҃ĐēаСаĐŊĐŊĐžĐĩ в ŅŅ‚ĐžĐš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐĩ.", "oauth_storage_quota_default": "ĐšĐ˛ĐžŅ‚Đ° Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ (ГБ)", "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, ĐēĐžŅ‚ĐžŅ€Đ°Ņ ĐąŅƒĐ´ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒŅŅ, ĐĩҁĐģи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊĐĩ СадаĐŊа (ввĐĩĐ´Đ¸Ņ‚Đĩ 0 Đ´ĐģŅ ĐŊĐĩĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊĐŊОК ĐēĐ˛ĐžŅ‚Ņ‹).", + "oauth_timeout": "ĐĸаКĐŧĐ°ŅƒŅ‚ Đ´ĐģŅ СаĐŋŅ€ĐžŅĐžĐ˛", + "oauth_timeout_description": "МаĐēŅĐ¸ĐŧаĐģҌĐŊĐžĐĩ Đ˛Ņ€ĐĩĐŧŅ, в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž ĐžĐļĐ¸Đ´Đ°Ņ‚ŅŒ ĐžŅ‚Đ˛ĐĩŅ‚Đ°, в ĐŧиĐģĐģĐ¸ŅĐĩĐē҃ĐŊĐ´Đ°Ņ…", "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐŋŅƒŅ‚Đ¸", "offline_paths_description": "Đ­Ņ‚Đ¸ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ‹ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Đ˛Ņ‹ĐˇĐ˛Đ°ĐŊŅ‹ Ņ€ŅƒŅ‡ĐŊŅ‹Đŧ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩĐŧ Ņ„Đ°ĐšĐģОв, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐĩ ŅĐ˛ĐģŅŅŽŅ‚ŅŅ Ņ‡Đ°ŅŅ‚ŅŒŅŽ вĐŊĐĩ҈ĐŊĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "password_enable_description": "Đ’ĐžĐšŅ‚Đ¸ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ и ĐŋĐ°Ņ€ĐžĐģŅŽ", @@ -225,7 +223,7 @@ "registration_description": "ĐŸĐžŅĐēĐžĐģҌĐē҃ Đ˛Ņ‹ ŅĐ˛ĐģŅĐĩŅ‚ĐĩҁҌ ĐŋĐĩŅ€Đ˛Ņ‹Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ в ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ, ваĐŧ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ€Đ¸ŅĐ˛ĐžĐĩĐŊа Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°, и Đ˛Ņ‹ ĐąŅƒĐ´ĐĩŅ‚Đĩ ĐžŅ‚Đ˛ĐĩŅ‡Đ°Ņ‚ŅŒ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊŅ‹Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸. ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐąŅƒĐ´ĐĩŅ‚Đĩ ŅĐžĐˇĐ´Đ°Đ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹.", "repair_all": "ĐŸĐžŅ‡Đ¸ĐŊĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", "repair_matched_items": "ĐĄĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ {count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Ņƒ} few {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ}}", - "repaired_items": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž {count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚} few {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", + "repaired_items": "{count, plural, one {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ # ŅĐģĐĩĐŧĐĩĐŊŅ‚} many {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž # ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž # ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "require_password_change_on_login": "ĐĸŅ€ĐĩĐąĐžĐ˛Đ°Ņ‚ŅŒ ҁĐŧĐĩĐŊ҃ ĐŋĐ°Ņ€ĐžĐģŅ ĐŋŅ€Đ¸ ĐŋĐĩŅ€Đ˛ĐžĐŧ Đ˛Ņ…ĐžĐ´Đĩ", "reset_settings_to_default": "ĐĄĐąŅ€ĐžŅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ´Đž СĐŊĐ°Ņ‡ĐĩĐŊиК ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "reset_settings_to_recent_saved": "ĐĄĐąŅ€ĐžŅŅŒŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đē ĐŋĐžŅĐģĐĩĐ´ĐŊиĐŧ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊŅ‹Đŧ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧ", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "ĐĄŅ€ĐžĐē в Đ´ĐŊŅŅ…, ĐŋĐž Đ¸ŅŅ‚Đĩ҇ĐĩĐŊиĐĩ ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž ĐŋŅ€ĐžĐ¸ŅŅ…ĐžĐ´Đ¸Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и ĐĩĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛. Đ—Đ°Đ´Đ°Ņ‡Đ° ĐŋĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ в ĐŋĐžĐģĐŊĐžŅ‡ŅŒ. ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ ŅŅ‚ĐžĐš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐąŅƒĐ´ŅƒŅ‚ ŅƒŅ‡Ņ‚ĐĩĐŊŅ‹ ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ СаĐŋ҃ҁĐēĐĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸.", "user_delete_immediately": "АĐēĐēĐ°ŅƒĐŊŅ‚ и Ņ„Đ°ĐšĐģŅ‹ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user} ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊŅ‹ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ.", "user_delete_immediately_checkbox": "ПоĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и ĐĩĐŗĐž Ņ„Đ°ĐšĐģŅ‹ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ", + "user_details": "ДаĐŊĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "user_management": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи", "user_password_has_been_reset": "ĐŸĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐąŅ‹Đģ ŅĐąŅ€ĐžŅˆĐĩĐŊ:", "user_password_reset_description": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ и ŅĐžĐžĐąŅ‰Đ¸Ņ‚Đĩ ĐĩĐŧ҃, Ņ‡Ņ‚Đž ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ Đ˛Ņ…ĐžĐ´Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃ ĐŋĐ°Ņ€ĐžĐģҌ ĐŊ҃ĐļĐŊĐž ĐąŅƒĐ´ĐĩŅ‚ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ.", @@ -371,13 +370,17 @@ "admin_password": "ĐŸĐ°Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "administration": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", "advanced": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ", - "advanced_settings_log_level_title": "ĐŖŅ€ĐžĐ˛ĐĩĐŊҌ ĐģĐžĐŗĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ:", + "advanced_settings_enable_alternate_media_filter_subtitle": "Đ˜ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ Đ´ĐģŅ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Đ°Ņ†Đ¸Đ¸ ĐŧĐĩĐ´Đ¸Đ°Ņ„Đ°ĐšĐģОв вО Đ˛Ņ€ĐĩĐŧŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐŊа ĐžŅĐŊОвĐĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊҋ҅ ĐēŅ€Đ¸Ņ‚ĐĩŅ€Đ¸Đĩв. ĐŸŅ€ĐžĐąŅƒĐšŅ‚Đĩ Ņ‚ĐžĐģҌĐēĐž в Ņ‚ĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ, ĐĩҁĐģи ҃ Đ˛Đ°Ņ ĐĩŅŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ҁ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩĐŧ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", + "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНĐĸАЛĐŦНО] Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Đ° ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ аĐģŅŒĐąĐžĐŧОв аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", + "advanced_settings_log_level_title": "ĐŖŅ€ĐžĐ˛ĐĩĐŊҌ ĐģĐžĐŗĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ: {level}", "advanced_settings_prefer_remote_subtitle": "НĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ĐžŅ‡ĐĩĐŊҌ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°ŅŽŅ‚ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ŅƒĐšŅ‚Đĩ ŅŅ‚Ņƒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐē҃, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Đ˛ŅĐĩĐŗĐ´Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐģĐ¸ŅŅŒ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "advanced_settings_prefer_remote_title": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚Đž ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", - "advanced_settings_proxy_headers_subtitle": "ОĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēŅĐ¸-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Immich Đ´ĐžĐģĐļĐĩĐŊ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ҁ ĐēаĐļĐ´Ņ‹Đŧ ҁĐĩŅ‚ĐĩĐ˛Ņ‹Đŧ СаĐŋŅ€ĐžŅĐžĐŧ.", + "advanced_settings_proxy_headers_subtitle": "ОĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēŅĐ¸-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Immich Đ´ĐžĐģĐļĐĩĐŊ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ҁ ĐēаĐļĐ´Ņ‹Đŧ ҁĐĩŅ‚ĐĩĐ˛Ņ‹Đŧ СаĐŋŅ€ĐžŅĐžĐŧ", "advanced_settings_proxy_headers_title": "Đ—Đ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēŅĐ¸", "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°. ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊҋ҅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžĐ˛.", "advanced_settings_self_signed_ssl_title": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊŅ‹Đĩ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Ņ‹", + "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģŅŅ‚ŅŒ иĐģи Đ˛ĐžŅŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚ ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ, ĐēĐžĐŗĐ´Đ° ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ҇ĐĩŅ€ĐĩС вĐĩĐą-иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ", + "advanced_settings_sync_remote_deletions_title": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒĐ´Đ°ĐģĐĩĐŊĐŊҋ҅ ŅƒĐ´Đ°ĐģĐĩĐŊиК [ЭКСПЕРИМЕНĐĸАЛĐŦНО]", "advanced_settings_tile_subtitle": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "advanced_settings_troubleshooting_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ€Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ вОСĐŧĐžĐļĐŊĐžŅŅ‚Đ¸ Đ´ĐģŅ Ņ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", "advanced_settings_troubleshooting_title": "Đ Đĩ҈ĐĩĐŊиĐĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", "album_share_no_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅŅ‚Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", "album_thumbnail_card_item": "1 ŅĐģĐĩĐŧĐĩĐŊŅ‚", - "album_thumbnail_card_items": "{} ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛", - "album_thumbnail_card_shared": "¡ ĐžĐąŅ‰Đ¸Đš", - "album_thumbnail_shared_by": "ПодĐĩĐģиĐģŅŅ {}", + "album_thumbnail_card_items": "{count} ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛", + "album_thumbnail_card_shared": " ¡ ĐžĐąŅ‰Đ¸Đš", + "album_thumbnail_shared_by": "ПодĐĩĐģиĐģŅŅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {user}", "album_updated": "АĐģŅŒĐąĐžĐŧ ОйĐŊОвĐģŅ‘ĐŊ", "album_updated_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐŊĐžĐ˛Ņ‹Ņ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "album_user_left": "Đ’Ņ‹ ĐŋĐžĐēиĐŊ҃Đģи {album}", @@ -417,7 +420,7 @@ "album_viewer_page_share_add_users": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš", "album_with_link_access": "ПодĐĩĐģĐ¸Ņ‚ĐĩҁҌ ҁҁҋĐģĐēОК ĐŊа аĐģŅŒĐąĐžĐŧ, Ņ‡Ņ‚ĐžĐąŅ‹ Đ˛Đ°ŅˆĐ¸ Đ´Ņ€ŅƒĐˇŅŒŅ ĐŧĐžĐŗĐģи ĐĩĐŗĐž ĐŋĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ.", "albums": "АĐģŅŒĐąĐžĐŧŅ‹", - "albums_count": "{count, plural, one {{count, number} аĐģŅŒĐąĐžĐŧ} few {{count, number} аĐģŅŒĐąĐžĐŧа} many {{count, number} аĐģŅŒĐąĐžĐŧОв} other {{count, number} аĐģŅŒĐąĐžĐŧОв}}", + "albums_count": "{count, plural, one {{count, number} аĐģŅŒĐąĐžĐŧ} few {{count, number} аĐģŅŒĐąĐžĐŧа} many {{count, number} аĐģŅŒĐąĐžĐŧОв} other {{count, number} аĐģŅŒĐąĐžĐŧа}}", "all": "Đ’ŅĐĩ", "all_albums": "Đ’ŅĐĩ аĐģŅŒĐąĐžĐŧŅ‹", "all_people": "Đ’ŅĐĩ ĐģŅŽĐ´Đ¸", @@ -440,11 +443,11 @@ "archive": "ĐŅ€Ņ…Đ¸Đ˛", "archive_or_unarchive_photo": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ иĐģи Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚Đž", "archive_page_no_archived_assets": "В Đ°Ņ€Ņ…Đ¸Đ˛Đĩ ҁĐĩĐšŅ‡Đ°Ņ ĐŋŅƒŅŅ‚Đž", - "archive_page_title": "ĐŅ€Ņ…Đ¸Đ˛ ({})", + "archive_page_title": "ĐŅ€Ņ…Đ¸Đ˛ ({count})", "archive_size": "РаСĐŧĐĩŅ€ Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "archive_size_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа Ņ€Đ°ĐˇĐŧĐĩŅ€Đ° Đ°Ņ€Ņ…Đ¸Đ˛Đ° Đ´ĐģŅ ҁĐēĐ°Ņ‡Đ¸Đ˛Đ°ĐŊĐ¸Ņ (в GiB)", "archived": "ĐŅ€Ņ…Đ¸Đ˛", - "archived_count": "{count, plural, other {ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐž #}}", + "archived_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŊĐĩҁґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž}} в Đ°Ņ€Ņ…Đ¸Đ˛", "are_these_the_same_person": "Đ­Ņ‚Đž ОдиĐŊ и Ņ‚ĐžŅ‚ ĐļĐĩ ҇ĐĩĐģОвĐĩĐē?", "are_you_sure_to_do_this": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅŅ‚Đž ŅĐ´ĐĩĐģĐ°Ņ‚ŅŒ?", "asset_action_delete_err_read_only": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) Ņ‚ĐžĐģҌĐēĐž Đ´ĐģŅ ҇҂ĐĩĐŊĐ¸Ņ, ĐŋŅ€ĐžĐŋ҃ҁĐē...", @@ -473,22 +476,22 @@ "asset_viewer_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "asset_viewer_settings_title": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК", "assets": "ĐžĐąŅŠĐĩĐē҂ҋ", - "assets_added_count": "ДобавĐģĐĩĐŊĐž {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", - "assets_added_to_album_count": "В аĐģŅŒĐąĐžĐŧ дОйавĐģĐĩĐŊĐž {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", - "assets_added_to_name_count": "ДобавĐģĐĩĐŊĐž {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} в {hasName, select, true {{name}} other {ĐŊĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ}}", - "assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", - "assets_deleted_permanently": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ŅƒĐ´Đ°ĐģĐĩĐŊ(Ņ‹) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "assets_deleted_permanently_from_server": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ŅƒĐ´Đ°ĐģĐĩĐŊ(Ņ‹) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", - "assets_moved_to_trash_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "assets_permanently_deleted_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ŅƒĐ´Đ°ĐģĐĩĐŊĐž ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "assets_removed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ŅƒĐ´Đ°ĐģĐĩĐŊĐž", - "assets_removed_permanently_from_device": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ŅƒĐ´Đ°ĐģĐĩĐŊ(Ņ‹) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "assets_added_count": "{count, plural, one {ДобавĐģĐĩĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {ДобавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ДобавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", + "assets_added_to_album_count": "В аĐģŅŒĐąĐžĐŧ {count, plural, one {дОйавĐģĐĩĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {дОйавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {дОйавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", + "assets_added_to_name_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ дОйавĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ дОйавĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° дОйавĐģĐĩĐŊĐž}} в {hasName, select, true {аĐģŅŒĐąĐžĐŧ {name}} other {ĐŊĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ}}", + "assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", + "assets_deleted_permanently": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ŅƒĐ´Đ°ĐģĐĩĐŊĐž ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", + "assets_deleted_permanently_from_server": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", + "assets_moved_to_trash_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", + "assets_permanently_deleted_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", + "assets_removed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", + "assets_removed_permanently_from_device": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "assets_restore_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ иС ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ! ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŊиĐŧаĐŊиĐĩ, Ņ‡Ņ‚Đž ĐģŅŽĐąŅ‹Đĩ ĐžŅ„Ņ„ĐģаКĐŊ-ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹ Ņ‚Đ°ĐēиĐŧ ҁĐŋĐžŅĐžĐąĐžĐŧ.", - "assets_restored_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž", - "assets_restored_successfully": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ(Ņ‹)", - "assets_trashed": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ĐŋĐžĐŧĐĩ҉ĐĩĐŊ(Ņ‹) в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "assets_trashed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "assets_trashed_from_server": "{} ĐžĐąŅŠĐĩĐēŅ‚(Ņ‹) ĐŋĐžĐŧĐĩ҉ĐĩĐŊ(Ņ‹) в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Immich", + "assets_restored_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž}}", + "assets_restored_successfully": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž", + "assets_trashed": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŋĐžĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", + "assets_trashed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", + "assets_trashed_from_server": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŋĐžĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Immich", "assets_were_part_of_album_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ҃ĐļĐĩ в аĐģŅŒĐąĐžĐŧĐĩ", "authorized_devices": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "automatic_endpoint_switching_subtitle": "ПодĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ ĐģĐžĐēаĐģҌĐŊĐž ĐŋĐž Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК ҁĐĩŅ‚Đ¸ и Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ‹Đĩ Đ°Đ´Ņ€ĐĩŅĐ° в иĐŊĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ", @@ -497,20 +500,20 @@ "back_close_deselect": "Назад, СаĐēŅ€Ņ‹Ņ‚ŅŒ иĐģи ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€", "background_location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ в Ņ„ĐžĐŊĐĩ", "background_location_permission_content": "Đ§Ņ‚ĐžĐąŅ‹ ŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸ в Ņ„ĐžĐŊĐĩ, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ *Đ˛ŅĐĩĐŗĐ´Đ°* ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸Đŧ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ‚ĐžŅ‡ĐŊĐžĐŧ҃ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", - "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ({})", - "backup_album_selection_page_albums_tap": "НаĐļĐŧĐ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ,\nĐŊаĐļĐŧĐ¸Ņ‚Đĩ дваĐļĐ´Ņ‹, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", + "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ({count})", + "backup_album_selection_page_albums_tap": "НаĐļĐŧĐ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, дваĐļĐ´Ņ‹, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", "backup_album_selection_page_assets_scatter": "Đ’Đ°ŅˆĐ¸ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ и видĐĩĐž ĐŧĐžĐŗŅƒŅ‚ ĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚ŅŒŅŅ в Ņ€Đ°ĐˇĐŊҋ҅ аĐģŅŒĐąĐžĐŧĐ°Ņ…. Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ Đ˛Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ, ĐēаĐēиĐĩ аĐģŅŒĐąĐžĐŧŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, а ĐēаĐēиĐĩ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иС Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ.", "backup_album_selection_page_select_albums": "Đ’Ņ‹ĐąĐžŅ€ аĐģŅŒĐąĐžĐŧОв", "backup_album_selection_page_selection_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Đ˛Ņ‹ĐąĐžŅ€Đĩ", "backup_album_selection_page_total_assets": "Đ’ŅĐĩĐŗĐž ҃ĐŊиĐēаĐģҌĐŊҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "backup_all": "Đ’ŅĐĩ", "backup_background_service_backup_failed_message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ ĐŋĐžĐŋҋ҂Đēаâ€Ļ", - "backup_background_service_connection_failed_message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ ĐŋĐžĐŋҋ҂Đēа...", - "backup_background_service_current_upload_notification": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ {}", + "backup_background_service_connection_failed_message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ ĐŋĐžĐŋҋ҂Đēаâ€Ļ", + "backup_background_service_current_upload_notification": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ {filename}", "backup_background_service_default_notification": "ĐŸĐžĐ¸ŅĐē ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛â€Ļ", "backup_background_service_error_title": "ĐžŅˆĐ¸ĐąĐēа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Đ˛Đ°ŅˆĐ¸Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛â€Ļ", - "backup_background_service_upload_failure_notification": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи {}", + "backup_background_service_upload_failure_notification": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи {filename}", "backup_controller_page_albums": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ аĐģŅŒĐąĐžĐŧОв", "backup_controller_page_background_app_refresh_disabled_content": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ„ĐžĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ в ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи > ĐžĐąŅ‰Đ¸Đĩ > ФОĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžĐŊОвОĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ.", "backup_controller_page_background_app_refresh_disabled_title": "ФОĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", @@ -521,22 +524,21 @@ "backup_controller_page_background_battery_info_title": "ОĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐąĐ°Ņ‚Đ°Ņ€Đĩи", "backup_controller_page_background_charging": "ĐĸĐžĐģҌĐēĐž вО Đ˛Ņ€ĐĩĐŧŅ ĐˇĐ°Ņ€ŅĐ´Đēи", "backup_controller_page_background_configure_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", - "backup_controller_page_background_delay": "ĐžŅ‚ĐģĐžĐļĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛: {}", + "backup_controller_page_background_delay": "ĐžŅ‚ĐģĐžĐļĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛: {duration}", "backup_controller_page_background_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐģŅŽĐąŅ‹Ņ… ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐąĐĩС ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ", "backup_controller_page_background_is_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_background_is_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_background_turn_off": "Đ’Ņ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", "backup_controller_page_background_turn_on": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", - "backup_controller_page_background_wifi": "ĐĸĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС WiFi", + "backup_controller_page_background_wifi": "ĐĸĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС Wi-Fi", "backup_controller_page_backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_controller_page_backup_selected": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž: ", "backup_controller_page_backup_sub": "Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž", - "backup_controller_page_created": "ХОСдаĐŊĐž: {}", + "backup_controller_page_created": "ХОСдаĐŊĐž: {date}", "backup_controller_page_desc_backup": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋŅ€Đ¸ ĐžŅ‚ĐēŅ€Ņ‹Ņ‚Đ¸Đ¸ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ.", - "backup_controller_page_excluded": "Đ˜ŅĐēĐģŅŽŅ‡ĐĩĐŊŅ‹:", - "backup_controller_page_failed": "НĐĩŅƒĐ´Đ°Ņ‡ĐŊҋ҅ ({})", - "backup_controller_page_filename": "ИĐŧŅ Ņ„Đ°ĐšĐģа: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Đ˜ŅĐēĐģŅŽŅ‡ĐĩĐŊŅ‹: ", + "backup_controller_page_failed": "НĐĩŅƒĐ´Đ°Ņ‡ĐŊҋ҅ ({count})", + "backup_controller_page_filename": "ИĐŧŅ Ņ„Đ°ĐšĐģа: {filename} [{size}]", "backup_controller_page_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŧ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊии", "backup_controller_page_none_selected": "ĐĐ¸Ņ‡ĐĩĐŗĐž ĐŊĐĩ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", "backup_controller_page_remainder": "ĐžŅŅ‚Đ°ĐģĐžŅŅŒ", @@ -545,7 +547,7 @@ "backup_controller_page_start_backup": "ĐĐ°Ņ‡Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_controller_page_status_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ Đ˛Ņ‹ĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_status_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž", - "backup_controller_page_storage_format": "{} иС {} Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐž", + "backup_controller_page_storage_format": "{used} иС {total} Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐž", "backup_controller_page_to_backup": "АĐģŅŒĐąĐžĐŧŅ‹ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "backup_controller_page_total_sub": "Đ’ŅĐĩ ҃ĐŊиĐēаĐģҌĐŊŅ‹Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž иС Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊҋ҅ аĐģŅŒĐąĐžĐŧОв", "backup_controller_page_turn_off": "Đ’Ņ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", @@ -560,31 +562,35 @@ "backup_options_page_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_setting_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŗĐž и Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "backward": "Назад", + "biometric_auth_enabled": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ вĐēĐģŅŽŅ‡ĐĩĐŊа", + "biometric_locked_out": "ВаĐŧ СаĐēҀҋ҂ Đ´ĐžŅŅ‚ŅƒĐŋ Đē йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", + "biometric_no_options": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа", + "biometric_not_available": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "birthdate_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", "birthdate_set_description": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ Ņ€Đ°ŅŅ‡ĐĩŅ‚Đ° Đ˛ĐžĐˇŅ€Đ°ŅŅ‚Đ° ŅŅ‚ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа ĐŊа ĐŧĐžĐŧĐĩĐŊŅ‚ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸.", "blurred_background": "РаСĐŧŅ‹Ņ‚Ņ‹Đš Ņ„ĐžĐŊ", "bugs_and_feature_requests": "ĐžŅˆĐ¸ĐąĐēи и СаĐŋŅ€ĐžŅŅ‹", "build": "ĐĄĐąĐžŅ€Đēа", "build_image": "ВĐĩŅ€ŅĐ¸Ņ ŅĐąĐžŅ€Đēи", - "bulk_delete_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŧĐ°ŅŅĐžĐ˛Đž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}? Đ­Ņ‚Đž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ ŅĐ°ĐŧŅ‹Đš йОĐģŅŒŅˆĐžĐš Ņ„Đ°ĐšĐģ иС ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋŅ‹ и ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐ¸Ņ‚ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹. Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", - "bulk_keep_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}? Đ­Ņ‚Đž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ Đ˛ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹.", - "bulk_trash_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŧĐ°ŅŅĐžĐ˛Đž ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}? Đ­Ņ‚Đž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ ŅĐ°ĐŧŅ‹Đš йОĐģŅŒŅˆĐžĐš Ņ„Đ°ĐšĐģ иС ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋŅ‹ и ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃.", + "bulk_delete_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} many {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ‘ŅƒĐ´ĐĩŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅĐ°ĐŧŅ‹Đš йОĐģŅŒŅˆĐžĐš Ņ„Đ°ĐšĐģ в ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋĐĩ, а ĐĩĐŗĐž Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹. Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", + "bulk_keep_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} many {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ­Ņ‚Đž ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ Đ˛ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹.", + "bulk_trash_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} many {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ‘ŅƒĐ´ĐĩŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅĐ°ĐŧŅ‹Đš йОĐģŅŒŅˆĐžĐš Ņ„Đ°ĐšĐģ в ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋĐĩ, а ĐĩĐŗĐž Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃.", "buy": "ĐŸŅ€Đ¸ĐžĐąŅ€ĐĩŅ‚ĐĩĐŊиĐĩ ĐģĐ¸Ņ†ĐĩĐŊСии Immich", - "cache_settings_album_thumbnails": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ† йийĐģĐ¸ĐžŅ‚ĐĩĐēи ({} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", + "cache_settings_album_thumbnails": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ† йийĐģĐ¸ĐžŅ‚ĐĩĐēи ({count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "cache_settings_clear_cache_button": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēŅŅˆ", "cache_settings_clear_cache_button_title": "ĐžŅ‡Đ¸Ņ‰Đ°ĐĩŅ‚ ĐēŅŅˆ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. Đ­Ņ‚Đž ĐŊĐĩĐŗĐ°Ņ‚Đ¸Đ˛ĐŊĐž ĐŋОвĐģĐ¸ŅĐĩŅ‚ ĐŊа ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ĐĩĐģҌĐŊĐžŅŅ‚ŅŒ, ĐŋĐžĐēа ĐēŅŅˆ ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ŅĐžĐˇĐ´Đ°ĐŊ СаĐŊОвО.", "cache_settings_duplicated_assets_clear_button": "ОЧИСĐĸИĐĸĐŦ", "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž и видĐĩĐž, СаĐŊĐĩҁĐĩĐŊĐŊŅ‹Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ в ҇ĐĩŅ€ĐŊŅ‹Đš ҁĐŋĐ¸ŅĐžĐē", - "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐĩŅŅ ĐžĐąŅŠĐĩĐē҂ҋ ({})", - "cache_settings_image_cache_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ({} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", + "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐĩŅŅ ĐžĐąŅŠĐĩĐē҂ҋ ({count})", + "cache_settings_image_cache_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ({count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "cache_settings_statistics_album": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", - "cache_settings_statistics_assets": "{} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ({})", + "cache_settings_statistics_assets": "{count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ({size})", "cache_settings_statistics_full": "ПоĐģĐŊŅ‹Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "cache_settings_statistics_shared": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ ĐžĐąŅ‰Đ¸Ņ… аĐģŅŒĐąĐžĐŧОв", "cache_settings_statistics_thumbnail": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹", "cache_settings_statistics_title": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ°", "cache_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐēŅŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩĐŧ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", - "cache_settings_thumbnail_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ ({} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", + "cache_settings_thumbnail_size": "РаСĐŧĐĩŅ€ ĐēŅŅˆĐ° ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ ({count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "cache_settings_tile_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐģĐžĐēаĐģҌĐŊŅ‹Đŧ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩĐŧ", "cache_settings_tile_title": "ЛоĐēаĐģҌĐŊĐžĐĩ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", "cache_settings_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēŅŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", @@ -597,7 +603,9 @@ "cannot_merge_people": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "cannot_undo_this_action": "Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", "cannot_update_the_description": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", + "cast": "ĐĸŅ€Đ°ĐŊҁĐģĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "change_date": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", + "change_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "change_display_order": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžŅ€ŅĐ´ĐžĐē ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "change_expiration_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ€ĐĩĐŧŅ ĐžĐēĐžĐŊŅ‡Đ°ĐŊĐ¸Ņ", "change_location": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -610,12 +618,13 @@ "change_password_form_new_password": "ĐĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", "change_password_form_password_mismatch": "ĐŸĐ°Ņ€ĐžĐģи ĐŊĐĩ ŅĐžĐ˛ĐŋĐ°Đ´Đ°ŅŽŅ‚", "change_password_form_reenter_new_password": "ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ввĐĩĐ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", + "change_pin_code": "ИСĐŧĐĩĐŊĐĩĐŊиĐĩ PIN-ĐēОда", "change_your_password": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŋĐ°Ņ€ĐžĐģҌ", "changed_visibility_successfully": "ВидиĐŧĐžŅŅ‚ŅŒ ҃ҁĐŋĐĩ҈ĐŊĐž иСĐŧĐĩĐŊĐĩĐŊа", "check_all": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅŅ‘", "check_corrupt_asset_backup": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа ĐŋĐžĐ˛Ņ€ĐĩĐļĐ´ĐĩĐŊĐŊҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК", "check_corrupt_asset_backup_button": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ", - "check_corrupt_asset_backup_description": "ĐŸŅ€ĐžĐ˛ĐžĐ´Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Ņ‚ĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС Wi-Fi и Ņ‚ĐžĐģҌĐēĐž ĐŋĐžŅĐģĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. Đ­Ņ‚Đ° ĐžĐŋĐĩŅ€Đ°Ņ†Đ¸Ņ ĐŧĐžĐļĐĩŅ‚ СаĐŊŅŅ‚ŅŒ ĐŊĐĩҁĐēĐžĐģҌĐēĐž ĐŧиĐŊŅƒŅ‚", + "check_corrupt_asset_backup_description": "ЗаĐŋ҃ҁĐēĐ°ĐšŅ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Ņ‚ĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС Wi-Fi и ĐŋĐžŅĐģĐĩ ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊОК ĐēĐžĐŋии Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. ОĐŋĐĩŅ€Đ°Ņ†Đ¸Ņ ĐŧĐžĐļĐĩŅ‚ СаĐŊŅŅ‚ŅŒ ĐŊĐĩҁĐēĐžĐģҌĐēĐž ĐŧиĐŊŅƒŅ‚.", "check_logs": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ ĐļŅƒŅ€ĐŊаĐģŅ‹", "choose_matching_people_to_merge": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋĐžĐ´Ņ…ĐžĐ´ŅŅ‰Đ¸Ņ… ĐģŅŽĐ´ĐĩĐš Đ´ĐģŅ ҁĐģĐ¸ŅĐŊĐ¸Ņ", "city": "Đ“ĐžŅ€ĐžĐ´", @@ -624,14 +633,13 @@ "clear_all_recent_searches": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐŊĐĩдавĐŊиĐĩ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ‹ ĐŋĐžĐ¸ŅĐēа", "clear_message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ", "clear_value": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ", "client_cert_import": "ИĐŧĐŋĐžŅ€Ņ‚", "client_cert_import_success_msg": "КĐģиĐĩĐŊ҂ҁĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊ", "client_cert_invalid_msg": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Ņ„Đ°ĐšĐģ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° иĐģи ĐŊĐĩвĐĩŅ€ĐŊŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", "client_cert_remove_msg": "КĐģиĐĩĐŊ҂ҁĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ ŅƒĐ´Đ°ĐģĐĩĐŊ", "client_cert_subtitle": "ПоддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ Ņ‚ĐžĐģҌĐēĐž Ņ„ĐžŅ€ĐŧĐ°Ņ‚ PKCS12 (.p12, .pfx). ИĐŧĐŋĐžŅ€Ņ‚/ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž Ņ‚ĐžĐģҌĐēĐž ĐŋĐĩŅ€ĐĩĐ´ Đ˛Ņ…ĐžĐ´ĐžĐŧ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃", - "client_cert_title": "КĐģиĐĩĐŊ҂ҁĐēиК SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ ", + "client_cert_title": "КĐģиĐĩĐŊ҂ҁĐēиК SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚", "clockwise": "По Ņ‡Đ°ŅĐžĐ˛ĐžĐš", "close": "ЗаĐēŅ€Ņ‹Ņ‚ŅŒ", "collapse": "ХвĐĩŅ€ĐŊŅƒŅ‚ŅŒ", @@ -647,20 +655,22 @@ "completed": "ЗавĐĩŅ€ŅˆĐĩĐŊĐž", "confirm": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", "confirm_admin_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "confirm_delete_face": "Đ’Ņ‹ Ņ‚ĐžŅ‡ĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đž {name} иС ĐžĐąŅŠĐĩĐēŅ‚Đ°?", + "confirm_delete_face": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа {name} иС ĐžĐąŅŠĐĩĐēŅ‚Đ°?", "confirm_delete_shared_link": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃?", "confirm_keep_this_delete_others": "Đ’ŅĐĩ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐŗŅ€ŅƒĐŋĐŋĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹, ĐēŅ€ĐžĐŧĐĩ ŅŅ‚ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ?", + "confirm_new_pin_code": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš PIN-ĐēОд", "confirm_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ", + "connected_to": "ПодĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē", "contain": "ВĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ", - "control_bottom_app_bar_album_info_shared": "{} ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛ ¡ ĐžĐąŅ‰Đ¸Đš", + "control_bottom_app_bar_album_info_shared": "{count} ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛ ¡ ĐžĐąŅ‰Đ¸Đš", "control_bottom_app_bar_create_new_album": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "control_bottom_app_bar_delete_from_immich": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Immich\n", + "control_bottom_app_bar_delete_from_immich": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Immich", "control_bottom_app_bar_delete_from_local": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ҁ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "control_bottom_app_bar_edit_location": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚Đž", "control_bottom_app_bar_edit_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁҁҋĐģĐēОК", "control_bottom_app_bar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ", "control_bottom_app_bar_trash_from_immich": "В ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "copied_image_to_clipboard": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа.", @@ -683,8 +693,8 @@ "create_link_to_share": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐžĐąŅ‰ĐĩĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", "create_link_to_share_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Đ˛ŅĐĩĐŧ, ҃ ĐēĐžĐŗĐž ĐĩŅŅ‚ŅŒ ҁҁҋĐģĐēа, ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", "create_new": "СОЗДАĐĸĐŦ НОВĐĢЙ", - "create_new_person": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", - "create_new_person_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ĐŊОвОĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", + "create_new_person": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", + "create_new_person_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", "create_new_user": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "create_shared_album_page_share_add_assets": "ДОБАВИĐĸĐŦ ОБĐĒЕКĐĸĐĢ", "create_shared_album_page_share_select_photos": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", @@ -692,19 +702,18 @@ "create_tag_description": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš Ņ‚ĐĩĐŗ. ДĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ‚ĐĩĐŗĐžĐ˛ ввĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐžĐģĐŊŅ‹Đš ĐŋŅƒŅ‚ŅŒ Đē Ņ‚ĐĩĐŗŅƒ, вĐēĐģŅŽŅ‡Đ°Ņ ҁĐģŅŅˆĐ¸.", "create_user": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "created": "ХОСдаĐŊ", + "created_at": "ХОСдаĐŊ", "crop": "ĐžĐąŅ€ĐĩĐˇĐ°Ņ‚ŅŒ", "curated_object_page_title": "ĐŸŅ€ĐĩĐ´ĐŧĐĩ҂ҋ", "current_device": "ĐĸĐĩĐēŅƒŅ‰ĐĩĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", + "current_pin_code": "ĐĸĐĩĐēŅƒŅ‰Đ¸Đš PIN-ĐēОд", "current_server_address": "ĐĸĐĩĐēŅƒŅ‰Đ¸Đš Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК Ņ€ĐĩĐŗĐ¸ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Đ´Đ°Ņ‚ и Ņ‡Đ¸ŅĐĩĐģ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ŅĐˇŅ‹Đēа и Ņ€ĐĩĐŗĐ¸ĐžĐŊа", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "ĐĸŅ‘ĐŧĐŊŅ‹Đš", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅĐģĐĩ", "date_and_time": "Đ”Đ°Ņ‚Đ° и Đ’Ņ€ĐĩĐŧŅ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", "date_range": "ДиаĐŋаСОĐŊ Đ´Đ°Ņ‚", "day": "ДĐĩĐŊҌ", @@ -763,7 +772,7 @@ "download_enqueue": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в ĐžŅ‡ĐĩŅ€Đĩди", "download_error": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "download_failed": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ", - "download_filename": "Ņ„Đ°ĐšĐģ: {}", + "download_filename": "Ņ„Đ°ĐšĐģ: {filename}", "download_finished": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐžĐēĐžĐŊ҇ĐĩĐŊа", "download_include_embedded_motion_videos": "Đ’ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ видĐĩĐž", "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ видĐĩĐž, Đ˛ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ в ĐļĐ¸Đ˛Ņ‹Đĩ Ņ„ĐžŅ‚Đž, в видĐĩ ĐžŅ‚Đ´ĐĩĐģҌĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģа", @@ -787,6 +796,8 @@ "edit_avatar": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", "edit_date": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", "edit_date_and_time": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ и Đ˛Ņ€ĐĩĐŧŅ", + "edit_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", + "edit_description_prompt": "ĐŖĐēаĐļĐ¸Ņ‚Đĩ ĐŊОвОĐĩ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ:", "edit_exclusion_pattern": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ŅˆĐ°ĐąĐģĐžĐŊа Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", "edit_faces": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐģĐ¸Ņ†", "edit_import_path": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", @@ -796,10 +807,10 @@ "edit_location": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "edit_location_dialog_title": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "edit_name": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ иĐŧŅ", - "edit_people": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", + "edit_people": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "edit_tag": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", "edit_title": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ—Đ°ĐŗĐžĐģОвОĐē", - "edit_user": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", + "edit_user": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "edited": "ĐžŅ‚Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐž", "editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "editor_close_without_save_prompt": "ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", @@ -807,19 +818,23 @@ "editor_crop_tool_h2_aspect_ratios": "ĐĄĐžĐžŅ‚ĐŊĐžŅˆĐĩĐŊĐ¸Ņ ŅŅ‚ĐžŅ€ĐžĐŊ", "editor_crop_tool_h2_rotation": "Đ’Ņ€Đ°Ņ‰ĐĩĐŊиĐĩ", "email": "Đ­ĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐ°Ņ ĐŋĐžŅ‡Ņ‚Đ°", - "empty_folder": "This folder is empty", + "email_notifications": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ", + "empty_folder": "ĐŸŅƒŅŅ‚Đ°Ņ ĐŋаĐŋĐēа", "empty_trash": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "empty_trash_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃? Đ’ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Immich.\nĐ’Ņ‹ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚Đĩ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", "enable": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", + "enable_biometric_auth_description": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžĐš PIN-ĐēОд Đ´ĐģŅ вĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "enabled": "ВĐēĐģŅŽŅ‡ĐĩĐŊĐž", "end_date": "Đ”Đ°Ņ‚Đ° ĐžĐēĐžĐŊŅ‡Đ°ĐŊĐ¸Ņ", "enqueued": "ЗаĐŊĐĩҁĐĩĐŊĐž в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ", "enter_wifi_name": "ВвĐĩĐ´Đ¸Ņ‚Đĩ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸", + "enter_your_pin_code": "ВвĐĩĐ´Đ¸Ņ‚Đĩ Đ˛Đ°Ņˆ PIN-ĐēОд", + "enter_your_pin_code_subtitle": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžĐš PIN-ĐēОд Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ", "error": "ĐžŅˆĐ¸ĐąĐēа", "error_change_sort_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи аĐģŅŒĐąĐžĐŧа", "error_delete_face": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐģĐ¸Ņ†Đ° иС ĐžĐąŅŠĐĩĐēŅ‚Đ°", "error_loading_image": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", - "error_saving_image": "ĐžŅˆĐ¸ĐąĐēа: {}", + "error_saving_image": "ĐžŅˆĐ¸ĐąĐēа: {error}", "error_title": "ĐžŅˆĐ¸ĐąĐēа - Đ§Ņ‚Đž-Ņ‚Đž ĐŋĐžŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "errors": { "cannot_navigate_next_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ҃ ĐžĐąŅŠĐĩĐēŅ‚Ņƒ", @@ -827,7 +842,7 @@ "cant_apply_changes": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ", "cant_change_activity": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ {enabled, select, true {ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ} other {вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ}} аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ", "cant_change_asset_favorite": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ \"Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ\" Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°", - "cant_change_metadata_assets_count": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ ҃ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€ŅĐ°} few {# Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} many {# Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛}}", + "cant_change_metadata_assets_count": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ ҃ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", "cant_get_faces": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đ°", "cant_get_number_of_comments": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đĩв", "cant_search_people": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē ĐģŅŽĐ´ĐĩĐš", @@ -849,10 +864,12 @@ "failed_to_keep_this_delete_others": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ и ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐ¸Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "failed_to_load_asset": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи ĐžĐąŅŠĐĩĐēŅ‚Đ°", "failed_to_load_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", + "failed_to_load_notifications": "ХйОК ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "failed_to_load_people": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "failed_to_remove_product_key": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐēĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ°", "failed_to_stack_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "failed_to_unstack_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Ņ€Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", + "failed_to_update_notification_status": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ", "import_path_already_exists": "Đ­Ņ‚ĐžŅ‚ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ° ҃ĐļĐĩ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒĐĩŅ‚.", "incorrect_email_or_password": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Đ°Đ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹ иĐģи ĐŋĐ°Ņ€ĐžĐģҌ", "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚ŅŒ} other {# ĐŋŅƒŅ‚ĐĩĐš}} ĐŊĐĩ ĐŋŅ€ĐžŅˆĐģи ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃", @@ -870,10 +887,11 @@ "unable_to_archive_unarchive": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ {archived, select, true {Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ} other {Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ}}", "unable_to_change_album_user_role": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ в аĐģŅŒĐąĐžĐŧĐĩ", "unable_to_change_date": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", + "unable_to_change_description": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "unable_to_change_favorite": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ \"Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ\" Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°", "unable_to_change_location": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "unable_to_change_password": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", - "unable_to_change_visibility": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ видиĐŧĐžŅŅ‚ŅŒ Đ´ĐģŅ {count, plural, one {# ҇ĐĩĐģОвĐĩĐēа} few {# ĐģŅŽĐ´ĐĩĐš} many {# ĐģŅŽĐ´ĐĩĐš} other {# ĐģŅŽĐ´ĐĩĐš}}", + "unable_to_change_visibility": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ видиĐŧĐžŅŅ‚ŅŒ ҃ {count, plural, one {# ҇ĐĩĐģОвĐĩĐēа} other {# ҇ĐĩĐģОвĐĩĐē}}", "unable_to_complete_oauth_login": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ…ĐžĐ´ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ OAuth", "unable_to_connect": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ", "unable_to_connect_to_server": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", @@ -897,7 +915,7 @@ "unable_to_exit_fullscreen": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐšŅ‚Đ¸ иС ĐŋĐžĐģĐŊĐžŅĐēŅ€Đ°ĐŊĐŊĐžĐŗĐž Ņ€ĐĩĐļиĐŧа", "unable_to_get_comments_number": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đĩв", "unable_to_get_shared_link": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃", - "unable_to_hide_person": "НĐĩвОСĐŧĐžĐļĐŊĐž ҁĐēŅ€Ņ‹Ņ‚ŅŒ ĐŋĐĩŅ€ŅĐžĐŊ҃", + "unable_to_hide_person": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ҁĐēŅ€Ņ‹Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "unable_to_link_motion_video": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐ˛ŅĐˇĐ°Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", "unable_to_link_oauth_account": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐ˛ŅĐˇĐ°Ņ‚ŅŒ ŅƒŅ‡ĐĩŅ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ OAuth", "unable_to_load_album": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", @@ -907,9 +925,10 @@ "unable_to_log_out_all_devices": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛Ņ‹ĐšŅ‚Đ¸ иС Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", "unable_to_log_out_device": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛Ņ‹ĐšŅ‚Đ¸ иС ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "unable_to_login_with_oauth": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛ĐžĐšŅ‚Đ¸ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃ ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ OAuth", + "unable_to_move_to_locked_folder": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", "unable_to_play_video": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩŅŅ‚Đ¸ видĐĩĐž", - "unable_to_reassign_assets_existing_person": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ĐŊа {name, select, null {ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰ĐĩĐŗĐž ҇ĐĩĐģОвĐĩĐēа} other {{name}}}", - "unable_to_reassign_assets_new_person": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ĐŊОвОĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", + "unable_to_reassign_assets_existing_person": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа {name, select, null {Đ´Ņ€ŅƒĐŗĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа} other {҇ĐĩĐģОвĐĩĐēа ҁ иĐŧĐĩĐŊĐĩĐŧ {name}}}", + "unable_to_reassign_assets_new_person": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", "unable_to_refresh_user": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "unable_to_remove_album_users": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš иС аĐģŅŒĐąĐžĐŧа", "unable_to_remove_api_key": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐēĐģŅŽŅ‡ API", @@ -920,6 +939,7 @@ "unable_to_remove_reaction": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ€ĐĩаĐēŅ†Đ¸ŅŽ", "unable_to_repair_items": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ", "unable_to_reset_password": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", + "unable_to_reset_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐąŅ€ĐžŅĐĩ PIN-ĐēОда", "unable_to_resolve_duplicate": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚", "unable_to_restore_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "unable_to_restore_trash": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ", @@ -947,16 +967,15 @@ "unable_to_update_user": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "unable_to_upload_file": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģ" }, - "exif": "Exif", "exif_bottom_sheet_description": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ...", "exif_bottom_sheet_details": "ПОДРОБНОСĐĸИ", "exif_bottom_sheet_location": "МЕСĐĸО", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ иĐŧŅ", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {age}", + "exif_bottom_sheet_person_age_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {months} ĐŧĐĩŅŅŅ†Đĩв", + "exif_bottom_sheet_person_age_year_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´, {months} ĐŧĐĩŅŅŅ†Đĩв", + "exif_bottom_sheet_person_age_years": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {years}", "exit_slideshow": "Đ’Ņ‹ĐšŅ‚Đ¸ иС ҁĐģаКд-ŅˆĐžŅƒ", "expand_all": "РаСвĐĩŅ€ĐŊŅƒŅ‚ŅŒ Đ˛ŅŅ‘", "experimental_settings_new_asset_list_subtitle": "В Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚ĐēĐĩ", @@ -977,8 +996,9 @@ "external_network_sheet_info": "ĐšĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŊĐĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ‹Ņ‚Đ°Ņ‚ŅŒŅŅ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž Đ°Đ´Ņ€ĐĩŅĐ°Đŧ ĐŊиĐļĐĩ, ŅĐ˛ĐĩŅ€Ņ…Ņƒ вĐŊиС, Đ´Đž ҃ҁĐŋĐĩ҈ĐŊĐžĐŗĐž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", "face_unassigned": "НĐĩ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊĐž", "failed": "ĐžŅˆĐ¸ĐąĐēа", + "failed_to_authenticate": "ĐžŅˆĐ¸ĐąĐēа Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "failed_to_load_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ ĐŋаĐŋĐēи", "favorite": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "favorite_or_unfavorite_photo": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ иĐģи ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", "favorites": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", @@ -992,10 +1012,11 @@ "filetype": "ĐĸиĐŋ Ņ„Đ°ĐšĐģа", "filter": "ФиĐģŅŒŅ‚Ņ€", "filter_people": "ФиĐģŅŒŅ‚Ņ€ ĐŋĐž ĐģŅŽĐ´ŅĐŧ", + "filter_places": "ФиĐģŅŒŅ‚Ņ€ ĐŋĐž ĐŧĐĩŅŅ‚Đ°Đŧ", "find_them_fast": "Đ‘Ņ‹ŅŅ‚Ņ€Đž ĐŊĐ°ĐšĐ´Đ¸Ņ‚Đĩ Đ¸Ņ… ĐŋĐž иĐŧĐĩĐŊи ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ĐŋĐžĐ¸ŅĐēа", "fix_incorrect_match": "Đ˜ŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊĐžĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Đĩ", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "ПаĐŋĐēа", + "folder_not_found": "ПаĐŋĐēа ĐŊĐĩ ĐŊаКдĐĩĐŊа", "folders": "ПаĐŋĐēи", "folders_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ ĐŋаĐŋĐžĐē ҁ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи и видĐĩĐž в Ņ„Đ°ĐšĐģОвОК ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", "forward": "ВĐŋĐĩŅ€Ņ‘Đ´", @@ -1027,7 +1048,7 @@ "hide_gallery": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ĐŗĐ°ĐģĐĩŅ€ĐĩŅŽ", "hide_named_person": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ {name}", "hide_password": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", - "hide_person": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ĐŋĐĩŅ€ŅĐžĐŊ҃", + "hide_person": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "hide_unnamed_people": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš ĐąĐĩС иĐŧĐĩĐŊи", "home_page_add_to_album_conflicts": "ДобавĐģĐĩĐŊĐž {added} ĐŧĐĩдиа в аĐģŅŒĐąĐžĐŧ {album}. {failed} ĐŧĐĩдиа ҃ĐļĐĩ в аĐģŅŒĐąĐžĐŧĐĩ.", "home_page_add_to_album_err_local": "ПоĐēа ĐŊĐĩĐģŅŒĐˇŅ дОйавĐģŅŅ‚ŅŒ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в аĐģŅŒĐąĐžĐŧŅ‹, ĐŋŅ€ĐžĐŋ҃ҁĐē", @@ -1040,7 +1061,9 @@ "home_page_delete_remote_err_local": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_favorite_err_local": "ПоĐēа ĐŊĐĩĐģŅŒĐˇŅ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_favorite_err_partner": "ПоĐēа ĐŊĐĩĐģŅŒĐˇŅ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ ĐŧĐĩдиа ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°, ĐŋŅ€ĐžĐŋ҃ҁĐē", - "home_page_first_time_notice": "Đ•ŅĐģи Đ˛Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ вĐŋĐĩŅ€Đ˛Ņ‹Đĩ, Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ аĐģŅŒĐąĐžĐŧŅ‹ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ иĐģи ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚Đĩ Đ¸Ņ… Đ˛Ņ€ŅƒŅ‡ĐŊŅƒŅŽ, Ņ‡Ņ‚ĐžĐąŅ‹ СаĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ иĐŧи Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅƒŅŽ ҈ĐēаĐģ҃.", + "home_page_first_time_notice": "ПĐĩŅ€ĐĩĐ´ ĐŊĐ°Ņ‡Đ°ĐģĐžĐŧ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ аĐģŅŒĐąĐžĐŧ ҁ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧи Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐžĐŊи ĐžŅ‚ĐžĐąŅ€Đ°ĐˇĐ¸ĐģĐ¸ŅŅŒ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊОК ҈ĐēаĐģĐĩ", + "home_page_locked_error_local": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ĐŋŅ€ĐžĐŋ҃ҁĐē", + "home_page_locked_error_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_share_err_local": "НĐĩĐģŅŒĐˇŅ ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ ĐģĐžĐēаĐģҌĐŊŅ‹Đŧи Ņ„Đ°ĐšĐģаĐŧи ĐŋĐž ҁҁҋĐģĐēĐĩ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_upload_err_limit": "Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐŧаĐēŅĐ¸Đŧ҃Đŧ 30 Ņ„Đ°ĐšĐģОв Са Ņ€Đ°Đˇ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "host": "ĐĨĐžŅŅ‚", @@ -1066,7 +1089,7 @@ "immich_web_interface": "ВĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ Immich", "import_from_json": "ИĐŧĐŋĐžŅ€Ņ‚ иС JSON", "import_path": "ĐŸŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", - "in_albums": "В {count, plural, one {# аĐģŅŒĐąĐžĐŧĐĩ} few {# аĐģŅŒĐąĐžĐŧĐ°Ņ…} many {# аĐģŅŒĐąĐžĐŧĐ°Ņ…} other {# аĐģŅŒĐąĐžĐŧĐ°Ņ…}}", + "in_albums": "В {count, plural, one {# аĐģŅŒĐąĐžĐŧĐĩ} other {# аĐģŅŒĐąĐžĐŧĐ°Ņ…}}", "in_archive": "В Đ°Ņ€Ņ…Đ¸Đ˛Đĩ", "include_archived": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đ°Ņ€Ņ…Đ¸Đ˛", "include_shared_albums": "ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đĩ аĐģŅŒĐąĐžĐŧŅ‹", @@ -1084,12 +1107,12 @@ "invalid_date_format": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹", "invite_people": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ", "invite_to_album": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧ", - "items_count": "{count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚} two {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°} few {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛}}", + "items_count": "{count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "jobs": "Đ—Đ°Đ´Đ°Ņ‡Đ¸", "keep": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ", "keep_all": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", "keep_this_delete_others": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚, ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ", - "kept_this_deleted_others": "ĐĄĐžŅ…Ņ€Đ°ĐŊиĐģ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ и ŅƒĐ´Đ°ĐģиĐģ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", + "kept_this_deleted_others": "ĐĄĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ и {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", "keyboard_shortcuts": "ĐĄĐžŅ‡ĐĩŅ‚Đ°ĐŊĐ¸Ņ ĐēĐģĐ°Đ˛Đ¸Ņˆ", "language": "Đ¯ĐˇŅ‹Đē", "language_setting_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐĩĐŧŅ‹Đš ваĐŧи ŅĐˇŅ‹Đē", @@ -1120,12 +1143,14 @@ "local_network": "ЛоĐēаĐģҌĐŊĐ°Ņ ҁĐĩŅ‚ŅŒ", "local_network_sheet_info": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž ŅŅ‚ĐžĐŧ҃ Đ°Đ´Ņ€Đĩҁ҃, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸", "location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ", - "location_permission_content": "Đ§Ņ‚ĐžĐąŅ‹ ŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸Đŧ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ‚ĐžŅ‡ĐŊĐžĐŧ҃ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "location_permission_content": "Đ§Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŽ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ, Immich ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Ņ‚ĐžŅ‡ĐŊĐžĐĩ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸Ņ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐžĐŊĐž ĐŧĐžĐŗĐģĐž ŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŊаСваĐŊиĐĩ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐš Wi-Fi ҁĐĩŅ‚Đ¸", "location_picker_choose_on_map": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŊа ĐēĐ°Ņ€Ņ‚Đĩ", "location_picker_latitude_error": "ĐŖĐēаĐļĐ¸Ņ‚Đĩ ĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊŅƒŅŽ ŅˆĐ¸Ņ€ĐžŅ‚Ņƒ", "location_picker_latitude_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ŅˆĐ¸Ņ€ĐžŅ‚Ņƒ", "location_picker_longitude_error": "ĐŖĐēаĐļĐ¸Ņ‚Đĩ ĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊŅƒŅŽ Đ´ĐžĐģĐŗĐžŅ‚Ņƒ", "location_picker_longitude_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ Đ´ĐžĐģĐŗĐžŅ‚Ņƒ", + "lock": "ЗабĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "locked_folder": "Đ›Đ¸Ņ‡ĐŊĐ°Ņ ĐŋаĐŋĐēа", "log_out": "Đ’Ņ‹ĐšŅ‚Đ¸", "log_out_all_devices": "Đ’Ņ‹ĐšĐ´Đ¸Ņ‚Đĩ иС ŅĐ¸ŅŅ‚ĐĩĐŧŅ‹ ŅĐž Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", "logged_out_all_devices": "Đ’Ņ‹ĐšŅ‚Đ¸ ĐŊа Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…", @@ -1134,8 +1159,6 @@ "login_disabled": "Đ’Ņ…ĐžĐ´ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ", "login_form_api_exception": "ĐžŅˆĐ¸ĐąĐēа ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ. ĐŸŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ URL-Đ°Đ´Ņ€Đĩҁ и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đĩ҉Đĩ Ņ€Đ°Đˇ.", "login_form_back_button_text": "Назад", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "URL-aĐ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "login_form_err_http": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ҃ĐēаĐļĐ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ‚ĐžĐēĐžĐģ http:// иĐģи https://", "login_form_err_invalid_email": "НĐĩĐēĐžŅ€Ņ€ĐĩĐēŅ‚ĐŊŅ‹Đš Đ°Đ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹", @@ -1145,7 +1168,7 @@ "login_form_failed_get_oauth_server_config": "ĐžŅˆĐ¸ĐąĐēа Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸ ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ OAuth, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "login_form_failed_get_oauth_server_disable": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ ҇ĐĩŅ€ĐĩС OAuth ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊа ŅŅ‚ĐžĐŧ ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", "login_form_failed_login": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ Đ˛Ņ…ĐžĐ´Đĩ, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, Đ°Đ´Ņ€Đĩҁ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Ņ‹ и ĐŋĐ°Ņ€ĐžĐģҌ", - "login_form_handshake_exception": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ°. Đ•ŅĐģи Đ˛Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊŅ‹Đš ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚, вĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐŋОддĐĩŅ€ĐļĐē҃ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊҋ҅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžĐ˛ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", + "login_form_handshake_exception": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ°. Đ•ŅĐģи Đ˛Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊŅ‹Đš ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚, вĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐŋОддĐĩŅ€ĐļĐē҃ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊҋ҅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžĐ˛ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", "login_form_password_hint": "ĐŋĐ°Ņ€ĐžĐģҌ", "login_form_save_login": "ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ в ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", "login_form_server_empty": "ВвĐĩĐ´Đ¸Ņ‚Đĩ URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", @@ -1170,8 +1193,8 @@ "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Đŧи, ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… ĐžŅŅƒŅ‰ĐĩŅŅ‚Đ˛ĐģŅĐģŅŅ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", "manage_your_oauth_connection": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŋОдĐēĐģŅŽŅ‡Ņ‘ĐŊĐŊĐžĐŗĐž OAuth", "map": "ĐšĐ°Ņ€Ņ‚Đ°", - "map_assets_in_bound": "{} Ņ„ĐžŅ‚Đž", - "map_assets_in_bounds": "{} Ņ„ĐžŅ‚Đž", + "map_assets_in_bound": "{count} Ņ„ĐžŅ‚Đž", + "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚Đž", "map_cannot_get_user_location": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "Đ­Ņ‚Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -1185,15 +1208,18 @@ "map_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐ°Ņ€Ņ‚Ņ‹", "map_settings_dark_mode": "ĐĸĐĩĐŧĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", "map_settings_date_range_option_day": "24 Ņ‡Đ°ŅĐ°", - "map_settings_date_range_option_days": "{} Đ´ĐŊĐĩĐš", + "map_settings_date_range_option_days": "ĐŸĐžŅĐģĐĩĐ´ĐŊиĐĩ {days} Đ´ĐŊĐĩĐš", "map_settings_date_range_option_year": "Год", - "map_settings_date_range_option_years": "{} ĐŗĐžĐ´Đ°", + "map_settings_date_range_option_years": "{years} ĐŗĐžĐ´Đ°", "map_settings_dialog_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐ°Ņ€Ņ‚Ņ‹", "map_settings_include_show_archived": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐĩ", "map_settings_include_show_partners": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ ĐŧĐĩдиа ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "map_settings_only_show_favorites": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "map_settings_theme_settings": "ĐĻвĐĩŅ‚ ĐēĐ°Ņ€Ņ‚Ņ‹", "map_zoom_to_see_photos": "ĐŖĐŧĐĩĐŊҌ҈ĐĩĐŊиĐĩ ĐŧĐ°ŅŅˆŅ‚Đ°ĐąĐ° Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš", + "mark_all_as_read": "ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐēаĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ‹Đĩ", + "mark_as_read": "ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ ĐēаĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐŊĐžĐĩ", + "marked_all_as_read": "ĐžŅ‚ĐŧĐĩ҇ĐĩĐŊŅ‹ ĐēаĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ‹Đĩ", "matches": "ХОвĐŋадĐĩĐŊĐ¸Ņ", "media_type": "ĐĸиĐŋ ĐŧĐĩдиа", "memories": "Đ’ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", @@ -1202,24 +1228,27 @@ "memories_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ Ņ‚ĐĩĐŧ, Ņ‡Ņ‚Đž Đ˛Ņ‹ Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ в ŅĐ˛ĐžĐ¸Ņ… Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸ŅŅ…", "memories_start_over": "ĐĐ°Ņ‡Đ°Ņ‚ŅŒ СаĐŊОвО", "memories_swipe_to_close": "ĐĄĐŧĐ°Ņ…ĐŊĐ¸Ņ‚Đĩ ввĐĩҀ҅, Ņ‡Ņ‚ĐžĐąŅ‹ СаĐēŅ€Ņ‹Ņ‚ŅŒ", - "memories_year_ago": "Год ĐŊаСад", - "memories_years_ago": "ЛĐĩŅ‚ ĐŊаСад: {}", "memory": "ПаĐŧŅŅ‚ŅŒ", "memory_lane_title": "Đ’ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ {title}", "menu": "МĐĩĐŊŅŽ", "merge": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ", - "merge_people": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ĐŋĐĩŅ€ŅĐžĐŊŅ‹", + "merge_people": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "merge_people_limit": "Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐžĐąŅŠĐĩдиĐŊŅŅ‚ŅŒ Đ´Đž 5 ĐģĐ¸Ņ† Са ОдиĐŊ Ņ€Đ°Đˇ", "merge_people_prompt": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ¸Ņ… ĐģŅŽĐ´ĐĩĐš? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐžĐąŅ€Đ°Ņ‚Đ¸ĐŧĐž.", - "merge_people_successfully": "ПĐĩŅ€ŅĐžĐŊŅ‹ ĐžĐąŅŠĐĩдиĐŊĐĩĐŊŅ‹ ҃ҁĐŋĐĩ҈ĐŊĐž", - "merged_people_count": "ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž {count, plural, one {# ҇ĐĩĐģОвĐĩĐē} few {# ҇ĐĩĐģОвĐĩĐēа} many {# ҇ĐĩĐģОвĐĩĐē} other {# ҇ĐĩĐģОвĐĩĐēа}}", + "merge_people_successfully": "Đ›Đ¸Ņ†Đ° ĐģŅŽĐ´ĐĩĐš ҃ҁĐŋĐĩ҈ĐŊĐž ĐžĐąŅŠĐĩдиĐŊĐĩĐŊŅ‹", + "merged_people_count": "ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž {count, plural, one {# ҇ĐĩĐģОвĐĩĐē} many {# ҇ĐĩĐģОвĐĩĐē} other {# ҇ĐĩĐģОвĐĩĐēа}}", "minimize": "МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "minute": "МиĐŊŅƒŅ‚Đ°", "missing": "ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ", "model": "МодĐĩĐģҌ", "month": "МĐĩŅŅŅ†", - "monthly_title_text_date_format": "MMMM y", "more": "БоĐģҌ҈Đĩ", + "move": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ", + "move_off_locked_folder": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", + "move_to_locked_folder": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", + "move_to_locked_folder_confirmation": "Đ­Ņ‚Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв и ĐąŅƒĐ´ŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ Ņ‚ĐžĐģҌĐēĐž в ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ", + "moved_to_archive": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в Đ°Ņ€Ņ…Đ¸Đ˛", + "moved_to_library": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "moved_to_trash": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "multiselect_grid_edit_date_time_err_read_only": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ„Đ°ĐšĐģОв Ņ‚ĐžĐģҌĐēĐž Đ´ĐģŅ ҇҂ĐĩĐŊĐ¸Ņ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "multiselect_grid_edit_gps_err_read_only": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ Ņ„Đ°ĐšĐģОв Ņ‚ĐžĐģҌĐēĐž Đ´ĐģŅ ҇҂ĐĩĐŊĐ¸Ņ, ĐŋŅ€ĐžĐŋ҃ҁĐē", @@ -1233,7 +1262,9 @@ "new_album": "ĐĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ", "new_api_key": "ĐĐžĐ˛Ņ‹Đš API-ĐēĐģŅŽŅ‡", "new_password": "ĐĐžĐ˛Ņ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ", - "new_person": "ĐĐžĐ˛Đ°Ņ ĐŋĐĩŅ€ŅĐžĐŊа", + "new_person": "ĐĐžĐ˛Ņ‹Đš ҇ĐĩĐģОвĐĩĐē", + "new_pin_code": "ĐĐžĐ˛Ņ‹Đš PIN-ĐēОд", + "new_pin_code_subtitle": "Đ­Ņ‚Đž Đ˛Đ°Ņˆ ĐŋĐĩŅ€Đ˛Ņ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ. ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ PIN-ĐēОд Đ´ĐģŅ ĐˇĐ°Ņ‰Đ¸Ņ‰ĐĩĐŊĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа Đē ŅŅ‚ĐžĐš ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đĩ.", "new_user_created": "ĐĐžĐ˛Ņ‹Đš ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ŅĐžĐˇĐ´Đ°ĐŊ", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ ĐĐžĐ’ĐĐ¯ Đ’Đ•Đ ĐĄĐ˜Đ¯", "newest_first": "ĐĄĐŊĐ°Ņ‡Đ°Đģа ĐŊĐžĐ˛Ņ‹Đĩ", @@ -1251,7 +1282,10 @@ "no_explore_results_message": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°ĐšŅ‚Đĩ йОĐģҌ҈Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ŅĐģаĐļĐ´Đ°Ņ‚ŅŒŅŅ Đ˛Đ°ŅˆĐĩĐš ĐēĐžĐģĐģĐĩĐēŅ†Đ¸ĐĩĐš.", "no_favorites_message": "ДобавĐģŅĐšŅ‚Đĩ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐąŅ‹ŅŅ‚Ņ€Đž ĐŊĐ°ĐšŅ‚Đ¸ ŅĐ˛ĐžĐ¸ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž", "no_libraries_message": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ вĐŊĐĩ҈ĐŊŅŽŅŽ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž", + "no_locked_photos_message": "Đ¤ĐžŅ‚Đž и видĐĩĐž, ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐŊŅ‹Đĩ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ҁĐēҀҋ҂ҋ и ĐŊĐĩ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°ŅŽŅ‚ŅŅ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "no_name": "НĐĩŅ‚ иĐŧĐĩĐŊи", + "no_notifications": "НĐĩŅ‚ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", + "no_people_found": "НиĐēĐžĐŗĐž ĐŊĐĩ ĐŊаКдĐĩĐŊĐž", "no_places": "НĐĩŅ‚ ĐŧĐĩҁ҂", "no_results": "НĐĩŅ‚ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛", "no_results_description": "ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ŅĐ¸ĐŊĐžĐŊиĐŧ иĐģи йОĐģĐĩĐĩ ĐžĐąŅ‰ĐĩĐĩ ĐēĐģŅŽŅ‡ĐĩвОĐĩ ҁĐģОвО", @@ -1260,14 +1294,14 @@ "not_selected": "НĐĩ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", "note_apply_storage_label_to_previously_uploaded assets": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ Ņ€ĐĩŅŅƒŅ€ŅĐ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ", "notes": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ", + "nothing_here_yet": "ЗдĐĩҁҌ ĐŋĐžĐēа ĐŊĐ¸Ņ‡ĐĩĐŗĐž ĐŊĐĩŅ‚", "notification_permission_dialog_content": "Đ§Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ, ĐŋĐĩŅ€ĐĩĐšĐ´Đ¸Ņ‚Đĩ в ÂĢĐĐ°ŅŅ‚Ņ€ĐžĐšĐēиÂģ и Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ÂĢĐ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒÂģ.", - "notification_permission_list_tile_content": "ĐŸŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа вĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", + "notification_permission_list_tile_content": "ĐŸŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа ĐŋĐžĐēаС ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК.", "notification_permission_list_tile_enable_button": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ", "notification_permission_list_tile_title": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ", "notification_toggle_setting_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ", "notifications": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ", "notifications_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸ŅĐŧи", - "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģҌĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ Immich", "offline": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ", "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐŋŅƒŅ‚Đ¸", @@ -1282,6 +1316,7 @@ "onboarding_welcome_user": "Đ”ĐžĐąŅ€Đž ĐŋĐžĐļаĐģĐžĐ˛Đ°Ņ‚ŅŒ, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋĐĩĐŊ", "only_favorites": "ĐĸĐžĐģҌĐēĐž Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", + "open": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ", "open_in_map_view": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ в Ņ€ĐĩĐļиĐŧĐĩ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° ĐēĐ°Ņ€Ņ‚Ņ‹", "open_in_openstreetmap": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ в OpenStreetMap", "open_the_search_filters": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Ņ‹ ĐŋĐžĐ¸ŅĐēа", @@ -1298,14 +1333,14 @@ "partner_can_access": "{partner} иĐŧĐĩĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋ", "partner_can_access_assets": "Đ’ŅĐĩ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩОСаĐŋĐ¸ŅĐ¸, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩŅ…, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐ°Ņ…ĐžĐ´ŅŅ‚ŅŅ в ĐŅ€Ņ…Đ¸Đ˛Đĩ и ĐšĐžŅ€ĐˇĐ¸ĐŊĐĩ", "partner_can_access_location": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ, ĐŗĐ´Đĩ ĐąŅ‹Đģи ŅĐ´ĐĩĐģаĐŊŅ‹ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", - "partner_list_user_photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ {user}", + "partner_list_user_photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}", "partner_list_view_all": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛ŅĐĩ", - "partner_page_empty_message": "ĐŖ Đ˛Đ°ŅˆĐĩĐŗĐž ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° Đĩ҉Đĩ ĐŊĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž", + "partner_page_empty_message": "ĐŖ Đ˛Đ°ŅˆĐĩĐŗĐž ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° Đĩ҉Đĩ ĐŊĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž.", "partner_page_no_more_users": "Đ’Ņ‹ĐąŅ€Đ°ĐŊŅ‹ Đ˛ŅĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи", "partner_page_partner_add_failed": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "partner_page_select_partner": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "partner_page_shared_to_title": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ...", - "partner_page_stop_sharing_content": "{} йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧ", + "partner_page_stop_sharing_content": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {partner} йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž.", "partner_sharing": "ХОвĐŧĐĩҁ҂ĐŊĐžĐĩ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", "partners": "ĐŸĐ°Ņ€Ņ‚ĐŊґҀҋ", "password": "ĐŸĐ°Ņ€ĐžĐģҌ", @@ -1324,16 +1359,16 @@ "paused": "ĐŸŅ€Đ¸ĐžŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž", "pending": "ОĐļидаĐĩŅ‚", "people": "Đ›ŅŽĐ´Đ¸", - "people_edits_count": "ИСĐŧĐĩĐŊĐĩĐŊĐž {count, plural, one {# ҇ĐĩĐģОвĐĩĐē} few {# ҇ĐĩĐģОвĐĩĐēа} many {# ĐģŅŽĐ´ĐĩĐš} other {# ҇ĐĩĐģОвĐĩĐē}}", + "people_edits_count": "{count, plural, one {ИСĐŧĐĩĐŊŅ‘ĐŊ # ҇ĐĩĐģОвĐĩĐē} many {ИСĐŧĐĩĐŊĐĩĐŊĐž # ҇ĐĩĐģОвĐĩĐē} other {ИСĐŧĐĩĐŊĐĩĐŊĐž # ҇ĐĩĐģОвĐĩĐēа}}", "people_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž, ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊҋ҅ ĐŋĐž ĐģŅŽĐ´ŅĐŧ", "people_sidebar_description": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đŋ҃ĐŊĐēŅ‚ ĐŧĐĩĐŊŅŽ \"Đ›ŅŽĐ´Đ¸\" в йОĐēОвОК ĐŋаĐŊĐĩĐģи", "permanent_deletion_warning": "ĐŸŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ Ой ŅƒĐ´Đ°ĐģĐĩĐŊии", "permanent_deletion_warning_setting_description": "ĐŸŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´Đ°Ņ‚ŅŒ ĐŋĐĩŅ€ĐĩĐ´ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊŅ‹Đŧ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩĐŧ Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛", "permanently_delete": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "permanently_delete_assets_count": "БĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {Ņ€ĐĩŅŅƒŅ€Ņ} few {Ņ€ĐĩŅŅƒŅ€ŅĐ°} many {Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} other {Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛}}", - "permanently_delete_assets_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚?} other {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛?}} Đ­Ņ‚Đž Ņ‚Đ°Đē ĐļĐĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {ĐĩĐŗĐž} other {Đ¸Ņ…}} иС аĐģŅŒĐąĐžĐŧа(Ов).", + "permanently_delete_assets_count": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {ĐžĐąŅŠĐĩĐēŅ‚} other {ĐžĐąŅŠĐĩĐē҂ҋ}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", + "permanently_delete_assets_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚} many {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ­Ņ‚Đž Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {ĐĩĐŗĐž} other {Đ¸Ņ…}} иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", "permanently_deleted_asset": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "permanently_deleted_assets_count": "БĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐž {count, plural, one {# Ņ„Đ°ĐšĐģ} few {# Ņ„Đ°ĐšĐģа} many {# Ņ„Đ°ĐšĐģОв} other {# Ņ„Đ°ĐšĐģОв}}", + "permanently_deleted_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "permission_onboarding_back": "Назад", "permission_onboarding_continue_anyway": "Đ’ŅĐĩ Ņ€Đ°Đ˛ĐŊĐž ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ", "permission_onboarding_get_started": "Đ”Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŊĐ°Ņ‡ĐŊŅ‘Đŧ", @@ -1341,23 +1376,28 @@ "permission_onboarding_permission_denied": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ, Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đē \"Đ¤ĐžŅ‚Đž и видĐĩĐž\" в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", "permission_onboarding_permission_granted": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊ! Đ’ŅŅ‘ ĐŗĐžŅ‚ĐžĐ˛Đž.", "permission_onboarding_permission_limited": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē Ņ„Đ°ĐšĐģаĐŧ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ. Đ§Ņ‚ĐžĐąŅ‹ Immich ĐŧĐžĐŗ ŅĐžĐˇĐ´Đ°Đ˛Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ‹Đĩ ĐēĐžĐŋии и ҃ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ Đ˛Đ°ŅˆĐĩĐš ĐŗĐ°ĐģĐĩŅ€ĐĩĐĩĐš, ĐŋĐžĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅ‚ŅƒĐŋ Đē \"Đ¤ĐžŅ‚Đž и видĐĩĐž\" в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", - "permission_onboarding_request": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž и видĐĩĐž", + "permission_onboarding_request": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž и видĐĩĐž.", "person": "ЧĐĩĐģОвĐĩĐē", "person_birthdate": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ: {date}", "person_hidden": "{name}{hidden, select, true { (ҁĐēҀҋ҂)} other {}}", "photo_shared_all_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", "photos": "Đ¤ĐžŅ‚Đž", "photos_and_videos": "Đ¤ĐžŅ‚Đž и ВидĐĩĐž", - "photos_count": "{count, plural, one {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ} few {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸} many {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš} other {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš}}", + "photos_count": "{count, plural, one {{count, number} Ņ„ĐžŅ‚Đž} other {{count, number} Ņ„ĐžŅ‚Đž}}", "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ ĐŋŅ€ĐžŅˆĐģҋ҅ ĐģĐĩŅ‚ в ŅŅ‚ĐžŅ‚ Đ´ĐĩĐŊҌ", "pick_a_location": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "pin_code_changed_successfully": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž иСĐŧĐĩĐŊŅ‘ĐŊ", + "pin_code_reset_successfully": "PIN-ĐēОд ŅĐąŅ€ĐžŅˆĐĩĐŊ", + "pin_code_setup_successfully": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ", + "pin_verification": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа PIN-ĐēОда", "place": "МĐĩŅŅ‚Đ°", "places": "МĐĩŅŅ‚Đ°", - "places_count": "{count, plural, one {{count, number} МĐĩŅŅ‚Đž} other {{count, number} МĐĩҁ҂}}", + "places_count": "{count, plural, one {{count, number} ĐŧĐĩŅŅ‚Đž} many {{count, number} ĐŧĐĩҁ҂} other {{count, number} ĐŧĐĩŅŅ‚Đ°}}", "play": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩŅŅ‚Đ¸", "play_memories": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩŅŅ‚Đ¸ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", "play_motion_photo": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ŅŒ двиĐļŅƒŅ‰Đ¸ĐĩŅŅ Ņ„ĐžŅ‚Đž", "play_or_pause_video": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ иĐģи ĐŋŅ€Đ¸ĐžŅŅ‚Đ°ĐŊОвĐēа видĐĩĐž", + "please_auth_to_access": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇŅƒĐšŅ‚ĐĩҁҌ", "port": "ĐŸĐžŅ€Ņ‚", "preferences_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа вĐŊĐĩ҈ĐŊĐĩĐŗĐž вида", "preferences_settings_title": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ", @@ -1368,11 +1408,11 @@ "previous_or_next_photo": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰Đ°Ņ иĐģи ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ°Ņ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ", "primary": "ГĐģавĐŊĐžĐĩ", "privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ", + "profile": "ĐŸŅ€ĐžŅ„Đ¸ĐģҌ", "profile_drawer_app_logs": "Đ–ŅƒŅ€ĐŊаĐģ", "profile_drawer_client_out_of_date_major": "ВĐĩŅ€ŅĐ¸Ņ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_client_out_of_date_minor": "ВĐĩŅ€ŅĐ¸Ņ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_client_server_up_to_date": "КĐģиĐĩĐŊŅ‚ и ҁĐĩŅ€Đ˛ĐĩŅ€ ОйĐŊОвĐģĐĩĐŊŅ‹", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_server_out_of_date_minor": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_image_of_user": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ {user}", @@ -1381,7 +1421,7 @@ "public_share": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ", "purchase_account_info": "ПоддĐĩŅ€ĐļĐēа", "purchase_activated_subtitle": "БĐģĐ°ĐŗĐžĐ´Đ°Ņ€Đ¸Đŧ Đ˛Đ°Ņ Са ĐŋОддĐĩŅ€ĐļĐē҃ Immich и ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŧĐŊĐžĐŗĐž ОйĐĩҁĐŋĐĩ҇ĐĩĐŊĐ¸Ņ ҁ ĐžŅ‚ĐēҀҋ҂ҋĐŧ Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đŧ ĐēОдОĐŧ", - "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐž ĐŊа {date, date}", + "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐž {date}", "purchase_activated_title": "Đ’Đ°Ņˆ ĐēĐģŅŽŅ‡ ҃ҁĐŋĐĩ҈ĐŊĐž аĐēŅ‚Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊ", "purchase_button_activate": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "purchase_button_buy": "ĐšŅƒĐŋĐ¸Ņ‚ŅŒ", @@ -1410,22 +1450,24 @@ "purchase_server_description_1": "ДĐģŅ Đ˛ŅĐĩĐŗĐž ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "purchase_server_description_2": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ ĐŋОддĐĩŅ€ĐļĐēи", "purchase_server_title": "ĐĄĐĩŅ€Đ˛ĐĩŅ€", - "purchase_settings_server_activated": "КĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҃ĐŋŅ€Đ°Đ˛ĐģŅĐĩŅ‚ŅŅ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ", + "purchase_settings_server_activated": "КĐģŅŽŅ‡ĐžĐŧ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ° ҃ĐŋŅ€Đ°Đ˛ĐģŅĐĩŅ‚ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "rating": "Đ ĐĩĐšŅ‚Đ¸ĐŊĐŗ ĐˇĐ˛Ņ‘ĐˇĐ´", "rating_clear": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ", - "rating_count": "{count, plural, one {# СвĐĩСда} other {# СвĐĩСд}}", + "rating_count": "{count, plural, one {# СвĐĩСда} many {# СвĐĩСд} other {# СвĐĩĐˇĐ´Ņ‹}}", "rating_description": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ в ĐŋаĐŊĐĩĐģи иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", "reaction_options": "ОĐŋŅ†Đ¸Đ¸ Ņ€ĐĩаĐēŅ†Đ¸Đš", "read_changelog": "ĐŸŅ€ĐžŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ ҁĐŋĐ¸ŅĐžĐē иСĐŧĐĩĐŊĐĩĐŊиК", "reassign": "ПĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ", - "reassigned_assets_to_existing_person": "ПĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ{count, plural, one { # Ņ€ĐĩŅŅƒŅ€Ņ} few {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐ°} many {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} other {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛}} ĐŊа {name, select, null {ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰ĐĩĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ} other {{name}}}", - "reassigned_assets_to_new_person": "ПĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ{count, plural, one { # Ņ€ĐĩŅŅƒŅ€Ņ} few {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐ°} many {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} other {Đž # Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛}} ĐŊОвОĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", - "reassing_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰ĐĩĐŧ҃ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ", + "reassigned_assets_to_existing_person": "Đ›Đ¸Ņ†Đ° ĐŊа {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đĩ} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°Ņ…}} ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊŅ‹ ĐŊа {name, select, null {Đ´Ņ€ŅƒĐŗĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа} other {҇ĐĩĐģОвĐĩĐēа ҁ иĐŧĐĩĐŊĐĩĐŧ {name}}}", + "reassigned_assets_to_new_person": "Đ›Đ¸Ņ†Đ° ĐŊа {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đĩ} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°Ņ…}} ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊŅ‹ ĐŊа ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", + "reassing_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ҃ĐēаСаĐŊĐŊĐžĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", "recent": "НĐĩдавĐŊиĐĩ", "recent-albums": "НĐĩдавĐŊиĐĩ аĐģŅŒĐąĐžĐŧŅ‹", "recent_searches": "НĐĩдавĐŊиĐĩ ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đĩ СаĐŋŅ€ĐžŅŅ‹", "recently_added": "НĐĩдавĐŊĐž дОйавĐģĐĩĐŊĐŊŅ‹Đĩ", "recently_added_page_title": "НĐĩдавĐŊĐž дОйавĐģĐĩĐŊĐŊŅ‹Đĩ", + "recently_taken": "НĐĩдавĐŊĐž ҁĐŊŅŅ‚Đž", + "recently_taken_page_title": "НĐĩдавĐŊĐž ĐĄĐŊŅŅ‚Đž", "refresh": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "refresh_encoded_videos": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ СаĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ видĐĩĐž", "refresh_faces": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đ°", @@ -1438,13 +1480,15 @@ "refreshing_metadata": "ОбĐŊОвĐģĐĩĐŊиĐĩ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", "regenerating_thumbnails": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€", "remove": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", - "remove_assets_album_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} иС аĐģŅŒĐąĐžĐŧа?", - "remove_assets_shared_link_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} иС ŅŅ‚ĐžĐš ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи?", + "remove_assets_album_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС аĐģŅŒĐąĐžĐŧа?", + "remove_assets_shared_link_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа ĐŋĐž ŅŅ‚ĐžĐš ҁҁҋĐģĐēĐĩ?", "remove_assets_title": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ?", "remove_custom_date_range": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК диаĐŋаСОĐŊ Đ´Đ°Ņ‚", "remove_deleted_assets": "ĐŖĐ´Đ°ĐģĐĩĐŊиĐĩ Đ°Đ˛Ņ‚ĐžĐŊĐžĐŧĐŊҋ҅ Ņ„Đ°ĐšĐģОв", "remove_from_album": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС аĐģŅŒĐąĐžĐŧа", "remove_from_favorites": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", + "remove_from_locked_folder": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", + "remove_from_locked_folder_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ŅŅ‚Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи? ОĐŊи ŅŅ‚Đ°ĐŊŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ в Đ˛Đ°ŅˆĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", "remove_from_shared_link": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", "remove_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ", "remove_photo_from_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž иС Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", @@ -1453,10 +1497,10 @@ "removed_api_key": "ĐŖĐ´Đ°ĐģĐĩĐŊ ĐēĐģŅŽŅ‡ API: {name}", "removed_from_archive": "ĐŖĐ´Đ°ĐģĐĩĐŊ иС Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "removed_from_favorites": "ĐŖĐ´Đ°ĐģĐĩĐŊĐž иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", - "removed_from_favorites_count": "{count, plural, other {ĐŖĐ´Đ°ĐģĐĩĐŊĐž #}} иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", + "removed_from_favorites_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", "removed_memory": "Đ’ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊĐž", "removed_photo_from_memory": "Đ¤ĐžŅ‚Đž ŅƒĐ´Đ°ĐģĐĩĐŊĐž иС Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", - "removed_tagged_assets": "ĐĸĐĩĐŗ Đ´ĐģŅ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}} ŅƒĐ´Đ°ĐģĐĩĐŊ", + "removed_tagged_assets": "ĐĸĐĩĐŗ ŅƒĐ´Đ°ĐģŅ‘ĐŊ ҃ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", "rename": "ПĐĩŅ€ĐĩиĐŧĐĩĐŊĐžĐ˛Đ°Ņ‚ŅŒ", "repair": "Đ ĐĩĐŧĐžĐŊŅ‚", "repair_no_results_message": "ЗдĐĩҁҌ ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒŅŅ ĐŊĐĩĐžŅ‚ŅĐģĐĩĐļиваĐĩĐŧŅ‹Đĩ и ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ Ņ„Đ°ĐšĐģŅ‹", @@ -1468,6 +1512,7 @@ "reset": "ĐĄĐąŅ€ĐžŅ", "reset_password": "ĐĄĐąŅ€ĐžŅ ĐŋĐ°Ņ€ĐžĐģŅ", "reset_people_visibility": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ видиĐŧĐžŅŅ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", + "reset_pin_code": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд", "reset_to_default": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиĐĩ СĐŊĐ°Ņ‡ĐĩĐŊиК ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "resolve_duplicates": "ĐŖŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "resolved_all_duplicates": "Đ’ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ŅƒŅŅ‚Ņ€Đ°ĐŊĐĩĐŊŅ‹", @@ -1510,7 +1555,7 @@ "search_filter_date_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐŧĐĩĐļŅƒŅ‚ĐžĐē", "search_filter_display_option_not_in_album": "НĐĩ в аĐģŅŒĐąĐžĐŧĐĩ", "search_filter_display_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", - "search_filter_filename": "Search by file name", + "search_filter_filename": "ĐŸĐžĐ¸ŅĐē ĐŋĐž иĐŧĐĩĐŊи Ņ„Đ°ĐšĐģа", "search_filter_location": "МĐĩŅŅ‚Đž", "search_filter_location_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŧĐĩŅŅ‚Đž", "search_filter_media_type": "ĐĸиĐŋ Ņ„Đ°ĐšĐģа", @@ -1518,10 +1563,10 @@ "search_filter_people_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐģŅŽĐ´ĐĩĐš", "search_for": "ĐŸĐžĐ¸ŅĐē ĐŋĐž", "search_for_existing_person": "ĐŸĐžĐ¸ŅĐē ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰ĐĩĐŗĐž ҇ĐĩĐģОвĐĩĐēа", - "search_no_more_result": "No more results", + "search_no_more_result": "БоĐģҌ҈Đĩ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŊĐĩŅ‚", "search_no_people": "НĐĩŅ‚ ĐģŅŽĐ´ĐĩĐš", "search_no_people_named": "НĐĩŅ‚ ĐģŅŽĐ´ĐĩĐš ҁ иĐŧĐĩĐŊĐĩĐŧ \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "ĐĐ¸Ņ‡ĐĩĐŗĐž ĐŊĐĩ ĐŊаКдĐĩĐŊĐž, ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đš СаĐŋŅ€ĐžŅ", "search_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐŋĐžĐ¸ŅĐēа", "search_page_categories": "ĐšĐ°Ņ‚ĐĩĐŗĐžŅ€Đ¸Đ¸", "search_page_motion_photos": "ДиĐŊаĐŧĐ¸Ņ‡ĐĩҁĐēиĐĩ Ņ„ĐžŅ‚Đž", @@ -1538,9 +1583,9 @@ "search_places": "ĐŸĐžĐ¸ŅĐē ĐŧĐĩҁ҂", "search_rating": "ĐŸĐžĐ¸ŅĐē ĐŋĐž Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗŅƒ...", "search_result_page_new_search_hint": "ĐĐžĐ˛Ņ‹Đš ĐŋĐžĐ¸ŅĐē", - "search_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŋĐžĐ¸ŅĐēа", + "search_settings": "ĐŸĐžĐ¸ŅĐē ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē", "search_state": "ĐŸĐžĐ¸ŅĐē Ņ€ĐĩĐŗĐ¸ĐžĐŊа...", - "search_suggestion_list_smart_search_hint_1": "ИĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐē вĐēĐģŅŽŅ‡ĐĩĐŊ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ, Đ´ĐģŅ ĐŋĐžĐ¸ŅĐēа ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ҁĐŋĐĩŅ†Đ¸Đ°ĐģҌĐŊŅ‹Đš ŅĐ¸ĐŊŅ‚Đ°ĐēŅĐ¸Ņ", + "search_suggestion_list_smart_search_hint_1": "ИĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐē вĐēĐģŅŽŅ‡ĐĩĐŊ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ, Đ´ĐģŅ ĐŋĐžĐ¸ŅĐēа ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ҁĐŋĐĩŅ†Đ¸Đ°ĐģҌĐŊŅ‹Đš ŅĐ¸ĐŊŅ‚Đ°ĐēŅĐ¸Ņ ", "search_suggestion_list_smart_search_hint_2": "m:Đ˛Đ°Ņˆ-ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đš-СаĐŋŅ€ĐžŅ", "search_tags": "ĐŸĐžĐ¸ŅĐē ĐŋĐž Ņ‚ĐĩĐŗĐ°Đŧ...", "search_timezone": "ĐŸĐžĐ¸ŅĐē Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅĐ°...", @@ -1559,12 +1604,13 @@ "select_from_computer": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ҁ ĐēĐžĐŧĐŋŅŒŅŽŅ‚ĐĩŅ€Đ°", "select_keep_all": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "select_library_owner": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ вĐģадĐĩĐģŅŒŅ†Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēи", - "select_new_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŊОвОĐĩ ĐģĐ¸Ņ†Đž", + "select_new_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐžĐĩ ĐģĐ¸Ņ†Đž", + "select_person_to_tag": "Đ’Ņ‹Đ´ĐĩĐģĐ¸Ņ‚Đĩ ҇ĐĩĐģОвĐĩĐēа, ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ", "select_photos": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", "select_trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "select_user_for_sharing_page_err_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "selected": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž", - "selected_count": "{count, plural, one {# Đ˛Ņ‹ĐąŅ€Đ°ĐŊ} other {# Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž}}", + "selected_count": "{count, plural, one {Đ’Ņ‹ĐąŅ€Đ°ĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", "send_message": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ", "send_welcome_email": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛ĐĩĐŊĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž", "server_endpoint": "ĐĐ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", @@ -1581,36 +1627,38 @@ "set_date_of_birth": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", "set_profile_picture": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", "set_slideshow_to_fullscreen": "ПĐĩŅ€ĐĩвĐĩĐ´Đ¸Ņ‚Đĩ ҁĐģаКд-ŅˆĐžŅƒ в ĐŋĐžĐģĐŊĐžŅĐēŅ€Đ°ĐŊĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", - "setting_image_viewer_help": "ĐŸŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ҁĐŋĐĩŅ€Đ˛Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°, ĐˇĐ°Ņ‚ĐĩĐŧ \n҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đ° (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž), а ĐˇĐ°Ņ‚ĐĩĐŧ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž).", - "setting_image_viewer_original_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐŗĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ в ĐŋĐžĐģĐŊĐžĐŧ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊии (йОĐģŅŒŅˆĐžĐĩ!).\nĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ҃ĐŧĐĩĐŊŅŒŅˆĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐŧ даĐŊĐŊҋ҅ (ĐēаĐē ҁĐĩŅ‚Đ¸, Ņ‚Đ°Đē и ĐēŅŅˆĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°).", + "setting_image_viewer_help": "ĐŸŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ҁĐŋĐĩŅ€Đ˛Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°, ĐˇĐ°Ņ‚ĐĩĐŧ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đ° (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž), а ĐˇĐ°Ņ‚ĐĩĐŧ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ (ĐĩҁĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž).", + "setting_image_viewer_original_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐŗĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ в ĐŋĐžĐģĐŊĐžĐŧ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊии (йОĐģŅŒŅˆĐžĐĩ!). ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐ¸Ņ ĐžĐąŅŠŅ‘Đŧа даĐŊĐŊҋ҅ (ĐēаĐē ҁĐĩŅ‚Đ¸, Ņ‚Đ°Đē и ĐēŅŅˆĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°).", "setting_image_viewer_original_title": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", - "setting_image_viewer_preview_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ.\nĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ иĐģи ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņƒ.", + "setting_image_viewer_preview_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ҁҀĐĩĐ´ĐŊĐĩĐŗĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ. ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ иĐģи ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņƒ.", "setting_image_viewer_preview_title": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", "setting_image_viewer_title": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "setting_languages_apply": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", "setting_languages_subtitle": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅĐˇŅ‹Đē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "setting_languages_title": "Đ¯ĐˇŅ‹Đē", - "setting_notifications_notify_failures_grace_period": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģŅŅ‚ŅŒ Ой ĐžŅˆĐ¸ĐąĐēĐ°Ņ… Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ: {}", - "setting_notifications_notify_hours": "{} ҇.", + "setting_notifications_notify_failures_grace_period": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģŅŅ‚ŅŒ Ой ĐžŅˆĐ¸ĐąĐēĐ°Ņ… Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ: {duration}", + "setting_notifications_notify_hours": "{count} ҇.", "setting_notifications_notify_immediately": "ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž", - "setting_notifications_notify_minutes": "{} ĐŧиĐŊ.", + "setting_notifications_notify_minutes": "{count} ĐŧиĐŊ.", "setting_notifications_notify_never": "ĐŊиĐēĐžĐŗĐ´Đ°", - "setting_notifications_notify_seconds": "{} ҁĐĩĐē.", + "setting_notifications_notify_seconds": "{count} ҁĐĩĐē.", "setting_notifications_single_progress_subtitle": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Ņ…ĐžĐ´Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ´ĐģŅ ĐēаĐļĐ´ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", "setting_notifications_single_progress_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ Ņ…ĐžĐ´ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "setting_notifications_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "setting_notifications_total_progress_subtitle": "ĐžĐąŅ‰Đ¸Đš ĐŋŅ€ĐžĐŗŅ€Đĩҁҁ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи (Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐž/Đ˛ŅĐĩĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "setting_notifications_total_progress_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đš ĐŋŅ€ĐžĐŗŅ€Đĩҁҁ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "setting_video_viewer_looping_title": "ĐĻиĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ", - "setting_video_viewer_original_video_subtitle": "ĐŸŅ€Đ¸ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊии видĐĩĐž ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ, даĐļĐĩ ĐĩҁĐģи Đ´ĐžŅŅ‚ŅƒĐŋĐŊа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐ°Ņ вĐĩŅ€ŅĐ¸Ņ. МоĐļĐĩŅ‚ ĐŋŅ€Đ¸Đ˛ĐĩŅŅ‚Đ¸ Đē ĐąŅƒŅ„ĐĩŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸. НĐĩ вĐģĐ¸ŅĐĩŅ‚ ĐŊа ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ видĐĩĐž", + "setting_video_viewer_original_video_subtitle": "ĐŸŅ€Đ¸ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊии видĐĩĐž ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ, даĐļĐĩ ĐĩҁĐģи Đ´ĐžŅŅ‚ŅƒĐŋĐŊа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐ°Ņ вĐĩŅ€ŅĐ¸Ņ. МоĐļĐĩŅ‚ ĐŋŅ€Đ¸Đ˛ĐĩŅŅ‚Đ¸ Đē ĐąŅƒŅ„ĐĩŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸. ВидĐĩĐž, Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐģĐžĐēаĐģҌĐŊĐž, Đ˛ŅĐĩĐŗĐ´Đ° Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´ŅŅ‚ŅŅ в Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐŧ ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ.", "setting_video_viewer_original_video_title": "ĐĸĐžĐģҌĐēĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģҌĐŊĐžĐĩ видĐĩĐž", "settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "settings_require_restart": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ Đ˛ŅŅ‚ŅƒĐŋиĐģи в ŅĐ¸Đģ҃", "settings_saved": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", + "setup_pin_code": "ХОСдаĐŊиĐĩ PIN-ĐēОда", "share": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ", "share_add_photos": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž", - "share_assets_selected": "{} Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", + "share_assets_selected": "{count} Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", "share_dialog_preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", + "share_link": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁҁҋĐģĐēОК", "shared": "ĐžĐąŅ‰Đ¸e", "shared_album_activities_input_disable": "КоĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹", "shared_album_activity_remove_content": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ?", @@ -1623,34 +1671,33 @@ "shared_by_user": "ВĐģадĐĩĐģĐĩ҆: {user}", "shared_by_you": "Đ’Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ", "shared_from_partner": "Đ¤ĐžŅ‚Đž ĐžŅ‚ {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", + "shared_intent_upload_button_progress_text": "{current} / {total} Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", "shared_link_app_bar_title": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", "shared_link_clipboard_copied_massage": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа", - "shared_link_clipboard_text": "ĐĄŅŅ‹ĐģĐēа: {}\nĐŸĐ°Ņ€ĐžĐģҌ: {}", + "shared_link_clipboard_text": "ĐĄŅŅ‹ĐģĐēа: {link}\nĐŸĐ°Ņ€ĐžĐģҌ: {password}", "shared_link_create_error": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐžĐˇĐ´Đ°ĐŊии ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", "shared_link_edit_description_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", "shared_link_edit_expire_after_option_day": "1 Đ´ĐĩĐŊҌ", - "shared_link_edit_expire_after_option_days": "{} Đ´ĐŊĐĩĐš", + "shared_link_edit_expire_after_option_days": "{count} Đ´ĐŊĐĩĐš", "shared_link_edit_expire_after_option_hour": "1 Ņ‡Đ°Ņ", - "shared_link_edit_expire_after_option_hours": "{} Ņ‡Đ°ŅĐžĐ˛", + "shared_link_edit_expire_after_option_hours": "{count} Ņ‡Đ°ŅĐžĐ˛", "shared_link_edit_expire_after_option_minute": "1 ĐŧиĐŊŅƒŅ‚Ņƒ", - "shared_link_edit_expire_after_option_minutes": "{} ĐŧиĐŊŅƒŅ‚", - "shared_link_edit_expire_after_option_months": "{} ĐŧĐĩŅŅŅ†Đĩв", - "shared_link_edit_expire_after_option_year": "{} ĐģĐĩŅ‚", + "shared_link_edit_expire_after_option_minutes": "{count} ĐŧиĐŊŅƒŅ‚", + "shared_link_edit_expire_after_option_months": "{count} ĐŧĐĩŅŅŅ†Đĩв", + "shared_link_edit_expire_after_option_year": "{count} ĐģĐĩŅ‚", "shared_link_edit_password_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", "shared_link_edit_submit_button": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ҁҁҋĐģĐē҃", "shared_link_error_server_url_fetch": "НĐĩвОСĐŧĐžĐļĐŊĐž СаĐŋŅ€ĐžŅĐ¸Ņ‚ŅŒ URL ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "shared_link_expires_day": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} Đ´ĐĩĐŊҌ", - "shared_link_expires_days": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} Đ´ĐŊĐĩĐš", - "shared_link_expires_hour": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} Ņ‡Đ°Ņ", - "shared_link_expires_hours": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} Ņ‡Đ°ŅĐžĐ˛", - "shared_link_expires_minute": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} ĐŧиĐŊŅƒŅ‚Ņƒ", - "shared_link_expires_minutes": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} ĐŧиĐŊŅƒŅ‚", + "shared_link_expires_day": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} Đ´ĐĩĐŊҌ", + "shared_link_expires_days": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} Đ´ĐŊĐĩĐš", + "shared_link_expires_hour": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} Ņ‡Đ°Ņ", + "shared_link_expires_hours": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} Ņ‡Đ°ŅĐžĐ˛", + "shared_link_expires_minute": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} ĐŧиĐŊŅƒŅ‚Ņƒ", + "shared_link_expires_minutes": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} ĐŧиĐŊŅƒŅ‚", "shared_link_expires_never": "ВĐĩ҇ĐŊĐ°Ņ ҁҁҋĐģĐēа", - "shared_link_expires_second": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} ҁĐĩĐē҃ĐŊĐ´Ņƒ", - "shared_link_expires_seconds": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС {} ҁĐĩĐē҃ĐŊĐ´", + "shared_link_expires_second": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} ҁĐĩĐē҃ĐŊĐ´Ņƒ", + "shared_link_expires_seconds": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} ҁĐĩĐē҃ĐŊĐ´", "shared_link_individual_shared": "ИĐŊĐ´Đ¸Đ˛Đ¸Đ´ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐžĐąŅ‰Đ¸Đš Đ´ĐžŅŅ‚ŅƒĐŋ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧи ҁҁҋĐģĐēаĐŧи", "shared_link_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊҋ҅ ҁҁҋĐģĐžĐē", "shared_links": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", @@ -1711,7 +1758,7 @@ "stack_duplicates": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "stack_select_one_photo": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŗĐģавĐŊŅƒŅŽ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ Đ´ĐģŅ ĐŗŅ€ŅƒĐŋĐŋŅ‹", "stack_selected_photos": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", - "stacked_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ дОйавĐģĐĩĐŊ} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ° дОйавĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ дОйавĐģĐĩĐŊĐž}} в ĐŗŅ€ŅƒĐŋĐŋ҃", + "stacked_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐžĐąŅŠĐĩдиĐŊĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž}} в ĐŗŅ€ŅƒĐŋĐŋ҃", "stacktrace": "ĐĸŅ€Đ°ŅŅĐ¸Ņ€ĐžĐ˛Đēа ҁ҂ĐĩĐēа", "start": "ĐĄŅ‚Đ°Ņ€Ņ‚", "start_date": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ‡Đ°Đģа", @@ -1723,6 +1770,7 @@ "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ҁ ŅŅ‚Đ¸Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ", "storage": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", "storage_label": "ĐĸĐĩĐŗ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", + "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_usage": "{used} иС {available} Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅", "submit": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", "suggestions": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", @@ -1740,17 +1788,17 @@ "tag_created": "ĐĸĐĩĐŗ {tag} ŅĐžĐˇĐ´Đ°ĐŊ", "tag_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž, ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊҋ҅ ĐŋĐž Ņ‚ĐĩĐŗĐ°Đŧ", "tag_not_found_question": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŊĐ°ĐšŅ‚Đ¸ Ņ‚ĐĩĐŗ? ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš Ņ‚ĐĩĐŗ.", - "tag_people": "ĐĸĐĩĐŗ ĐģŅŽĐ´ĐĩĐš", + "tag_people": "ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "tag_updated": "ĐĸĐĩĐŗ {tag} иСĐŧĐĩĐŊĐĩĐŊ", - "tagged_assets": "ПоĐŧĐĩ҇ĐĩĐŊĐž {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", + "tagged_assets": "ĐĸĐĩĐŗ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ Đ´ĐģŅ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", "tags": "ĐĸĐĩĐŗĐ¸", "template": "ШайĐģĐžĐŊ", "theme": "ĐĸĐĩĐŧа", "theme_selection": "Đ’Ņ‹ĐąĐžŅ€ Ņ‚ĐĩĐŧŅ‹", "theme_selection_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ Ņ‚ĐĩĐŧ҃ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ŅĐ¸ŅŅ‚ĐĩĐŧĐŊҋ҅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ˛Đ°ŅˆĐĩĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", "theme_setting_asset_list_storage_indicator_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ иĐŊдиĐēĐ°Ņ‚ĐžŅ€ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŊа ĐŋĐģĐ¸Ņ‚ĐēĐ°Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", - "theme_setting_asset_list_tiles_per_row_title": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в ŅŅ‚Ņ€ĐžĐēĐĩ ({})", - "theme_setting_colorful_interface_subtitle": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžŅ‚Ņ‚ĐĩĐŊĐžĐē Đē Ņ„ĐžĐŊ҃", + "theme_setting_asset_list_tiles_per_row_title": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в ŅŅ‚Ņ€ĐžĐēĐĩ ({count})", + "theme_setting_colorful_interface_subtitle": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžŅ‚Ņ‚ĐĩĐŊĐžĐē Đē Ņ„ĐžĐŊ҃.", "theme_setting_colorful_interface_title": "ĐĻвĐĩŅ‚ Ņ„ĐžĐŊа", "theme_setting_image_viewer_quality_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đ° ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "theme_setting_image_viewer_quality_title": "ĐšĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК", @@ -1783,18 +1831,20 @@ "trash_emptied": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ĐžŅ‡Đ¸Ņ‰ĐĩĐŊа", "trash_no_results_message": "ЗдĐĩҁҌ ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒŅŅ ŅƒĐ´Đ°ĐģŅ‘ĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž.", "trash_page_delete_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ", - "trash_page_empty_trash_dialog_content": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃? Đ­Ņ‚Đ¸ Ņ„Đ°ĐšĐģŅ‹ ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Immich.", - "trash_page_info": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {} Đ´ĐŊĐĩĐš", + "trash_page_empty_trash_dialog_content": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃? ĐžĐąŅŠĐĩĐē҂ҋ в ĐŊĐĩĐš ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Immich.", + "trash_page_info": "ĐžĐąŅŠĐĩĐē҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {days} Đ´ĐŊĐĩĐš", "trash_page_no_assets": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ĐŋŅƒŅŅ‚Đ°", "trash_page_restore_all": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "trash_page_select_assets_btn": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", - "trash_page_title": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ({})", + "trash_page_title": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ({count})", "trashed_items_will_be_permanently_deleted_after": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} other {# Đ´ĐŊĐĩĐš}}.", "type": "ĐĸиĐŋ", + "unable_to_change_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ иСĐŧĐĩĐŊĐĩĐŊии PIN-ĐēОда", + "unable_to_setup_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐžĐˇĐ´Đ°ĐŊии PIN-ĐēОда", "unarchive": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", - "unarchived_count": "{count, plural, other {Đ’ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž иС Đ°Ņ€Ņ…Đ¸Đ˛Đ° #}}", + "unarchived_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰Ņ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž}} иС Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "unfavorite": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", - "unhide_person": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋĐĩŅ€ŅĐžĐŊ҃", + "unhide_person": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "unknown": "НĐĩиСвĐĩҁ҂ĐŊĐž", "unknown_country": "НĐĩиСвĐĩҁ҂ĐŊĐ°Ņ ŅŅ‚Ņ€Đ°ĐŊа", "unknown_year": "НĐĩиСвĐĩҁ҂ĐŊŅ‹Đš Год", @@ -1807,34 +1857,38 @@ "unnamed_album_delete_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ аĐģŅŒĐąĐžĐŧ?", "unnamed_share": "ĐžĐąŅ‰Đ¸Đš Đ´ĐžŅŅ‚ŅƒĐŋ ĐąĐĩС ĐŊаСваĐŊĐ¸Ņ", "unsaved_change": "НĐĩ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊĐžĐĩ иСĐŧĐĩĐŊĐĩĐŊиĐĩ", - "unselect_all": "ĐĄĐŊŅŅ‚ŅŒ Đ˛ŅŅ‘", + "unselect_all": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ", "unselect_all_duplicates": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€ Đ˛ŅĐĩŅ… Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", "unstack": "Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", - "unstacked_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ иСвĐģĐĩ҇ĐĩĐŊ} few {# ĐžĐąŅŠĐĩĐēŅ‚Đ° иСвĐģĐĩ҇ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ иСвĐģĐĩ҇ĐĩĐŊĐž}} иС ĐŗŅ€ŅƒĐŋĐŋŅ‹", + "unstacked_assets_count": "{count, plural, one {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", "untracked_files": "НЕОĐĸСЛЕЖИВАЕМĐĢЕ ФАЙЛĐĢ", "untracked_files_decription": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ ĐžŅ‚ŅĐģĐĩĐļиваĐĩŅ‚ ŅŅ‚Đ¸ Ņ„Đ°ĐšĐģŅ‹. ОĐŊи ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐŧ ĐŊĐĩŅƒĐ´Đ°Ņ‡ĐŊҋ҅ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊиК, ĐŋŅ€ĐĩŅ€Đ˛Đ°ĐŊĐŊҋ҅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐžĐē иĐģи ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊŅ‹ иС-Са ĐžŅˆĐ¸ĐąĐēи", "up_next": "ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐĩ", + "updated_at": "ОбĐŊОвĐģŅ‘ĐŊ", "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ ОйĐŊОвĐģŅ‘ĐŊ", "upload": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", - "upload_dialog_info": "ĐĨĐžŅ‚Đ¸Ņ‚Đĩ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅƒŅŽ ĐēĐžĐŋĐ¸ŅŽ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ?", + "upload_dialog_info": "ĐĨĐžŅ‚Đ¸Ņ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€?", "upload_dialog_title": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚", - "upload_errors": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа СавĐĩŅ€ŅˆĐĩĐŊа ҁ {count, plural, one {# ĐžŅˆĐ¸ĐąĐēОК} other {# ĐžŅˆĐ¸ĐąĐēаĐŧи}}, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹.", + "upload_errors": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа СавĐĩŅ€ŅˆĐĩĐŊа ҁ {count, plural, one {# ĐžŅˆĐ¸ĐąĐēОК} other {# ĐžŅˆĐ¸ĐąĐēаĐŧи}}, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ.", "upload_progress": "ĐžŅŅ‚Đ°ĐģĐžŅŅŒ {remaining, number} - ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ°ĐŊĐž {processed, number}/{total, number}", - "upload_skipped_duplicates": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊ{count, plural, one { # Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ Ņ€ĐĩŅŅƒŅ€Ņ} few {Đž # Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°} many {Đž # Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛} other {Đž # Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°}}", + "upload_skipped_duplicates": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊ{count, plural, one { # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚} many {Đž # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛} other {Đž # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ°}}", "upload_status_duplicates": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "upload_status_errors": "ĐžŅˆĐ¸ĐąĐēи", "upload_status_uploaded": "Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", "upload_success": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŋŅ€ĐžŅˆĐģа ҃ҁĐŋĐĩ҈ĐŊĐž. ОбĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ.", - "upload_to_immich": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в Immich ({})", + "upload_to_immich": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в Immich ({count})", "uploading": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ", - "url": "URL", "usage": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", + "use_biometric": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ йиОĐŧĐĩŅ‚Ņ€Đ¸ŅŽ", "use_current_connection": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ", "use_custom_date_range": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК диаĐŋаСОĐŊ Đ´Đ°Ņ‚", "user": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ", + "user_has_been_deleted": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ĐąŅ‹Đģ ŅƒĐ´Đ°ĐģŅ‘ĐŊ.", "user_id": "ID ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "user_liked": "{user} ĐžŅ‚ĐŧĐĩŅ‚Đ¸Đģ(а) {type, select, photo {ŅŅ‚Đž Ņ„ĐžŅ‚Đž} video {ŅŅ‚Đž видĐĩĐž} asset {ŅŅ‚ĐžŅ‚ Ņ€ĐĩŅŅƒŅ€Ņ} other {ŅŅ‚ĐžŅ‚ аĐģŅŒĐąĐžĐŧ}}", + "user_pin_code_settings": "PIN-ĐēОд", + "user_pin_code_settings_description": "ХОСдаĐŊиĐĩ, иСĐŧĐĩĐŊĐĩĐŊиĐĩ иĐģи ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ĐŋĐĩŅ€ŅĐžĐŊаĐģҌĐŊĐžĐŗĐž PIN-ĐēОда Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē ĐˇĐ°Ņ‰Đ¸Ņ‰Ņ‘ĐŊĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ", "user_purchase_settings": "ПоĐē҃ĐŋĐēа", "user_purchase_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐžĐē҃ĐŋĐēОК", "user_role_set": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ {user} в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ {role}", @@ -1849,11 +1903,11 @@ "variables": "ПĐĩŅ€ĐĩĐŧĐĩĐŊĐŊŅ‹Đĩ", "version": "ВĐĩŅ€ŅĐ¸Ņ", "version_announcement_closing": "ĐĸвОК Đ´Ņ€ŅƒĐŗ АĐģĐĩĐēҁ", - "version_announcement_message": "Đ—Đ´Ņ€Đ°Đ˛ŅŅ‚Đ˛ŅƒĐšŅ‚Đĩ! Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ вĐĩŅ€ŅĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžŅ‡Ņ‚Đ¸Ņ‚Đĩ СаĐŧĐĩŅ‚Đēи Đē Đ˛Ņ‹Đŋ҃ҁĐē҃ и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Đ°ŅˆĐ¸ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ docker-compose.yml и .env аĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹, Ņ‡Ņ‚ĐžĐąŅ‹ иСйĐĩĐļĐ°Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐžĐē в ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Đ¸, ĐžŅĐžĐąĐĩĐŊĐŊĐž ĐĩҁĐģи Đ˛Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ WatchTower иĐģи Đ´Ņ€ŅƒĐŗĐžĐš ĐŧĐĩŅ…Đ°ĐŊиСĐŧ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ.", + "version_announcement_message": "Đ—Đ´Ņ€Đ°Đ˛ŅŅ‚Đ˛ŅƒĐšŅ‚Đĩ! Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ вĐĩŅ€ŅĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžŅ‡Ņ‚Đ¸Ņ‚Đĩ СаĐŧĐĩŅ‚Đēи Đē Đ˛Ņ‹Đŋ҃ҁĐē҃ и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Đ¸ аĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹, Đ´Đ°ĐąŅ‹ иСйĐĩĐļĐ°Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐžĐē, ĐžŅĐžĐąĐĩĐŊĐŊĐž ĐĩҁĐģи Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ WatchTower иĐģи Đ´Ņ€ŅƒĐŗĐžĐš ĐŧĐĩŅ…Đ°ĐŊиСĐŧ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ.", "version_announcement_overlay_release_notes": "ĐŋŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊĐ¸Ņ Đē Đ˛Ņ‹Đŋ҃ҁĐē҃", "version_announcement_overlay_text_1": "ĐŸŅ€Đ¸Đ˛ĐĩŅ‚, Đ´Ņ€ŅƒĐŗ! Đ’Ņ‹ŅˆĐģа ĐŊĐžĐ˛Đ°Ņ вĐĩŅ€ŅĐ¸Ņ", - "version_announcement_overlay_text_2": "ĐŋĐžĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐžŅĐĩŅ‚Đ¸Ņ‚Đĩ", - "version_announcement_overlay_text_3": " и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Đ°ŅˆĐ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи docker-compose и .env ОйĐŊОвĐģĐĩĐŊŅ‹, ĐžŅĐžĐąĐĩĐŊĐŊĐž ĐĩҁĐģи Đ˛Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ WatchTower иĐģи ĐģŅŽĐąĐžĐš Đ´Ņ€ŅƒĐŗĐžĐš ĐŧĐĩŅ…Đ°ĐŊиСĐŧ, ĐēĐžŅ‚ĐžŅ€Ņ‹Đš Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ОйĐŊОвĐģŅĐĩŅ‚ ҁĐĩŅ€Đ˛ĐĩŅ€.", + "version_announcement_overlay_text_2": "ĐŋĐžĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐžŅĐĩŅ‚Đ¸Ņ‚Đĩ ", + "version_announcement_overlay_text_3": " и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ в docker-compose и .env Ņ„Đ°ĐšĐģĐ°Ņ… аĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹, ĐžŅĐžĐąĐĩĐŊĐŊĐž ĐĩҁĐģи Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ WatchTower иĐģи ĐģŅŽĐąĐžĐš Đ´Ņ€ŅƒĐŗĐžĐš ĐŧĐĩŅ…Đ°ĐŊиСĐŧ Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ОйĐŊОвĐģĐĩĐŊĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК.", "version_announcement_overlay_title": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊĐžĐ˛Đ°Ņ вĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° 🎉", "version_history": "Đ˜ŅŅ‚ĐžŅ€Đ¸Ņ вĐĩŅ€ŅĐ¸Đš", "version_history_item": "ВĐĩŅ€ŅĐ¸Ņ {version} ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊа {date}", @@ -1861,7 +1915,7 @@ "video_hover_setting": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸", "video_hover_setting_description": "Đ’ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ŅŒ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ видĐĩĐž ĐŋŅ€Đ¸ ĐŊавĐĩĐ´ĐĩĐŊии ĐēŅƒŅ€ŅĐžŅ€Đ° ĐŧŅ‹ŅˆĐ¸ ĐŊа ĐžĐąŅŠĐĩĐēŅ‚. ДаĐļĐĩ ĐĩҁĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ, Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ĐŧĐžĐļĐŊĐž СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ, ĐŊавĐĩĐ´Ņ ĐēŅƒŅ€ŅĐžŅ€ ĐŊа СĐŊĐ°Ņ‡ĐžĐē Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ.", "videos": "ВидĐĩĐž", - "videos_count": "{count, plural, one {# ВидĐĩĐž} few {# ВидĐĩĐž} many {# ВидĐĩĐž} other {# ВидĐĩĐž}}", + "videos_count": "{count, plural, one {# видĐĩĐž} other {# видĐĩĐž}}", "view": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€", "view_album": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "view_all": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛ŅŅ‘", @@ -1877,13 +1931,14 @@ "viewer_remove_from_stack": "ĐŖĐąŅ€Đ°Ņ‚ŅŒ иС ĐŗŅ€ŅƒĐŋĐŋŅ‹", "viewer_stack_use_as_main_asset": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ ĐžŅĐŊОвĐŊĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", "viewer_unstack": "Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", - "visibility_changed": "ВидиĐŧĐžŅŅ‚ŅŒ иСĐŧĐĩĐŊĐĩĐŊа Đ´ĐģŅ {count, plural, one {# ҇ĐĩĐģОвĐĩĐēа} other {# ĐģŅŽĐ´ĐĩĐš}}", + "visibility_changed": "ИСĐŧĐĩĐŊĐĩĐŊа видиĐŧĐžŅŅ‚ŅŒ ҃ {count, plural, one {# ҇ĐĩĐģОвĐĩĐēа} other {# ҇ĐĩĐģОвĐĩĐē}}", "waiting": "В ĐžŅ‡ĐĩŅ€Đĩди", "warning": "ĐŸŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ", "week": "НĐĩĐ´ĐĩĐģŅ", "welcome": "Đ”ĐžĐąŅ€Đž ĐŋĐžĐļаĐģĐžĐ˛Đ°Ņ‚ŅŒ", "welcome_to_immich": "Đ”ĐžĐąŅ€Đž ĐŋĐžĐļаĐģĐžĐ˛Đ°Ņ‚ŅŒ в Immich", "wifi_name": "ИĐŧŅ ҁĐĩŅ‚Đ¸", + "wrong_pin_code": "НĐĩвĐĩŅ€ĐŊŅ‹Đš PIN-ĐēОд", "year": "Год", "years_ago": "{years, plural, one {# ĐŗĐžĐ´} few {# ĐŗĐžĐ´Đ°} many {# ĐģĐĩŅ‚} other {# ĐģĐĩŅ‚}} ĐŊаСад", "yes": "Да", diff --git a/i18n/sk.json b/i18n/sk.json index 8ab3b0cded..4253b7eeab 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,7 +14,6 @@ "add_a_location": "PridaÅĨ polohu", "add_a_name": "PridaÅĨ meno", "add_a_title": "PridaÅĨ nÃĄzov", - "add_endpoint": "Add endpoint", "add_exclusion_pattern": "PridaÅĨ vzor vylÃēčenia", "add_import_path": "PridaÅĨ cestu pre import", "add_location": "PridaÅĨ polohu", @@ -187,20 +186,13 @@ "oauth_auto_register": "AutomatickÃĄ regristrÃĄcia", "oauth_auto_register_description": "AutomatickÊ zaregistrovanie novÊho poŞívateÄža pri prihlÃĄsení pomocou OAuth", "oauth_button_text": "Text tlačítka", - "oauth_client_id": "Client ID", - "oauth_client_secret": "Client Secret", "oauth_enable_description": "PrihlÃĄsiÅĨ sa pomocou OAuth", - "oauth_issuer_url": "Adresa URL vydavateÄža", "oauth_mobile_redirect_uri": "URI mobilnÊho presmerovania", "oauth_mobile_redirect_uri_override": "Prepísanie URI mobilnÊho presmerovania", "oauth_mobile_redirect_uri_override_description": "PovoÄžte, keď poskytovateÄž protokolu OAuth nepovoÄžuje identifikÃĄtor URI pre mobilnÊ zariadenia, napríklad '{callback}'", - "oauth_profile_signing_algorithm": "Algoritmus podpisovania profilu", - "oauth_profile_signing_algorithm_description": "Algoritmus pouŞívanÃŊ na prihlÃĄsenie uŞívateÄžskÊho profilu.", - "oauth_scope": "Rozsah", "oauth_settings": "OAuth", "oauth_settings_description": "SpravovaÅĨ nastavenia prihlÃĄsenia OAuth", "oauth_settings_more_details": "Pre viac informÃĄcii o tejto funkcii, prejdite na docs.", - "oauth_signing_algorithm": "Algoritmus podpisovania", "oauth_storage_label_claim": "NÃĄrokovaÅĨ Å títok ÃēloÅžiska", "oauth_storage_label_claim_description": "Automaticky nastaviÅĨ Å títok ÃēloÅžiska pouŞívateÄža na hodnotu tohto nÃĄroku.", "oauth_storage_quota_claim": "DeklarÃĄcia kvÃŗty ÃēloÅžiska", @@ -366,11 +358,9 @@ "admin_password": "AdministrÃĄtorskÊ heslo", "administration": "AdministrÃĄcia", "advanced": "PokročilÊ", - "advanced_settings_log_level_title": "Úroveň logovania: {}", + "advanced_settings_log_level_title": "Úroveň logovania: {level}", "advanced_settings_prefer_remote_subtitle": "NiektorÊ zariadenia sÃē extrÊmne pomalÊ pre načítavanie miniatÃēr z fotiek na zariadení. PovoÄžte toto nastavenie aby sa namiesto toho načítavali obrÃĄzky zo servera.", "advanced_settings_prefer_remote_title": "PreferovaÅĨ vzdialenÊ obrÃĄzky", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", "advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikÃĄtom zo strany servera. VyÅžaduje sa pre samo-podpísanÊ certifikÃĄty.", "advanced_settings_self_signed_ssl_title": "PovoliÅĨ samo-podpísanÊ SSL certifikÃĄty", "advanced_settings_tile_subtitle": "PokročilÊ nastavenia pouŞívateÄža", @@ -395,9 +385,9 @@ "album_remove_user_confirmation": "Ste si istÃŊ, Åže chcete odstrÃĄniÅĨ pouŞívateÄža {user}?", "album_share_no_users": "VyzerÃĄ to, Åže ste tento album zdieÄžali so vÅĄetkÃŊmi pouŞívateÄžmi alebo nemÃĄte Åžiadneho pouŞívateÄža, s ktorÃŊm by ste ho mohli zdieÄžaÅĨ.", "album_thumbnail_card_item": "1 poloÅžka", - "album_thumbnail_card_items": "{} poloÅžiek", + "album_thumbnail_card_items": "{count} poloÅžiek", "album_thumbnail_card_shared": "ZdieÄžanÊ", - "album_thumbnail_shared_by": "ZdieÄžanÊ od {}", + "album_thumbnail_shared_by": "ZdieÄžanÊ od {user}", "album_updated": "Album bol aktualizovanÃŊ", "album_updated_setting_description": "ObdrÅžaÅĨ e-mailovÊ upozornenie, keď v zdieÄžanom albume pribudnÃē novÊ poloÅžky", "album_user_left": "Opustil {album}", @@ -435,10 +425,9 @@ "archive": "ArchivovaÅĨ", "archive_or_unarchive_photo": "ArchivÃĄcia alebo odarchivovanie fotografie", "archive_page_no_archived_assets": "ÅŊiadne archivovanÊ mÊdiÃĄ", - "archive_page_title": "Archív ({})", + "archive_page_title": "Archív ({count})", "archive_size": "VeÄžkosÅĨ archívu", "archive_size_description": "KonfigurÃĄcia veÄžkosti archívu na stiahnutie (v GiB)", - "archived": "Archived", "archived_count": "{count, plural, other {ArchivovanÃŊch #}}", "are_these_the_same_person": "Ide o tÃē istÃē osobu?", "are_you_sure_to_do_this": "Ste si istÃŊ, Åže to chcete urobiÅĨ?", @@ -460,39 +449,27 @@ "asset_list_settings_title": "FotografickÃĄ mrieÅžka", "asset_offline": "MÊdium je offline", "asset_offline_description": "Toto externÃŊ obsah sa uÅž nenachÃĄdza na disku. PoÅžiadajte o pomoc svojho sprÃĄvcu Immich.", - "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "PreskočenÊ", "asset_skipped_in_trash": "V koÅĄi", "asset_uploaded": "NahranÊ", "asset_uploading": "NahrÃĄva saâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", "asset_viewer_settings_title": "Zobrazovač poloÅžiek", "assets": "PoloÅžky", "assets_added_count": "{count, plural, one {PridanÃĄ # poloÅžka} few {PridanÊ # poloÅžky} other {PridanÃŊch # poloÅžek}}", "assets_added_to_album_count": "Do albumu {count, plural, one {bola pridanÃĄ # poloÅžka} few {boli pridanÊ # poloÅžky} other {bolo pridanÃŊch # poloÅžiek}}", "assets_added_to_name_count": "{count, plural, one {PridanÃĄ # poloÅžka} few {PridanÊ # poloÅžky} other {PridanÃŊch # poloÅžiek}} do {hasName, select, true {alba {name}} other {novÊho albumu}}", "assets_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžiek}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "Do koÅĄa {count, plural, one {bola presunutÃĄ # poloÅžka} few {boli presunutÊ # poloÅžky} other {bolo presunutÃŊch # poloÅžiek}}", "assets_permanently_deleted_count": "Trvalo {count, plural, one {vymazanÃĄ # poloÅžka} few {vymazanÊ # poloÅžky} other {vymazanÃŊch # poloÅžiek}}", "assets_removed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", "assets_restore_confirmation": "Naozaj chcete obnoviÅĨ vÅĄetky vyhodenÊ poloÅžky? TÃēto akciu nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ! Upozorňujeme, Åže tÃŊmto spôsobom nie je moÅžnÊ obnoviÅĨ Åžiadne offline poloÅžky.", "assets_restored_count": "{count, plural, one {ObnovenÃĄ # poloÅžka} few {ObnovenÊ # poloÅžky} other {ObnovenÃŊch # poloÅžiek}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", "assets_trashed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka bola} other {PoloÅžky boli}} sÃēčasÅĨou albumu", "authorized_devices": "AutorizovanÊ zariadenia", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "SpäÅĨ", "back_close_deselect": "SpäÅĨ, zavrieÅĨ alebo zruÅĄiÅĨ vÃŊber", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albumy v zariadení ({})", + "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na poloÅžku ju zahrniete, dvojitÃŊm ÅĨuknutím ju vylÃēčite", "backup_album_selection_page_assets_scatter": "SÃēbory môŞu byÅĨ roztrÃēsenÊ vo viacerÃŊch albumoch. To umoŞňuje zahrnÃēÅĨ alebo vylÃēčiÅĨ albumy počas procesu zÃĄlohovania.", "backup_album_selection_page_select_albums": "VybranÊ albumy", @@ -501,22 +478,21 @@ "backup_all": "VÅĄetko", "backup_background_service_backup_failed_message": "ZÃĄlohovanie mÊdií zlyhalo. SkÃēÅĄam to znova...", "backup_background_service_connection_failed_message": "Nepodarilo sa pripojiÅĨ k serveru. SkÃēÅĄam to znova...", - "backup_background_service_current_upload_notification": "NahrÃĄvanie {}", + "backup_background_service_current_upload_notification": "NahrÃĄvanie {filename}", "backup_background_service_default_notification": "Kontrola novÃŊch mÊdií {}", "backup_background_service_error_title": "Chyba zÃĄlohovania", "backup_background_service_in_progress_notification": "VytvÃĄram kÃŗpiu vaÅĄich mÊdií...", - "backup_background_service_upload_failure_notification": "Nepodarilo sa nahraÅĨ {}", + "backup_background_service_upload_failure_notification": "Nepodarilo sa nahraÅĨ {filename}", "backup_controller_page_albums": "ZÃĄlohovanÊ albumy", "backup_controller_page_background_app_refresh_disabled_content": "Ak chcete pouŞívaÅĨ zÃĄlohovanie na pozadí, povoÄžte obnovovanie aplikÃĄcií na pozadí v ponuke Nastavenia > VÅĄeobecnÊ > Obnovovanie aplikÃĄcií na pozadí.", "backup_controller_page_background_app_refresh_disabled_title": "Obnovovanie aplikÃĄcií na pozadí je vypnutÊ.", "backup_controller_page_background_app_refresh_enable_button_text": "PrejsÅĨ do nastavení", "backup_controller_page_background_battery_info_link": "UkÃĄÅž mi ako", "backup_controller_page_background_battery_info_message": "Ak chcete dosiahnuÅĨ najlepÅĄie vÃŊsledky pri zÃĄlohovaní na pozadí, vypnite vÅĄetky optimalizÃĄcie batÊrie, ktorÊ obmedzujÃē aktivitu na pozadí pre Immich vo vaÅĄom zariadení. KeďŞe to zÃĄvisí od zariadenia, skontrolujte poÅžadovanÊ informÃĄcie pre vÃŊrobcu vÃĄÅĄho zariadenia.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "OptimalizÃĄcia batÊrie", "backup_controller_page_background_charging": "Len počas nabíjania", "backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovaÅĨ sluÅžbu na pozadí", - "backup_controller_page_background_delay": "Oneskorenie zÃĄlohovania novÃŊch mÊdií: {}", + "backup_controller_page_background_delay": "Oneskorenie zÃĄlohovania novÃŊch mÊdií: {duration}", "backup_controller_page_background_description": "PovoÄžte sluÅžbu na pozadí na automatickÊ zÃĄlohovanie vÅĄetkÃŊch novÃŊch aktív bez nutnosti otvorenia aplikÃĄcie", "backup_controller_page_background_is_off": "AutomatickÊ zÃĄlohovanie na pozadí je vypnutÊ", "backup_controller_page_background_is_on": "AutomatickÊ zÃĄlohovanie na pozadí je zapnutÊ", @@ -526,12 +502,11 @@ "backup_controller_page_backup": "ZÃĄlohovanie", "backup_controller_page_backup_selected": "VybranÊ: ", "backup_controller_page_backup_sub": "ZÃĄlohovanÊ fotografie a videa", - "backup_controller_page_created": "VytvorenÊ: {}", + "backup_controller_page_created": "VytvorenÊ: {date}", "backup_controller_page_desc_backup": "Zapnite zÃĄlohovanie na popredí, aby sa novÊ poloÅžky automaticky nahrÃĄvali na server pri otvorení aplikÃĄcie.", "backup_controller_page_excluded": "VylÃēčenÊ: ", - "backup_controller_page_failed": "Nepodarilo sa ({})", - "backup_controller_page_filename": "NÃĄzov sÃēboru: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Nepodarilo sa ({count})", + "backup_controller_page_filename": "NÃĄzov sÃēboru: {filename} [{size}]", "backup_controller_page_info": "InformÃĄcie o zÃĄlohovaní", "backup_controller_page_none_selected": "ÅŊiadne vybranÊ", "backup_controller_page_remainder": "ZostÃĄva", @@ -540,7 +515,7 @@ "backup_controller_page_start_backup": "SpustiÅĨ zÃĄlohovanie", "backup_controller_page_status_off": "AutomatickÊ zÃĄlohovanie na popredí je vypnutÊ", "backup_controller_page_status_on": "AutomatickÊ zÃĄlohovanie na popredí je zapnutÊ", - "backup_controller_page_storage_format": "{} z {} pouÅžitÃŊch", + "backup_controller_page_storage_format": "{used} z {total} pouÅžitÃŊch", "backup_controller_page_to_backup": "Albumy ktorÊ budÃē zÃĄlohovanÊ", "backup_controller_page_total_sub": "VÅĄetky jedinečnÊ fotografie a videÃĄ z vybranÃŊch albumov", "backup_controller_page_turn_off": "VypnÃēÅĨ zÃĄlohovanie na popredí", @@ -553,7 +528,6 @@ "backup_manual_success": "Úspech", "backup_manual_title": "Stav nahrÃĄvania", "backup_options_page_title": "MoÅžnosti zÃĄlohovania", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Spätne", "birthdate_saved": "DÃĄtum narodenia bol ÃēspeÅĄne uloÅženÃŊ", "birthdate_set_description": "DÃĄtum narodenia sa pouŞíva na vÃŊpočet veku tejto osoby v čase fotografie.", @@ -565,21 +539,21 @@ "bulk_keep_duplicates_confirmation": "Naozaj chceÅĄ ponechaÅĨ {count, plural, one {# duplicitnÃŊ sÃēbor} other {# duplicitnÊ sÃēbory}}? TÃŊmto sa vysporiadaÅĄ so vÅĄetkÃŊmi duplicitnÃŊmi skupinami bez mazania sÃēborov.", "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazaÅĨ {count, plural, one {# duplicitnÃŊ sÃēbor} other {# duplicitnÊ sÃēbory}}? TÃŊmto si ponechÃĄÅĄ z kaÅždej skupiny najvÃ¤ÄÅĄÃ­ sÃēbor a vymaÅžeÅĄ vÅĄetky ostatnÊ duplicitnÊ sÃēbory v skupine.", "buy": "KÃēpiÅĨ Immich", - "cache_settings_album_thumbnails": "NÃĄhÄžady strÃĄnok kniÅžnice (poloÅžiek {})", + "cache_settings_album_thumbnails": "NÃĄhÄžady strÃĄnok kniÅžnice (poloÅžiek {count})", "cache_settings_clear_cache_button": "VymazaÅĨ vyrovnÃĄvaciu pamäÅĨ", "cache_settings_clear_cache_button_title": "VymaÅže vyrovnÃĄvaciu pamäÅĨ aplikÃĄcie. To vÃŊrazne ovplyvní vÃŊkon aplikÃĄcie, kÃŊm sa vyrovnÃĄvacia pamäÅĨ neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAŤ", "cache_settings_duplicated_assets_subtitle": "Fotky a videÃĄ ktorÊ sÃē na čiernej listine zvolenÊ aplikÃĄciou", - "cache_settings_duplicated_assets_title": "DuplikÃĄty ({})", - "cache_settings_image_cache_size": "VeÄžkosÅĨ vyrovnÃĄvacej pamäte (poloÅžiek {})", + "cache_settings_duplicated_assets_title": "DuplikÃĄty ({count})", + "cache_settings_image_cache_size": "VeÄžkosÅĨ vyrovnÃĄvacej pamäte (poloÅžiek {count})", "cache_settings_statistics_album": "KniÅžnica nÃĄhÄžadov", - "cache_settings_statistics_assets": "{} poloÅžky ({})", + "cache_settings_statistics_assets": "{count} poloÅžky ({size})", "cache_settings_statistics_full": "KompletnÊ fotografie", "cache_settings_statistics_shared": "ZdieÄžanÊ nÃĄhÄžady albumov", "cache_settings_statistics_thumbnail": "NÃĄhÄžady", "cache_settings_statistics_title": "PouÅžitie vyrovnÃĄvacej pamäte", "cache_settings_subtitle": "OvlÃĄdanie sprÃĄvania mobilnej aplikÃĄcie Immich v medzipamäti", - "cache_settings_thumbnail_size": "VeÄžkosÅĨ vyrovnÃĄvacej pamäte nÃĄhÄžadov (poloÅžiek {})", + "cache_settings_thumbnail_size": "VeÄžkosÅĨ vyrovnÃĄvacej pamäte nÃĄhÄžadov (poloÅžiek {count})", "cache_settings_tile_subtitle": "OvlÃĄdanie sprÃĄvania lokÃĄlneho ÃēloÅžiska", "cache_settings_tile_title": "LokÃĄlne ÃēloÅžisko", "cache_settings_title": "Nastavenia vyrovnÃĄvacej pamäte", @@ -588,12 +562,10 @@ "camera_model": "Model fotoaparÃĄtu", "cancel": "ZruÅĄiÅĨ", "cancel_search": "ZruÅĄiÅĨ vyhÄžadÃĄvanie", - "canceled": "Canceled", "cannot_merge_people": "Nie je moÅžnÊ zlÃēčiÅĨ Äžudí", "cannot_undo_this_action": "TÃēto akciu nemôŞete vrÃĄtiÅĨ späÅĨ!", "cannot_update_the_description": "Popis nie je moÅžnÊ aktualizovaÅĨ", "change_date": "UpraviÅĨ dÃĄtum", - "change_display_order": "Change display order", "change_expiration_time": "ZmeniÅĨ čas vyprÅĄania", "change_location": "UpraviÅĨ lokÃĄciu", "change_name": "UpraviÅĨ meno", @@ -608,9 +580,6 @@ "change_your_password": "Zmeňte si heslo", "changed_visibility_successfully": "ViditeÄžnosÅĨ bola ÃēspeÅĄne zmenenÃĄ", "check_all": "SkontrolovaÅĨ VÅĄetko", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "SkontrolovaÅĨ logy", "choose_matching_people_to_merge": "Vyberte rovnakÃŊch Äžudí na zlÃēčenie", "city": "Mesto", @@ -619,14 +588,6 @@ "clear_all_recent_searches": "VymazaÅĨ nedÃĄvne vyhÄžadÃĄvania", "clear_message": "VymazaÅĨ sprÃĄvu", "clear_value": "VymazaÅĨ hodnotu", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "clockwise": "V smere hodinovÃŊch ručičiek", "close": "ZatvoriÅĨ", "collapse": "ZbaliÅĨ", @@ -639,7 +600,6 @@ "comments_are_disabled": "KomentÃĄre sÃē vypnutÊ", "common_create_new_album": "VytvoriÅĨ novÃŊ album", "common_server_error": "Skontrolujte svoje sieÅĨovÊ pripojenie, uistite sa, Åže server je dostupnÃŊ a verzie aplikÃĄcie/server sÃē kompatibilnÊ.", - "completed": "Completed", "confirm": "PotvrdiÅĨ", "confirm_admin_password": "PotvrdiÅĨ AdministrÃĄtorskÊ Heslo", "confirm_delete_face": "Naozaj chcete z poloÅžky odstrÃĄniÅĨ tvÃĄr osoby {name}?", @@ -649,14 +609,12 @@ "contain": "ObsiahnÃēÅĨ", "context": "Kontext", "continue": "PokračovaÅĨ", - "control_bottom_app_bar_album_info_shared": "{} poloÅžiek - zdieÄžanÊ", + "control_bottom_app_bar_album_info_shared": "{count} poloÅžiek - zdieÄžanÊ", "control_bottom_app_bar_create_new_album": "VytvoriÅĨ novÃŊ album", "control_bottom_app_bar_delete_from_immich": "VymazaÅĨ z Immichu", "control_bottom_app_bar_delete_from_local": "VymazaÅĨ zo zariadenia", "control_bottom_app_bar_edit_location": "UpraviÅĨ polohu", "control_bottom_app_bar_edit_time": "UpraviÅĨ dÃĄtum a čas", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", "control_bottom_app_bar_trash_from_immich": "PresunÃēÅĨ do koÅĄa", "copied_image_to_clipboard": "ObrÃĄzok skopírovanÃŊ do schrÃĄnky.", "copied_to_clipboard": "SkopírovanÊ do schrÃĄnky!", @@ -677,7 +635,6 @@ "create_link": "VytvoriÅĨ odkaz", "create_link_to_share": "VytvoriÅĨ odkaz na zdieÄžanie", "create_link_to_share_description": "UmoÅžniÅĨ kaÅždÊmu kto mÃĄ odkaz zobraziÅĨ vybranÊ fotografie", - "create_new": "CREATE NEW", "create_new_person": "VytvoriÅĨ novÃē osobu", "create_new_person_hint": "PriradiÅĨ vybranÊ poloÅžky novej osobe", "create_new_user": "Vytvorenie novÊho pouŞívateÄža", @@ -687,10 +644,8 @@ "create_tag_description": "Vytvorenie novÊho ÅĄtítku. Pre VnorenÊ ÅĄtítky, prosím, zadaj celÃē cestu ÅĄtítku, vrÃĄtane lomítok vpred.", "create_user": "VytvoriÅĨ pouŞívateÄža", "created": "VytvorenÊ", - "crop": "Crop", "curated_object_page_title": "Veci", "current_device": "SÃēčasnÊ zariadenie", - "current_server_address": "Current server address", "custom_locale": "VlastnÃĄ LokalizÃĄcia", "custom_locale_description": "FormÃĄtovanie dÃĄtumov a čísel podÄža jazyka a regiÃŗnu", "daily_title_text_date": "EEEE, d. MMMM", @@ -741,7 +696,6 @@ "direction": "Smer", "disabled": "VypnutÊ", "disallow_edits": "ZakÃĄzaÅĨ editovanie", - "discord": "Discord", "discover": "ObjaviÅĨ", "dismiss_all_errors": "ZamietnuÅĨ vÅĄetky chyby", "dismiss_error": "ZamietnuÅĨ chybu", @@ -753,26 +707,12 @@ "documentation": "DokumentÃĄcia", "done": "Hotovo", "download": "StiahnuÅĨ", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", "download_include_embedded_motion_videos": "VloÅženÊ videÃĄ", "download_include_embedded_motion_videos_description": "ZahrnÃēÅĨ videÃĄ vloÅženÊ do pohyblivÃŊch fotiek ako samostatnÊ sÃēbory", - "download_notfound": "Download not found", - "download_paused": "Download paused", "download_settings": "StiahnuÅĨ", "download_settings_description": "SpravovaÅĨ nastavenia sÃēvisiace so sÅĨahovaním poloÅžiek", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", "downloading": "SÅĨahuje sa", "downloading_asset_filename": "SÅĨahuje sa poloÅžka {filename}", - "downloading_media": "Downloading media", "drop_files_to_upload": "Hoď sÃēbory kdekoÄžvek, nahrajÃē sa", "duplicates": "DuplikÃĄty", "duplicates_description": "VysporiadaÅĨ sa s kaÅždou skupinou tak, Åže sa duplicitnÊ označia ako duplicitnÊ", @@ -796,25 +736,20 @@ "edit_title": "UpraviÅĨ nÃĄzov", "edit_user": "UpraviÅĨ pouŞívateÄža", "edited": "UpravenÊ", - "editor": "Editor", "editor_close_without_save_prompt": "Úpravy nebudÃē uloÅženÊ", "editor_close_without_save_title": "ZavrieÅĨ editor?", "editor_crop_tool_h2_aspect_ratios": "Pomer strÃĄn", "editor_crop_tool_h2_rotation": "Rotovanie", "email": "E-mail", - "empty_folder": "This folder is empty", "empty_trash": "VyprÃĄzdniÅĨ kÃ´ÅĄ", "empty_trash_confirmation": "Naozaj chcete vyprÃĄzdniÅĨ kÃ´ÅĄ? NenÃĄvratne sa vymaÅžÃē vÅĄetky poloÅžky z Immich.\nTÃĄto akcia sa nedÃĄ vrÃĄtiÅĨ!", "enable": "AktivovaÅĨ", "enabled": "AktivovanÃŊ", "end_date": "KoncovÃŊ dÃĄtum", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "Chyba", - "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Chyba pri odstraňovaní tvÃĄre z poloÅžky", "error_loading_image": "Nepodarilo sa načítaÅĨ obrÃĄzok", - "error_saving_image": "Error: {}", "error_title": "Chyba - niečo sa pokazilo", "errors": { "cannot_navigate_next_asset": "NedokÃĄÅžem prejsÅĨ na ďaÄžÅĄiu poloÅžku", @@ -942,16 +877,11 @@ "unable_to_update_user": "Nie je moÅžnÊ aktualizovaÅĨ pouŞívateÄža", "unable_to_upload_file": "Nie je moÅžnÊ nahraÅĨ sÃēbor" }, - "exif": "Exif", "exif_bottom_sheet_description": "PridaÅĨ popis...", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKALITA", "exif_bottom_sheet_people": "ÄŊUDIA", "exif_bottom_sheet_person_add_person": "PridaÅĨ meno", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "OpustiÅĨ Slideshow", "expand_all": "RozbaliÅĨ vÅĄetko", "experimental_settings_new_asset_list_subtitle": "PrebiehajÃēca prÃĄca", @@ -968,12 +898,9 @@ "extension": "RozÅĄÃ­renie", "external": "ExternÃŊ", "external_libraries": "ExternÊ kniÅžnice", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "NepriradenÃĄ", - "failed": "Failed", "failed_to_load_assets": "Nepodarilo sa načítaÅĨ poloÅžky", - "failed_to_load_folder": "Failed to load folder", "favorite": "ObÄžÃēbenÊ", "favorite_or_unfavorite_photo": "OznačiÅĨ fotku ako obÄžÃēbenÃē alebo neobÄžÃēbenÃē", "favorites": "ObÄžÃēbenÊ", @@ -985,23 +912,18 @@ "file_name_or_extension": "NÃĄzov alebo prípona sÃēboru", "filename": "Meno sÃēboru", "filetype": "Typ sÃēboru", - "filter": "Filter", "filter_people": "FiltrovaÅĨ Äžudí", "find_them_fast": "NÃĄjdite ich rÃŊchlejÅĄie podÄža mena", "fix_incorrect_match": "OpraviÅĨ nesprÃĄvnu zhodu", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "Priečinky", "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na sÃēborovom systÊme", "forward": "Dopredu", "general": "VÅĄeobecnÊ", "get_help": "ZískaÅĨ pomoc", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Začíname", "go_back": "VrÃĄtiÅĨ sa späÅĨ", "go_to_folder": "PrejsÅĨ do priečinka", "go_to_search": "PrejsÅĨ na vyhÄžadÃĄvanie", - "grant_permission": "Grant permission", "group_albums_by": "ZoskupiÅĨ albumy podÄža...", "group_country": "Zoskupenie podÄža krajiny", "group_no": "NezoskupovaÅĨ", @@ -1011,12 +933,6 @@ "haptic_feedback_switch": "PovoliÅĨ hmatovÃē odozvu", "haptic_feedback_title": "HmatovÃĄ odozva", "has_quota": "MÃĄ kvÃŗtu", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "Ahoj {name} ({email})", "hide_all_people": "SkryÅĨ vÅĄetky osoby", "hide_gallery": "SkryÅĨ galÊriu", @@ -1032,7 +948,6 @@ "home_page_archive_err_partner": "Na teraz nemôŞete premiestniÅĨ partnerove mÊdiÃĄ do archívu", "home_page_building_timeline": "VytvÃĄranie časovej osi", "home_page_delete_err_partner": "Na teraz nemôŞete odstrÃĄniÅĨ partnerove mÊdiÃĄ", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", "home_page_favorite_err_local": "ZatiaÄž nie je moÅžnÊ zaradiÅĨ lokÃĄlne mÊdia medzi obÄžÃēbenÊ, preskakuje sa", "home_page_favorite_err_partner": "Na teraz nemôŞete pridaÅĨ partnerove mÊdiÃĄ medzi obÄžÃēbenÊ", "home_page_first_time_notice": "Ak aplikÃĄciu pouŞívate prvÃŊ krÃĄt, nezabudnite si vybraÅĨ zÃĄlohovanÊ albumy, aby sa na časovej osi mohli nachÃĄdzaÅĨ fotografie a videÃĄ z vybranÃŊch albumoch.", @@ -1040,8 +955,6 @@ "home_page_upload_err_limit": "Naraz môŞete nahraÅĨ len 30 mÊdií, preskakujem...", "host": "HostiteÄž", "hour": "Hodina", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "ObrÃĄzok", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} nasnímanÊ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} nasnímanÊ s {person1} dňa {date}", @@ -1053,7 +966,6 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {ObrÃĄzok}} v {city}, {country} s {person1} a {person2} zo dňa {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {ObrÃĄzok}} zo dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {ObrÃĄzok}} nasnímanÃŊ v {city}, {country} s {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", - "image_saved_successfully": "Image saved", "image_viewer_page_state_provider_download_started": "SÅĨahovanie sa začalo", "image_viewer_page_state_provider_download_success": "SÅĨahovanie bolo ÃēspeÅĄnÊ", "image_viewer_page_state_provider_share_error": "Chyba zdieÄžania", @@ -1075,8 +987,6 @@ "night_at_midnight": "KaÅždÃŊ deň o polnoci", "night_at_twoam": "KaÅždÃē noc o 2:00" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "PozvaÅĨ Äžudí", "invite_to_album": "PozvaÅĨ do albumu", "items_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžiek}}", @@ -1094,7 +1004,6 @@ "leave": "OpustiÅĨ", "lens_model": "Model objektívu", "let_others_respond": "Nechajte ostatnÃŊch reagovaÅĨ", - "level": "Level", "library": "KniÅžnica", "library_options": "MoÅžnosti kniÅžnice", "library_page_device_albums": "Albumy v zariadení", @@ -1112,9 +1021,6 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie vÃŊsledkov hÄžadania sa nepodarilo", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", "location_picker_choose_on_map": "ZvoÄžte mapu", "location_picker_latitude_error": "Zadajte platnÃē zemepisnÃē dÄēÅžku", @@ -1164,8 +1070,8 @@ "manage_your_devices": "SpravovaÅĨ vaÅĄe prihlÃĄsenÊ zariadenia", "manage_your_oauth_connection": "SpravovaÅĨ vaÅĄe OAuth spojenia", "map": "Mapa", - "map_assets_in_bound": "{} fotka", - "map_assets_in_bounds": "{} fotiek", + "map_assets_in_bound": "{count} fotka", + "map_assets_in_bounds": "{count} fotiek", "map_cannot_get_user_location": "NemoÅžno získaÅĨ polohu pouŞívateÄža", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "PouÅžiÅĨ tÃēto polohu", @@ -1179,9 +1085,9 @@ "map_settings": "Nastavenia mÃĄp", "map_settings_dark_mode": "TmavÃŊ reÅžim", "map_settings_date_range_option_day": "PoslednÃŊch 24 hodín", - "map_settings_date_range_option_days": "Po {} dňoch", + "map_settings_date_range_option_days": "Po {days} dňoch", "map_settings_date_range_option_year": "UplynulÃŊ rok", - "map_settings_date_range_option_years": "Po {} rokoch", + "map_settings_date_range_option_years": "Po {years} rokoch", "map_settings_dialog_title": "Nastavenia mÃĄp", "map_settings_include_show_archived": "ZahrnÃēÅĨ archivovanÊ", "map_settings_include_show_partners": "ZahrnÃēÅĨ partnerov", @@ -1196,11 +1102,8 @@ "memories_setting_description": "Spravuje čo vidíte v spomienkach", "memories_start_over": "ZačaÅĨ odznova", "memories_swipe_to_close": "Zatvoríte posunom nahor", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "PamäÅĨ", "memory_lane_title": "PÃĄs spomienok {title}", - "menu": "Menu", "merge": "ZlÃēčiÅĨ", "merge_people": "ZlÃēčiÅĨ Äžudí", "merge_people_limit": "ZlÃēčiÅĨ môŞete naraz najviac 5 tvÃĄrí", @@ -1210,7 +1113,6 @@ "minimize": "MinimalizovaÅĨ", "minute": "MinÃēta", "missing": "ChÃŊbajÃēce", - "model": "Model", "month": "Mesiac", "monthly_title_text_date_format": "LLLL y", "more": "Viac", @@ -1221,8 +1123,6 @@ "my_albums": "Moje albumy", "name": "Meno", "name_or_nickname": "Meno alebo prezÃŊvka", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "nikdy", "new_album": "NovÃŊ album", "new_api_key": "NovÃŊ API kÄžÃēč", @@ -1251,7 +1151,6 @@ "no_results_description": "SkÃēste synonymum alebo vÅĄeobecnejÅĄÃ­ vÃŊraz", "no_shared_albums_message": "Vytvorí album na zdieÄžanie fotiek a videí s Äžuďmi vo vaÅĄej sieti", "not_in_any_album": "Nie je v Åžiadnom albume", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "PoznÃĄmka: Ak chcete pouÅžiÅĨ Å títok ÃēloÅžiska na predtÃŊm nahranÊ mÊdiÃĄ, spustite príkaz", "notes": "PoznÃĄmky", "notification_permission_dialog_content": "Ak chcete povoliÅĨ upozornenia, prejdite do Nastavenia a vyberte moÅžnosÅĨ PovoliÅĨ.", @@ -1261,20 +1160,16 @@ "notification_toggle_setting_description": "PovoliÅĨ e-mailovÊ upozornenia", "notifications": "OznÃĄmenia", "notifications_setting_description": "SpravovaÅĨ upozornenia", - "oauth": "OAuth", "official_immich_resources": "OficiÃĄlne Immich zdroje", - "offline": "Offline", "offline_paths": "Offline cesty", "offline_paths_description": "Tieto vÃŊsledky môŞu byÅĨ kvôli ručnÊmu vymazaniu sÃēborov ktorÊ nie sÃē sÃēčasÅĨou externej kniÅžnice.", "ok": "OK", "oldest_first": "NajstarÅĄie prvÊ", - "on_this_device": "On this device", "onboarding": "Na palube", "onboarding_privacy_description": "NasledujÃēce (voliteÄžnÊ) funkcie zÃĄvisia na externÃŊch sluÅžbÃĄch, a kedykoÄžvek ich môŞete vypnÃēÅĨ v admin nastaveniach.", "onboarding_theme_description": "Vyberte farbu tÊmy pre vÃĄÅĄ server. MôŞete to aj neskôr zmeniÅĨ vo vaÅĄich nastaveniach.", "onboarding_welcome_description": "Poďme nastaviÅĨ pre vÃĄÅĄ server niekoÄžko zÃĄkladnÃŊch nastavení.", "onboarding_welcome_user": "Vitaj, {user}", - "online": "Online", "only_favorites": "Len obÄžÃēbenÊ", "open_in_map_view": "OtvoriÅĨ v mape", "open_in_openstreetmap": "OtvoriÅĨ v OpenStreetMap", @@ -1288,7 +1183,6 @@ "other_variables": "OstatnÊ premennÊ", "owned": "VlastnenÊ", "owner": "Vlastník", - "partner": "Partner", "partner_can_access": "{partner} môŞe pristupovaÅĨ", "partner_can_access_assets": "VÅĄetky vaÅĄe fotky a videÃĄ, okrem ArchivovanÃŊch a OdstrÃĄnenÃŊch", "partner_can_access_location": "Miesto kde bola fotka spravenÃĄ", @@ -1299,7 +1193,7 @@ "partner_page_partner_add_failed": "PridÃĄvanie partnera zlyhalo", "partner_page_select_partner": "ZvoliÅĨ partnera", "partner_page_shared_to_title": "ZdieÄžanÊ pre", - "partner_page_stop_sharing_content": "{} uÅž nebude maÅĨ prístup ku vaÅĄim fotkÃĄm.", + "partner_page_stop_sharing_content": "{partner} uÅž nebude maÅĨ prístup ku vaÅĄim fotkÃĄm.", "partner_sharing": "ZdieÄžanie s partnerom", "partners": "Partneri", "password": "Heslo", @@ -1352,8 +1246,6 @@ "play_memories": "PrehraÅĨ spomienky", "play_motion_photo": "PrehraÅĨ pohyblivÃē fotku", "play_or_pause_video": "Pustí alebo pozastaví video", - "port": "Port", - "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "Preferencie", "preset": "Prednastavenie", "preview": "NÃĄhÄžad", @@ -1366,7 +1258,6 @@ "profile_drawer_client_out_of_date_major": "MobilnÃĄ aplikÃĄcia je zastaralÃĄ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_client_out_of_date_minor": "MobilnÃĄ aplikÃĄcia je zastaralÃĄ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_client_server_up_to_date": "Klient a server sÃē aktuÃĄlne", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Server je zastaralÃŊ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_server_out_of_date_minor": "Server je zastaralÃŊ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_image_of_user": "ProfilovÃŊ obrÃĄzok pouŞívateÄža {user}", @@ -1375,7 +1266,7 @@ "public_share": "VerejnÊ zdieÄžanie", "purchase_account_info": "PodporovateÄž", "purchase_activated_subtitle": "Ďakujeme za podporu Immich a softvÊru s otvorenÃŊmi zdrojÃĄkmi", - "purchase_activated_time": "AktivovanÊ {date, date}", + "purchase_activated_time": "AktivovanÊ {date}", "purchase_activated_title": "VÃĄÅĄ kÄžÃēč je ÃēspeÅĄne aktivovanÃŊ", "purchase_button_activate": "AktivovaÅĨ", "purchase_button_buy": "KÃēpiÅĨ", @@ -1403,7 +1294,6 @@ "purchase_remove_server_product_key_prompt": "Naozaj chcete odstrÃĄniÅĨ produktovÃŊ kÄžÃēč servera?", "purchase_server_description_1": "Pre celÃŊ server", "purchase_server_description_2": "Stav podporovateÄža", - "purchase_server_title": "Server", "purchase_settings_server_activated": "ProduktovÃŊ kÄžÃēč servera spravuje admin", "rating": "Hodnotenie hviezdičkami", "rating_clear": "VyčistiÅĨ hodnotenie", @@ -1418,7 +1308,6 @@ "recent": "NedÃĄvne", "recent-albums": "PoslednÊ albumy", "recent_searches": "PoslednÊ vyhÄžadÃĄvania", - "recently_added": "Recently added", "recently_added_page_title": "NedÃĄvno pridanÊ", "refresh": "ObnoviÅĨ", "refresh_encoded_videos": "ObnoviÅĨ enkÃŗdovanÊ videÃĄ", @@ -1473,10 +1362,8 @@ "retry_upload": "ZopakovaÅĨ nahrÃĄvanie", "review_duplicates": "PrezrieÅĨ duplikÃĄty", "role": "Rola", - "role_editor": "Editor", "role_viewer": "DivÃĄk", "save": "UloÅžiÅĨ", - "save_to_gallery": "Save to gallery", "saved_api_key": "UloÅženÃŊ API KÄžÃēč", "saved_profile": "UloÅženÃŊ profil", "saved_settings": "UloÅženÊ nastavenia", @@ -1498,32 +1385,17 @@ "search_city": "HÄžadaÅĨ mesto...", "search_country": "HÄžadaÅĨ krajinu...", "search_filter_apply": "PouÅžiÅĨ filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", "search_filter_display_option_not_in_album": "Mimo albumu", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", "search_for": "VyhÄžadaÅĨ", "search_for_existing_person": "HÄžadaÅĨ existujÃēcu osobu", - "search_no_more_result": "No more results", "search_no_people": "ÅŊiadne osoby", "search_no_people_named": "ÅŊiadne osoby menom \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_options": "MoÅžnosti hÄžadania", "search_page_categories": "KategÃŗrie", "search_page_motion_photos": "PohyblivÊ fotky", "search_page_no_objects": "ÅŊiadne informÃĄcie o objektoch", "search_page_no_places": "ÅŊiadne informÃĄcie o mieste", "search_page_screenshots": "Snímky obrazovky", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", "search_page_things": "Veci", "search_page_view_all_button": "ZobraziÅĨ vÅĄetky", "search_page_your_activity": "VaÅĄa aktivita", @@ -1561,7 +1433,6 @@ "selected_count": "{count, plural, other {# vybranÊ}}", "send_message": "OdoslaÅĨ sprÃĄvu", "send_welcome_email": "OdoslaÅĨ uvítací e-mail", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "Verzia aplikÃĄcie", "server_info_box_server_url": "URL Serveru", "server_offline": "Server je Offline", @@ -1582,28 +1453,25 @@ "setting_image_viewer_preview_title": "NačítaÅĨ nÃĄhÄžad obrÃĄzka", "setting_image_viewer_title": "ObrÃĄzky", "setting_languages_apply": "PouÅžiÅĨ", - "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "Jazyky", - "setting_notifications_notify_failures_grace_period": "OznÃĄmenie o zlyhaní zÃĄlohovania na pozadí: {}", - "setting_notifications_notify_hours": "{} hodín", + "setting_notifications_notify_failures_grace_period": "OznÃĄmenie o zlyhaní zÃĄlohovania na pozadí: {duration}", + "setting_notifications_notify_hours": "{count} hodín", "setting_notifications_notify_immediately": "okamÅžite", - "setting_notifications_notify_minutes": "{} minÃēt", + "setting_notifications_notify_minutes": "{count} minÃēt", "setting_notifications_notify_never": "nikdy", - "setting_notifications_notify_seconds": "{} sekÃēnd", + "setting_notifications_notify_seconds": "{count} sekÃēnd", "setting_notifications_single_progress_subtitle": "PodrobnÊ informÃĄcie o priebehu nahrÃĄvania pre poloÅžku", "setting_notifications_single_progress_title": "ZobraziÅĨ priebeh detailov zÃĄlohovania na pozadí", "setting_notifications_subtitle": "Prispôsobenie predvolieb oznÃĄmení", "setting_notifications_total_progress_subtitle": "CelkovÃŊ priebeh nahrÃĄvania (nahranÃŊch/celkovo)", "setting_notifications_total_progress_title": "ZobraziÅĨ celkovÃŊ priebeh zÃĄlohovania na pozadí", "setting_video_viewer_looping_title": "Opakovanie", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Nastavenia", "settings_require_restart": "Na pouÅžitie tohto nastavenia reÅĄtartujte Immich", "settings_saved": "Nastavenia boli uloÅženÊ", "share": "ZdieÄžaÅĨ", "share_add_photos": "PridaÅĨ fotografie", - "share_assets_selected": "{} označenÃŊch", + "share_assets_selected": "{count} označenÃŊch", "share_dialog_preparing": "Pripravujem...", "shared": "ZdieÄžanÊ", "shared_album_activities_input_disable": "KomentÃĄr je zakÃĄzanÃŊ", @@ -1617,40 +1485,37 @@ "shared_by_user": "ZdieÄža {user}", "shared_by_you": "ZdieÄžanÊ vami", "shared_from_partner": "Fotky od {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "ZdieÄžanÊ odkazy", "shared_link_clipboard_copied_massage": "SkopírovanÊ do schrÃĄnky", - "shared_link_clipboard_text": "Odkaz: {}\nHeslo: {}", + "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Vyskytla sa chyba behom vytvÃĄrania zdieÄžanÊho odkazu", "shared_link_edit_description_hint": "Zadajte popis zdieÄžania", "shared_link_edit_expire_after_option_day": "1 deň", - "shared_link_edit_expire_after_option_days": "{} dní", + "shared_link_edit_expire_after_option_days": "{count} dní", "shared_link_edit_expire_after_option_hour": "1 hodina", - "shared_link_edit_expire_after_option_hours": "{} hodín", + "shared_link_edit_expire_after_option_hours": "{count} hodín", "shared_link_edit_expire_after_option_minute": "1 minÃēta", - "shared_link_edit_expire_after_option_minutes": "{} minÃēt", - "shared_link_edit_expire_after_option_months": "{} mesiacov", - "shared_link_edit_expire_after_option_year": "{} roky", + "shared_link_edit_expire_after_option_minutes": "{count} minÃēt", + "shared_link_edit_expire_after_option_months": "{count} mesiacov", + "shared_link_edit_expire_after_option_year": "{count} roky", "shared_link_edit_password_hint": "Zadajte heslo zdieÄžania", "shared_link_edit_submit_button": "AktualizovaÅĨ odkaz", "shared_link_error_server_url_fetch": "NemoÅžno nÃĄjsÅĨ URL severa", - "shared_link_expires_day": "VyprÅĄÃ­ o {} dní", - "shared_link_expires_days": "VyprÅĄÃ­ o {} dní", - "shared_link_expires_hour": "VyprÅĄÃ­ o {} hodín", - "shared_link_expires_hours": "VyprÅĄÃ­ o {} hodín", - "shared_link_expires_minute": "VyprÅĄÃ­ o {} minÃēt", - "shared_link_expires_minutes": "VyprÅĄÃ­ o {} minÃēt", + "shared_link_expires_day": "VyprÅĄÃ­ o {count} dní", + "shared_link_expires_days": "VyprÅĄÃ­ o {count} dní", + "shared_link_expires_hour": "VyprÅĄÃ­ o {count} hodín", + "shared_link_expires_hours": "VyprÅĄÃ­ o {count} hodín", + "shared_link_expires_minute": "VyprÅĄÃ­ o {count} minÃēt", + "shared_link_expires_minutes": "VyprÅĄÃ­ o {count} minÃēt", "shared_link_expires_never": "NevyprÅĄÃ­", - "shared_link_expires_second": "VyprÅĄÃ­ o {} sekÃēnd", - "shared_link_expires_seconds": "VyprÅĄÃ­ o {} sekÃēnd", + "shared_link_expires_second": "VyprÅĄÃ­ o {count} sekÃēnd", + "shared_link_expires_seconds": "VyprÅĄÃ­ o {count} sekÃēnd", "shared_link_individual_shared": "IndividuÃĄlne zdieÄžanÊ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "SpravovaÅĨ zdieÄžanÊ odkazy", "shared_link_options": "MoÅžnosti zdieÄžanÃŊch odkazov", "shared_links": "ZdieÄžanÊ odkazy", "shared_links_description": "ZdieÄžanie fotografií a videí pomocou odkazu", "shared_photos_and_videos_count": "{assetCount, plural, other {# zdieÄžanÊ fotky a videÃĄ.}}", - "shared_with_me": "Shared with me", "shared_with_partner": "ZdieÄžanÊ s {partner}", "sharing": "ZdieÄžanie", "sharing_enter_password": "Ak chcete zobraziÅĨ tÃēto strÃĄnku, prosím, zadajte heslo.", @@ -1726,9 +1591,6 @@ "support_third_party_description": "VaÅĄa inÅĄtalÃĄcia Immich bola pripravenÃĄ treÅĨou stranou. ProblÊmy, ktorÊ sa vyskytli, môŞu byÅĨ spôsobenÊ tÃŊmto balíčkom, preto sa na nich obrÃĄÅĨte v prvom rade cez nasledujÃēce odkazy.", "swap_merge_direction": "VymeniÅĨ smer zlÃēčenia", "sync": "SynchronizovaÅĨ", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Značka", "tag_assets": "PridaÅĨ značku", "tag_created": "VytvorenÃĄ značka: {tag}", @@ -1743,14 +1605,9 @@ "theme_selection": "VÃŊber tÊmy", "theme_selection_description": "Automaticky nastaví tÊmu na svetlÃē alebo tmavÃē podÄža systÊmovÃŊch preferencií v prehliadači", "theme_setting_asset_list_storage_indicator_title": "ZobraziÅĨ indikÃĄtor ÃēloÅžiska na dlaÅždiciach poloÅžiek", - "theme_setting_asset_list_tiles_per_row_title": "Počet poloÅžiek na riadok ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Počet poloÅžiek na riadok ({count})", "theme_setting_image_viewer_quality_subtitle": "Prispôsobenie kvality prehliadača detailov", "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrÃĄzkov", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "Automaticky (podÄža systemovÊho nastavenia)", "theme_setting_theme_subtitle": "Vyberte nastavenia tÊmy aplikÃĄcie", "theme_setting_three_stage_loading_subtitle": "TrojstupňovÊ načítanie môŞe zvÃŊÅĄiÅĨ vÃŊkonnosÅĨ načítania, ale vedie k vÃŊrazne vyÅĄÅĄiemu zaÅĨaÅženiu siete.", @@ -1774,15 +1631,14 @@ "trash_all": "VÅĄetko do koÅĄa", "trash_count": "{count, number} do koÅĄa", "trash_delete_asset": "PoloÅžky do koÅĄa/odstrÃĄniÅĨ", - "trash_emptied": "Emptied trash", "trash_no_results_message": "VymazanÊ fotografie a videÃĄ sa zobrazia tu.", "trash_page_delete_all": "VymazaÅĨ vÅĄetky", "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprÃĄzdniÅĨ kÃ´ÅĄ? Tieto poloÅžky budÃē permanentne odstrÃĄnenÊ z Immichu", - "trash_page_info": "MÊdiÃĄ v koÅĄi sa permanentne odstrÃĄnia po {} dňoch", + "trash_page_info": "MÊdiÃĄ v koÅĄi sa permanentne odstrÃĄnia po {days} dňoch", "trash_page_no_assets": "ÅŊiadne mÊdiÃĄ v koÅĄi", "trash_page_restore_all": "ObnoviÅĨ vÅĄetky", "trash_page_select_assets_btn": "OznačiÅĨ mÊdiÃĄ", - "trash_page_title": "KÃ´ÅĄ ({})", + "trash_page_title": "KÃ´ÅĄ ({count})", "trashed_items_will_be_permanently_deleted_after": "PoloÅžky v koÅĄi sa natrvalo vymaÅžÃē po {days, plural, one {# dni} other {# dňoch}}.", "type": "Typ", "unarchive": "OdarchivovaÅĨ", @@ -1820,11 +1676,8 @@ "upload_status_errors": "Chyby", "upload_status_uploaded": "NahranÊ", "upload_success": "NahrÃĄvanie ÃēspeÅĄnÊ, pridanÊ sÃēbory sa zobrazia po obnovení strÃĄnky.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", "url": "Odkaz URL", "usage": "PouÅžitie", - "use_current_connection": "use current connection", "use_custom_date_range": "PouÅžite radÅĄej vlastnÃŊ rozsah dÃĄtumov", "user": "PouŞívateÄž", "user_id": "PouŞívateÄžskÊ ID", @@ -1839,7 +1692,6 @@ "users": "PouŞívatelia", "utilities": "NÃĄstroje", "validate": "ValidovaÅĨ", - "validate_endpoint_error": "Please enter a valid URL", "variables": "PremennÊ", "version": "Verzia", "version_announcement_closing": "Tvoj kamarÃĄt, Alex", @@ -1851,7 +1703,6 @@ "version_announcement_overlay_title": "K dispozícii je novÃĄ verzia servera 🎉", "version_history": "HistÃŗria verzií", "version_history_item": "InÅĄtalovanÃĄ {version} dňa {date}", - "video": "Video", "video_hover_setting": "PrehrÃĄvaÅĨ video nÃĄhÄžad pri nabehnutí myÅĄou", "video_hover_setting_description": "PrehrÃĄ video nÃĄhÄžad keď kurzor myÅĄi prejde cez poloÅžku. Aj keď je vypnutÊ, prehrÃĄvanie sa môŞe spustiÅĨ nabehnutí cez ikonu PrehraÅĨ.", "videos": "VideÃĄ", diff --git a/i18n/sl.json b/i18n/sl.json index 1839212037..db1e7aead9 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -25,7 +25,8 @@ "add_to": "Dodaj vâ€Ļ", "add_to_album": "Dodaj v album", "add_to_album_bottom_sheet_added": "Dodano v {album}", - "add_to_album_bottom_sheet_already_exists": "ÅŊe v {albumu}", + "add_to_album_bottom_sheet_already_exists": "ÅŊe v {album}", + "add_to_locked_folder": "Dodaj v zaklenjeno mapo", "add_to_shared_album": "Dodaj k deljenemu albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano v arhiv", @@ -39,20 +40,21 @@ "authentication_settings_disable_all": "Ali zares Åželite onemogočiti vse prijavne metode? Prijava bo popolnoma onemogočena.", "authentication_settings_reenable": "Ponovno omogoči z uporabo streÅžniÅĄkega ukaza.", "background_task_job": "Opravila v ozadju", - "backup_database": "Varnostna kopija baze", - "backup_database_enable_description": "Omogoči varnostno kopiranje baze", - "backup_keep_last_amount": "Å tevilo prejÅĄnjih obdrÅžanih varnostnih kopij", - "backup_settings": "Nastavitve varnostnega kopiranja", - "backup_settings_description": "Upravljanje nastavitev varnostnih kopij", + "backup_database": "Ustvari izpis baze podatkov", + "backup_database_enable_description": "Omogoči izpise baze podatkov", + "backup_keep_last_amount": "Å tevilo prejÅĄnjih odlagaliÅĄÄ, ki jih je treba obdrÅžati", + "backup_settings": "Nastavitve izpisa baze podatkov", + "backup_settings_description": "Upravljanje nastavitev izpisa baze podatkov. Opomba: Ta opravila se ne spremljajo in o neuspehu ne boste obveÅĄÄeni.", "check_all": "Označi vse", "cleanup": "ČiÅĄÄenje", "cleared_jobs": "RazčiÅĄÄeno opravilo za: {job}", "config_set_by_file": "Konfiguracija je trenutno nastavljena s konfiguracijsko datoteko", "confirm_delete_library": "Ali ste prepričani, da Åželite izbrisati knjiÅžnico {library}?", - "confirm_delete_library_assets": "Ali ste prepričani, da Åželite izbrisati to knjiÅžnico? To bo iz Immicha izbrisalo {count, plural, one {# contained asset} other {all # vsebovanih virov}} in tega ni moÅžno razveljaviti. Datoteke bodo ostale na disku.", + "confirm_delete_library_assets": "Ali ste prepričani, da Åželite izbrisati to knjiÅžnico? To bo iz Immicha izbrisalo {count, plural, one {# vsebovani vir} two {# vsebovana vira} few {# vsebovane vire} other {vseh # vsebovanih virov}} in tega ni moÅžno razveljaviti. Datoteke bodo ostale na disku.", "confirm_email_below": "Za potrditev vnesite \"{email}\" spodaj", "confirm_reprocess_all_faces": "Ali ste prepričani, da Åželite znova obdelati vse obraze? S tem boste počistili tudi Åže imenovane osebe.", "confirm_user_password_reset": "Ali ste prepričani, da Åželite ponastaviti geslo uporabnika {user}?", + "confirm_user_pin_code_reset": "Ali ste prepričani, da Åželite ponastaviti PIN kodo uporabnika {user}?", "create_job": "Ustvari opravilo", "cron_expression": "Nastavitveni izraz Cron", "cron_expression_description": "Nastavite interval skeniranja z uporabo zapisa cron. Za več informacij poglej npr. Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "Samodejna registracija", "oauth_auto_register_description": "Samodejna registracija novih uporabnikov po prijavi z OAuth", "oauth_button_text": "Besedilo gumba", - "oauth_client_id": "ID stranke", - "oauth_client_secret": "Skrivnost stranke", + "oauth_client_secret_description": "Zahtevano, če ponudnik OAuth ne podpira PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Prijava z OAuth", - "oauth_issuer_url": "URL izdajatelja", "oauth_mobile_redirect_uri": "Mobilni preusmeritveni URI", "oauth_mobile_redirect_uri_override": "Preglasitev URI preusmeritve za mobilne naprave", "oauth_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je '{callback}'", - "oauth_profile_signing_algorithm": "Algoritem za podpisovanje profila", - "oauth_profile_signing_algorithm_description": "Algoritem, ki se uporablja za podpisovanje uporabniÅĄkega profila.", - "oauth_scope": "Področje uporabe", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", - "oauth_signing_algorithm": "Algoritem podpisovanja", "oauth_storage_label_claim": "Zahtevek za nalepko za shranjevanje", "oauth_storage_label_claim_description": "Samodejno nastavi uporabnikovo oznako za shranjevanje na vrednost tega zahtevka.", "oauth_storage_quota_claim": "Zahtevek za kvoto prostora za shranjevanje", "oauth_storage_quota_claim_description": "Samodejno nastavi uporabnikovo kvoto shranjevanja na vrednost tega zahtevka.", "oauth_storage_quota_default": "Privzeta kvota za shranjevanje (GiB)", "oauth_storage_quota_default_description": "Kvota v GiB, ki se uporabi, ko ni predloÅžen noben zahtevek (vnesite 0 za neomejeno kvoto).", + "oauth_timeout": "Časovna omejitev zahteve", + "oauth_timeout_description": "Časovna omejitev za zahteve v milisekundah", "offline_paths": "Poti brez povezave", "offline_paths_description": "Ti rezultati so morda posledica ročnega brisanja datotek, ki niso del zunanje knjiÅžnice.", "password_enable_description": "Prijava z e-poÅĄto in geslom", @@ -296,7 +294,7 @@ "transcoding_bitrate_description": "Videoposnetki, ki presegajo največjo bitno hitrost ali niso v sprejemljivem formatu", "transcoding_codecs_learn_more": "Če Åželite izvedeti več o tukaj uporabljeni terminologiji, glejte dokumentacijo FFmpeg za kodek H.264, kodek HEVC in VP9 kodek.", "transcoding_constant_quality_mode": "Način stalne kakovosti", - "transcoding_constant_quality_mode_description": "ICQ je boljÅĄi od CQP, vendar nekatere naprave za pospeÅĄevanje strojne opreme ne podpirajo tega načina. Če nastavite to moÅžnost, bo pri uporabi kodiranja na podlagi kakovosti izbran izbran način. NVENC ga ignorira, ker ne podpira ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je boljÅĄi od CQP, vendar nekatere naprave za pospeÅĄevanje strojne opreme ne podpirajo tega načina. Če nastavite to moÅžnost, bo pri uporabi kodiranja na podlagi kakovosti izbran način. NVENC ga ignorira, ker ne podpira ICQ.", "transcoding_constant_rate_factor": "Faktor konstantne stopnje (-crf)", "transcoding_constant_rate_factor_description": "Raven kakovosti videa. Tipične vrednosti so 23 za H.264, 28 za HEVC, 31 za VP9 in 35 za AV1. NiÅžje je boljÅĄe, vendar ustvarja večje datoteke.", "transcoding_disabled_description": "Ne prekodirajte nobenih videoposnetkov, lahko prekine predvajanje na nekaterih odjemalcih", @@ -347,11 +345,12 @@ "untracked_files": "Nesledene datoteke", "untracked_files_description": "Tem datotekam aplikacija ne sledi. Lahko so posledica neuspelih premikov, prekinjenih nalaganj ali zaostalih zaradi hroÅĄÄa", "user_cleanup_job": "ČiÅĄÄenje uporabnika", - "user_delete_delay": "Račun in sredstva {user} bodo načrtovani za trajno brisanje čez {delay, plural, one {# day} other {# days}}.", + "user_delete_delay": "Račun in sredstva {user} bodo načrtovani za trajno brisanje čez {delay, plural, one {# dan} other {# dni}}.", "user_delete_delay_settings": "Zamakni izbris", "user_delete_delay_settings_description": "Å tevilo dni po odstranitvi za trajno brisanje uporabnikovega računa in sredstev. Opravilo za brisanje uporabnikov se izvaja ob polnoči, da se preveri, ali so uporabniki pripravljeni na izbris. Spremembe te nastavitve bodo ovrednotene pri naslednji izvedbi.", "user_delete_immediately": "Račun in sredstva uporabnika {user} bodo v čakalni vrsti za trajno brisanje takoj.", "user_delete_immediately_checkbox": "Uporabnika in sredstva postavite v čakalno vrsto za takojÅĄnje brisanje", + "user_details": "Podrobnosti o uporabniku", "user_management": "Upravljanje uporabnikov", "user_password_has_been_reset": "Geslo uporabnika je bilo ponastavljeno:", "user_password_reset_description": "Uporabniku posredujte začasno geslo in ga obvestite, da bo moral ob naslednji prijavi spremeniti geslo.", @@ -371,19 +370,23 @@ "admin_password": "SkrbniÅĄko geslo", "administration": "Administracija", "advanced": "Napredno", - "advanced_settings_log_level_title": "Nivo dnevnika: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "Uporabite to moÅžnost za filtriranje medijev med sinhronizacijo na podlagi alternativnih meril. To poskusite le, če imate teÅžave z aplikacijo, ki zaznava vse albume.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Uporabite alternativni filter za sinhronizacijo albuma v napravi", + "advanced_settings_log_level_title": "Nivo dnevnika: {level}", "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz sredstev v napravi. Aktivirajte to nastavitev, če Åželite namesto tega naloÅžiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", "advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich poÅĄlje ob vsaki mreÅžni zahtevi", "advanced_settings_proxy_headers_title": "Proxy glave", "advanced_settings_self_signed_ssl_subtitle": "Preskoči preverjanje potrdila SSL za končno točko streÅžnika. Zahtevano za samopodpisana potrdila.", "advanced_settings_self_signed_ssl_title": "Dovoli samopodpisana SSL potrdila", + "advanced_settings_sync_remote_deletions_subtitle": "Samodejno izbriÅĄi ali obnovi sredstvo v tej napravi, ko je to dejanje izvedeno v spletu", + "advanced_settings_sync_remote_deletions_title": "Sinhroniziraj oddaljene izbrise [EKSPERIMENTALNO]", "advanced_settings_tile_subtitle": "Napredne uporabniÅĄke nastavitve", "advanced_settings_troubleshooting_subtitle": "Omogočite dodatne funkcije za odpravljanje teÅžav", "advanced_settings_troubleshooting_title": "Odpravljanje teÅžav", - "age_months": "Starost {months, plural, one {# month} other {# months}}", - "age_year_months": "Starost 1 leto, {months, plural, one {# month} other {# months}}", - "age_years": "{years, plural, other {starost #}}", + "age_months": "Starost {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "age_year_months": "Starost 1 leto, {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}}", + "age_years": "{years, plural, other {Starost #}}", "album_added": "Album dodan", "album_added_notification_setting_description": "Prejmite e-poÅĄtno obvestilo, ko ste dodani v album v skupni rabi", "album_cover_updated": "Naslovnica albuma posodobljena", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Ali ste prepričani, da Åželite odstraniti {user}?", "album_share_no_users": "Videti je, da ste ta album dali v skupno rabo z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi ga lahko delili.", "album_thumbnail_card_item": "1 element", - "album_thumbnail_card_items": "{} elementov", - "album_thumbnail_card_shared": "¡ V skupni rabi", - "album_thumbnail_shared_by": "Delil {}", + "album_thumbnail_card_items": "{count} elementov", + "album_thumbnail_card_shared": " ¡ V skupni rabi", + "album_thumbnail_shared_by": "Delil {user}", "album_updated": "Album posodobljen", "album_updated_setting_description": "Prejmite e-poÅĄtno obvestilo, ko ima album v skupni rabi nova sredstva", "album_user_left": "Zapustil {album}", @@ -413,11 +416,11 @@ "album_viewer_appbar_share_err_remove": "Pri odstranjevanju sredstev iz albuma so teÅžave", "album_viewer_appbar_share_err_title": "Naslova albuma ni bilo mogoče spremeniti", "album_viewer_appbar_share_leave": "Zapusti album", - "album_viewer_appbar_share_to": "Deli z", + "album_viewer_appbar_share_to": "Deli s/z", "album_viewer_page_share_add_users": "Dodaj uporabnike", "album_with_link_access": "Omogočite vsem s povezavo ogled fotografij in ljudi v tem albumu.", "albums": "Albumi", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumi}}", + "albums_count": "{count, plural, one {{count, number} album} two {{count, number} albuma} few {{count, number} albumi} other {{count, number} albumov}}", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -427,7 +430,7 @@ "allow_public_user_to_download": "Dovoli javnemu uporabniku prenos", "allow_public_user_to_upload": "Dovolite javnemu uporabniku nalaganje", "alt_text_qr_code": "Slika QR kode", - "anti_clockwise": "V nasprotni smeri urnega kazalca", + "anti_clockwise": "V nasprotni smeri urinega kazalca", "api_key": "API ključ", "api_key_description": "Ta vrednost bo prikazana samo enkrat. Ne pozabite jo kopirati, preden zaprete okno.", "api_key_empty": "Ime ključa API ne sme biti prazno", @@ -440,14 +443,14 @@ "archive": "Arhiv", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", - "archive_page_title": "Arhiv ({})\n", + "archive_page_title": "Arhiv ({count})", "archive_size": "Velikost arhiva", "archive_size_description": "Konfigurirajte velikost arhiva za prenose (v GiB)", "archived": "Arhivirano", - "archived_count": "{count, plural, other {arhivirano #}}", + "archived_count": "{count, plural, one {# arhiviran} two {# arhivirana} few {# arhivirani} other {# arhiviranih}}", "are_these_the_same_person": "Ali je to ista oseba?", "are_you_sure_to_do_this": "Ste prepričani, da Åželite to narediti?", - "asset_action_delete_err_read_only": "Sredstev samo za branje ni mogoče izbrisati, preskočim\n", + "asset_action_delete_err_read_only": "Sredstev samo za branje ni mogoče izbrisati, preskočim", "asset_action_share_err_offline": "Ni mogoče pridobiti sredstev brez povezave, preskočim", "asset_added_to_album": "Dodano v album", "asset_adding_to_album": "Dodajanje v albumâ€Ļ", @@ -473,23 +476,23 @@ "asset_viewer_settings_subtitle": "Upravljaj nastavitve pregledovalnika galerije", "asset_viewer_settings_title": "Pregledovalnik sredstev", "assets": "Sredstva", - "assets_added_count": "Dodano{count, plural, one {# sredstvo} other {# sredstev}}", - "assets_added_to_album_count": "Dodano{count, plural, one {# sredstvo} other {# sredstev}} v album", - "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} other {# sredstev}} v {hasName, select, true {{name}} other {new album}}", - "assets_count": "{count, plural, one {# sredstvo} other {# sredstev}}", - "assets_deleted_permanently": "Å t. za vedno izbrisanih sredstev: {}", - "assets_deleted_permanently_from_server": "Å t. za vedno izbrisanih sredstev iz sreÅžnika Immich: {}", - "assets_moved_to_trash_count": "Premaknjeno {count, plural, one {# sredstev} other {# sredstev}} v smetnjak", - "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_removed_count": "Odstranjeno {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_removed_permanently_from_device": "Å t. za vedno izbrisanih sredstev iz vaÅĄe naprave: {}", + "assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album", + "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {hasName, select, true {{name}} other {new album}}", + "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_deleted_permanently": "trajno izrisana sredstva {count}", + "assets_deleted_permanently_from_server": "trajno izbrisana sredstva iz streÅžnika Immich {count}", + "assets_moved_to_trash_count": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v smetnjak", + "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_removed_count": "Odstranjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_removed_permanently_from_device": "trajno odstranjena sredstva iz naprave {count}", "assets_restore_confirmation": "Ali ste prepričani, da Åželite obnoviti vsa sredstva, ki ste jih odstranili? Tega dejanja ne morete razveljaviti! UpoÅĄtevajte, da sredstev brez povezave ni mogoče obnoviti na ta način.", - "assets_restored_count": "Obnovljeno {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_restored_successfully": "Å t. uspeÅĄno obnovljenih sredstev: {}", - "assets_trashed": "Å t. sredstev premaknjenih v smetnjak: {}", - "assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} other {# sredstev}}", - "assets_trashed_from_server": "Å t sredstev izbrisanih iz streÅžnika Immich: {}", - "assets_were_part_of_album_count": "{count, plural, one {sredstvo je} other {sredstev je}} Åže del albuma", + "assets_restored_count": "Obnovljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_restored_successfully": "uspeÅĄno obnovljena sredstva {count}", + "assets_trashed": "sredstva v smetnjaku {count}", + "assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "assets_trashed_from_server": "sredstva iz streÅžnika Immich v smetnjaku {count}", + "assets_were_part_of_album_count": "{count, plural, one {sredstvo je} two {sredstvi sta} few {sredstva so} other {sredstev je}} Åže del albuma", "authorized_devices": "PooblaÅĄÄene naprave", "automatic_endpoint_switching_subtitle": "PoveÅžite se lokalno prek določenega omreÅžja Wi-Fi, ko je na voljo, in uporabite druge povezave drugje", "automatic_endpoint_switching_title": "Samodejno preklapljanje URL-jev", @@ -497,7 +500,7 @@ "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", "background_location_permission_content": "Ko deluje v ozadju mora imeti Immich za zamenjavo omreÅžij, *vedno* dostop do natančne lokacije, da lahko aplikacija prebere ime omreÅžja Wi-Fi", - "backup_album_selection_page_albums_device": "Albumi v napravi ({})", + "backup_album_selection_page_albums_device": "Albumi v napravi ({count})", "backup_album_selection_page_albums_tap": "Tapnite za vključitev, dvakrat tapnite za izključitev", "backup_album_selection_page_assets_scatter": "Sredstva so lahko razprÅĄena po več albumih. Tako je mogoče med postopkom varnostnega kopiranja albume vključiti ali izključiti.", "backup_album_selection_page_select_albums": "Izberi albume", @@ -506,11 +509,11 @@ "backup_all": "Vse", "backup_background_service_backup_failed_message": "Varnostno kopiranje sredstev ni uspelo. Ponovno poskuÅĄamâ€Ļ", "backup_background_service_connection_failed_message": "Povezava s streÅžnikom ni uspela. Ponovno poskuÅĄamâ€Ļ", - "backup_background_service_current_upload_notification": "Nalagam {}", + "backup_background_service_current_upload_notification": "Nalagam {filename}", "backup_background_service_default_notification": "Preverjam za novimi sredstviâ€Ļ", "backup_background_service_error_title": "Napaka varnostnega kopiranja", - "backup_background_service_in_progress_notification": "Varnostno kopiranje vaÅĄih sredstev ...", - "backup_background_service_upload_failure_notification": "Nalaganje {} ni uspelo", + "backup_background_service_in_progress_notification": "Varnostno kopiranje vaÅĄih sredstevâ€Ļ", + "backup_background_service_upload_failure_notification": "Nalaganje {filename} ni uspelo", "backup_controller_page_albums": "Varnostno kopiranje albumov", "backup_controller_page_background_app_refresh_disabled_content": "Omogočite osveÅževanje aplikacij v ozadju v Nastavitve > SploÅĄno > OsveÅžitev aplikacij v ozadju, če Åželite uporabiti varnostno kopiranje v ozadju.", "backup_controller_page_background_app_refresh_disabled_title": "OsveÅževanje aplikacije v ozadju je onemogočeno", @@ -521,22 +524,21 @@ "backup_controller_page_background_battery_info_title": "Optimizacije baterije", "backup_controller_page_background_charging": "Samo med polnjenjem", "backup_controller_page_background_configure_error": "Storitve v ozadju ni bilo mogoče nastaviti", - "backup_controller_page_background_delay": "Zakasni varnostno kopiranje novih sredstev: {}", + "backup_controller_page_background_delay": "Zakasni varnostno kopiranje novih sredstev: {duration}", "backup_controller_page_background_description": "Vklopite storitev v ozadju za samodejno varnostno kopiranje novih sredstev, ne da bi morali odpreti aplikacijo", "backup_controller_page_background_is_off": "Samodejno varnostno kopiranje v ozadju je izklopljeno", "backup_controller_page_background_is_on": "Samodejno varnostno kopiranje v ozadju je vklopljeno", "backup_controller_page_background_turn_off": "Izklopi storitev v ozadju", "backup_controller_page_background_turn_on": "Vklopi storitev v ozadju", - "backup_controller_page_background_wifi": "Samo na WiFi", + "backup_controller_page_background_wifi": "Samo na Wi-Fi", "backup_controller_page_backup": "Varnostna kopija", - "backup_controller_page_backup_selected": "Izbrano", + "backup_controller_page_backup_selected": "Izbrano: ", "backup_controller_page_backup_sub": "Varnostno kopirane fotografije in videoposnetki", - "backup_controller_page_created": "Ustvarjeno: {}", + "backup_controller_page_created": "Ustvarjeno: {date}", "backup_controller_page_desc_backup": "Vklopite varnostno kopiranje v ospredju za samodejno nalaganje novih sredstev na streÅžnik, ko odprete aplikacijo.", - "backup_controller_page_excluded": "Izključeno:", - "backup_controller_page_failed": "NeuspeÅĄno ({})", - "backup_controller_page_filename": "Ime datoteke: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Izključeno: ", + "backup_controller_page_failed": "NeuspeÅĄno ({count})", + "backup_controller_page_filename": "Ime datoteke: {filename} [{size}]", "backup_controller_page_info": "Informacija o varnostnem kopiranju", "backup_controller_page_none_selected": "Noben izbran", "backup_controller_page_remainder": "Ostanek", @@ -545,7 +547,7 @@ "backup_controller_page_start_backup": "ZaÅženi varnostno kopiranje", "backup_controller_page_status_off": "Samodejno varnostno kopiranje v ospredju je izklopljeno", "backup_controller_page_status_on": "Samodejno varnostno kopiranje v ospredju je vklopljeno", - "backup_controller_page_storage_format": "Uporabljeno {} od {}", + "backup_controller_page_storage_format": "Uporabljeno {used} od {total}", "backup_controller_page_to_backup": "Albumi, ki bodo varnostno kopirani", "backup_controller_page_total_sub": "Vse edinstvene fotografije in videi iz izbranih albumov", "backup_controller_page_turn_off": "Izklopite varnostno kopiranje v ospredju", @@ -560,31 +562,35 @@ "backup_options_page_title": "MoÅžnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", "backward": "Nazaj", + "biometric_auth_enabled": "Biometrična avtentikacija omogočena", + "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", + "biometric_no_options": "Biometrične moÅžnosti niso na voljo", + "biometric_not_available": "Biometrično preverjanje pristnosti ni na voljo v tej napravi", "birthdate_saved": "Datum rojstva je uspeÅĄno shranjen", "birthdate_set_description": "Datum rojstva se uporablja za izračun starosti te osebe v času fotografije.", "blurred_background": "Zamegljeno ozadje", "bugs_and_feature_requests": "Napake in zahteve po funkcijah", "build": "Različica", "build_image": "Različica slike", - "bulk_delete_duplicates_confirmation": "Ali ste prepričani, da Åželite mnoÅžično izbrisati {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste ohranili največje sredstvo vsake skupine in trajno izbrisali vse druge dvojnike. Tega dejanja ne morete razveljaviti!", - "bulk_keep_duplicates_confirmation": "Ali ste prepričani, da Åželite obdrÅžati {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste razreÅĄili vse podvojene skupine, ne da bi karkoli izbrisali.", - "bulk_trash_duplicates_confirmation": "Ali ste prepričani, da Åželite mnoÅžično vreči v smetnjak {count, plural, one {# dvojnik} other {# dvojnikov}}? S tem boste obdrÅžali največje sredstvo vsake skupine in odstranili vse druge dvojnike.", + "bulk_delete_duplicates_confirmation": "Ali ste prepričani, da Åželite mnoÅžično izbrisati {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste ohranili največje sredstvo vsake skupine in trajno izbrisali vse druge dvojnike. Tega dejanja ne morete razveljaviti!", + "bulk_keep_duplicates_confirmation": "Ali ste prepričani, da Åželite obdrÅžati {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste razreÅĄili vse podvojene skupine, ne da bi karkoli izbrisali.", + "bulk_trash_duplicates_confirmation": "Ali ste prepričani, da Åželite mnoÅžično vreči v smetnjak {count, plural, one {# dvojnik} two {# dvojnika} few {# dvojnike} other {# dvojnikov}}? S tem boste obdrÅžali največje sredstvo vsake skupine in odstranili vse druge dvojnike.", "buy": "Kupi Immich", - "cache_settings_album_thumbnails": "Sličice strani knjiÅžnice ({} sredstev)", + "cache_settings_album_thumbnails": "Sličice strani knjiÅžnice ({count} sredstev)", "cache_settings_clear_cache_button": "Počisti predpomnilnik", "cache_settings_clear_cache_button_title": "Počisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POČISTI", "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na črni seznam", - "cache_settings_duplicated_assets_title": "Podvojena sredstva ({})", - "cache_settings_image_cache_size": "Velikost predpomnilnika slik ({} sredstev)", + "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", + "cache_settings_image_cache_size": "Velikost predpomnilnika slik ({count} sredstev)", "cache_settings_statistics_album": "Sličice knjiÅžnice", - "cache_settings_statistics_assets": "{} sredstva ({})", - "cache_settings_statistics_full": "Polne slike", + "cache_settings_statistics_assets": "{count} sredstva ({size})", + "cache_settings_statistics_full": "Izvirne slike", "cache_settings_statistics_shared": "Sličice albuma v skupni rabi", "cache_settings_statistics_thumbnail": "Sličice", "cache_settings_statistics_title": "Uporaba predpomnilnika", "cache_settings_subtitle": "Nadzirajte delovanje predpomnjenja mobilne aplikacije Immich", - "cache_settings_thumbnail_size": "Velikost predpomnilnika sličic ({} sredstev)", + "cache_settings_thumbnail_size": "Velikost predpomnilnika sličic ({count} sredstev)", "cache_settings_tile_subtitle": "Nadzoruj vedenje lokalnega shranjevanja", "cache_settings_tile_title": "Lokalna shramba", "cache_settings_title": "Nastavitve predpomnjenja", @@ -597,7 +603,9 @@ "cannot_merge_people": "Oseb ni mogoče zdruÅžiti", "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", + "cast": "Pretakaj", "change_date": "Spremeni datum", + "change_description": "Spremeni opis", "change_display_order": "Spremeni vrstni red prikaza", "change_expiration_time": "Spremeni čas poteka", "change_location": "Spremeni lokacijo", @@ -610,6 +618,7 @@ "change_password_form_new_password": "Novo geslo", "change_password_form_password_mismatch": "Gesli se ne ujemata", "change_password_form_reenter_new_password": "Znova vnesi novo geslo", + "change_pin_code": "Spremeni PIN kodo", "change_your_password": "Spremenite geslo", "changed_visibility_successfully": "UspeÅĄno spremenjena vidnost", "check_all": "Označite vse", @@ -650,19 +659,21 @@ "confirm_delete_face": "Ali ste prepričani, da Åželite izbrisati obraz osebe {name} iz sredstva?", "confirm_delete_shared_link": "Ali ste prepričani, da Åželite izbrisati to skupno povezavo?", "confirm_keep_this_delete_others": "Vsa druga sredstva v skladu bodo izbrisana, razen tega sredstva. Ste prepričani, da Åželite nadaljevati?", + "confirm_new_pin_code": "Potrdi novo PIN kodo", "confirm_password": "Potrdi geslo", + "connected_to": "Povezan s", "contain": "Vsebuje", "context": "Kontekst", "continue": "Nadaljuj", - "control_bottom_app_bar_album_info_shared": "{} elementov ¡ V skupni rabi", + "control_bottom_app_bar_album_info_shared": "{count} elementov ¡ V skupni rabi", "control_bottom_app_bar_create_new_album": "Ustvari nov album", "control_bottom_app_bar_delete_from_immich": "IzbriÅĄi iz Immicha", "control_bottom_app_bar_delete_from_local": "IzbriÅĄi iz naprave", "control_bottom_app_bar_edit_location": "Uredi lokacijo", - "control_bottom_app_bar_edit_time": "Uredi datum in uro", + "control_bottom_app_bar_edit_time": "Uredi datum & uro", "control_bottom_app_bar_share_link": "Deli povezavo", - "control_bottom_app_bar_share_to": "Deli z", - "control_bottom_app_bar_trash_from_immich": "Prestavi v smeti", + "control_bottom_app_bar_share_to": "Deli s/z", + "control_bottom_app_bar_trash_from_immich": "Prestavi v smetnjak", "copied_image_to_clipboard": "Slika kopirana v odloÅžiÅĄÄe.", "copied_to_clipboard": "Kopirano v odloÅžiÅĄÄe!", "copy_error": "Napaka pri kopiranju", @@ -686,32 +697,31 @@ "create_new_person": "Ustvari novo osebo", "create_new_person_hint": "Dodeli izbrana sredstva novi osebi", "create_new_user": "Ustvari novega uporabnika", - "create_shared_album_page_share_add_assets": "DODAJ SREDSTVO", + "create_shared_album_page_share_add_assets": "DODAJ SREDSTVA", "create_shared_album_page_share_select_photos": "Izberi fotografije", "create_tag": "Ustvari oznako", "create_tag_description": "Ustvarite novo oznako. Za ugnezdene oznake vnesite celotno pot oznake, vključno s poÅĄevnicami.", "create_user": "Ustvari uporabnika", "created": "Ustvarjeno", + "created_at": "Ustvarjeno", "crop": "Obrezovanje", "curated_object_page_title": "Stvari", "current_device": "Trenutna naprava", + "current_pin_code": "Trenutna PIN koda", "current_server_address": "Trenutni naslov streÅžnika", "custom_locale": "Jezik po meri", "custom_locale_description": "Oblikujte datume in ÅĄtevilke glede na jezik in regijo", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", "date_after": "Datum po", "date_and_time": "Datum in ura", "date_before": "Datum pred", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Datum rojstva je uspeÅĄno shranjen", "date_range": "Časovno obdobje", "day": "Dan", "deduplicate_all": "Odstrani vse podvojene", "deduplication_criteria_1": "Velikost slike v bajtih", "deduplication_criteria_2": "Å tevilo podatkov EXIF", - "deduplication_info": "Informacije o deduplikaciji", + "deduplication_info": "Informacije o zaznavanju dvojnikov", "deduplication_info_description": "Za samodejno vnaprejÅĄnjo izbiro sredstev in mnoÅžično odstranjevanje dvojnikov si ogledamo:", "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in ÅĄtevilke glede na lokalne nastavitve brskalnika", @@ -742,11 +752,10 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", - "details": "PODROBNOSTI", + "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "Onemogočeno", "disallow_edits": "Onemogoči urejanje", - "discord": "Discord", "discover": "Odkrij", "dismiss_all_errors": "Opusti vse napake", "dismiss_error": "Opusti napako", @@ -763,7 +772,7 @@ "download_enqueue": "Prenos v čakalni vrsti", "download_error": "Napaka pri prenosu", "download_failed": "Prenos ni uspel", - "download_filename": "datoteka: {}", + "download_filename": "datoteka: {filename}", "download_finished": "Prenos zaključen", "download_include_embedded_motion_videos": "Vdelani videoposnetki", "download_include_embedded_motion_videos_description": "Videoposnetke, vdelane v fotografije gibanja, vključite kot ločeno datoteko", @@ -787,6 +796,8 @@ "edit_avatar": "Uredi avatar", "edit_date": "Uredi datum", "edit_date_and_time": "Uredi datum in uro", + "edit_description": "Uredi opis", + "edit_description_prompt": "Izberite nov opis:", "edit_exclusion_pattern": "Uredi vzorec izključitve", "edit_faces": "Uredi obraze", "edit_import_path": "Uredi uvozno pot", @@ -807,19 +818,23 @@ "editor_crop_tool_h2_aspect_ratios": "Razmerja stranic", "editor_crop_tool_h2_rotation": "Vrtenje", "email": "E-poÅĄta", + "email_notifications": "Obvestila po e-poÅĄti", "empty_folder": "Ta mapa je prazna", - "empty_trash": "Izprazni smeti", + "empty_trash": "Izprazni smetnjak", "empty_trash_confirmation": "Ste prepričani, da Åželite izprazniti smetnjak? S tem boste iz Immicha trajno odstranili vsa sredstva v smetnjaku.\nTega dejanja ne morete razveljaviti!", "enable": "Omogoči", + "enable_biometric_auth_description": "Vnesite svojo PIN kodo, da omogočite biometrično preverjanje pristnosti", "enabled": "Omogočeno", "end_date": "Končni datum", "enqueued": "V čakalni vrsti", - "enter_wifi_name": "Vnesi WiFi ime", + "enter_wifi_name": "Vnesi Wi-Fi ime", + "enter_your_pin_code": "Vnesite svojo PIN kodo", + "enter_your_pin_code_subtitle": "Vnesite svojo PIN kodo za dostop do zaklenjene mape", "error": "Napaka", "error_change_sort_album": "Vrstnega reda albuma ni bilo mogoče spremeniti", "error_delete_face": "Napaka pri brisanju obraza iz sredstva", "error_loading_image": "Napaka pri nalaganju slike", - "error_saving_image": "Napaka: {}", + "error_saving_image": "Napaka: {error}", "error_title": "Napaka - nekaj je ÅĄlo narobe", "errors": { "cannot_navigate_next_asset": "Ni mogoče krmariti do naslednjega sredstva", @@ -849,13 +864,15 @@ "failed_to_keep_this_delete_others": "Tega sredstva ni bilo mogoče obdrÅžati in izbrisati ostalih sredstev", "failed_to_load_asset": "Sredstva ni bilo mogoče naloÅžiti", "failed_to_load_assets": "Sredstev ni bilo mogoče naloÅžiti", + "failed_to_load_notifications": "Nalaganje obvestil ni uspelo", "failed_to_load_people": "Oseb ni bilo mogoče naloÅžiti", "failed_to_remove_product_key": "Ključa izdelka ni bilo mogoče odstraniti", "failed_to_stack_assets": "Zlaganje sredstev ni uspelo", "failed_to_unstack_assets": "Sredstev ni bilo mogoče razloÅžiti", + "failed_to_update_notification_status": "Stanja obvestila ni bilo mogoče posodobiti", "import_path_already_exists": "Ta uvozna pot Åže obstaja.", "incorrect_email_or_password": "Napačen e-poÅĄtni naslov ali geslo", - "paths_validation_failed": "{paths, plural, one {# pot} other {# poti}} ni bilo uspeÅĄno preverjeno", + "paths_validation_failed": "{paths, plural, one {# pot ni bila uspeÅĄno preverjena} two {# poti nista bili uspeÅĄno preverjeni} few {# poti niso bile uspeÅĄno preverjene} other {# poti ni bilo uspeÅĄno preverjenih}}", "profile_picture_transparent_pixels": "Profilne slike ne smejo imeti prosojnih slikovnih pik. Povečajte in/ali premaknite sliko.", "quota_higher_than_disk_size": "Nastavili ste kvoto, ki je viÅĄja od velikosti diska", "repair_unable_to_check_items": "Ni mogoče preveriti {count, select, one {predmeta} other {predmetov}}", @@ -870,10 +887,11 @@ "unable_to_archive_unarchive": "Ni mogoče {archived, select, true {arhivirano} other {nearhivirano}}", "unable_to_change_album_user_role": "Ni mogoče spremeniti vloge uporabnika albuma", "unable_to_change_date": "Datuma ni mogoče spremeniti", + "unable_to_change_description": "Opisa ni mogoče spremeniti", "unable_to_change_favorite": "Ni mogoče spremeniti priljubljenega za sredstvo", "unable_to_change_location": "Lokacije ni mogoče spremeniti", "unable_to_change_password": "Gesla ni mogoče spremeniti", - "unable_to_change_visibility": "Ni mogoče spremeniti vidnosti za {count, plural, one {# osebo} other {# oseb}}", + "unable_to_change_visibility": "Ni mogoče spremeniti vidnosti za {count, plural, one {# osebo} two {# osebi} few {# osebe} other {# oseb}}", "unable_to_complete_oauth_login": "Prijave OAuth ni mogoče dokončati", "unable_to_connect": "Ni mogoče vzpostaviti povezave", "unable_to_connect_to_server": "Ni mogoče vzpostaviti povezave s streÅžnikom", @@ -907,6 +925,7 @@ "unable_to_log_out_all_devices": "Ni mogoče odjaviti vseh naprav", "unable_to_log_out_device": "Naprave ni mogoče odjaviti", "unable_to_login_with_oauth": "Prijava z OAuth ni mogoča", + "unable_to_move_to_locked_folder": "Ni mogoče premakniti v zaklenjeno mapo", "unable_to_play_video": "Videoposnetka ni mogoče predvajati", "unable_to_reassign_assets_existing_person": "Ni mogoče dodeliti sredstev {name, select, null {obstoječi osebi} other {{name}}}", "unable_to_reassign_assets_new_person": "Ponovna dodelitev sredstev novi osebi ni moÅžna", @@ -920,6 +939,7 @@ "unable_to_remove_reaction": "Reakcije ni mogoče odstraniti", "unable_to_repair_items": "Elementov ni mogoče popraviti", "unable_to_reset_password": "Gesla ni mogoče ponastaviti", + "unable_to_reset_pin_code": "PIN kode ni mogoče ponastaviti", "unable_to_resolve_duplicate": "Dvojnika ni mogoče razreÅĄiti", "unable_to_restore_assets": "Sredstev ni mogoče obnoviti", "unable_to_restore_trash": "Smetnjaka ni mogoče obnoviti", @@ -947,16 +967,15 @@ "unable_to_update_user": "Uporabnika ni mogoče posodobiti", "unable_to_upload_file": "Datoteke ni mogoče naloÅžiti" }, - "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis..", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", "exif_bottom_sheet_person_add_person": "Dodaj ime", - "exif_bottom_sheet_person_age": "Starost {}", - "exif_bottom_sheet_person_age_months": "Starost {} mesecev", - "exif_bottom_sheet_person_age_year_months": "Starost 1 leto, {} mesecev", - "exif_bottom_sheet_person_age_years": "Starost {}", + "exif_bottom_sheet_person_age": "Starost {age}", + "exif_bottom_sheet_person_age_months": "Staros {months} mesecev", + "exif_bottom_sheet_person_age_year_months": "Starost 1 leto, {months} mesecev", + "exif_bottom_sheet_person_age_years": "Starost {years}", "exit_slideshow": "Zapustite diaprojekcijo", "expand_all": "RazÅĄiri vse", "experimental_settings_new_asset_list_subtitle": "Delo v teku", @@ -974,9 +993,10 @@ "external": "Zunanji", "external_libraries": "Zunanje knjiÅžnice", "external_network": "Zunanje omreÅžje", - "external_network_sheet_info": "Ko aplikacija ni v Åželenem omreÅžju WiFi, se bo povezala s streÅžnikom prek prvega od spodnjih URL-jev, ki jih lahko doseÅže, začenÅĄi od zgoraj navzdol", + "external_network_sheet_info": "Ko aplikacija ni v Åželenem omreÅžju Wi-Fi, se bo povezala s streÅžnikom prek prvega od spodnjih URL-jev, ki jih lahko doseÅže, začenÅĄi od zgoraj navzdol", "face_unassigned": "Nedodeljen", "failed": "Ni uspelo", + "failed_to_authenticate": "Preverjanje pristnosti ni uspelo", "failed_to_load_assets": "Sredstev ni bilo mogoče naloÅžiti", "failed_to_load_folder": "Mape ni bilo mogoče naloÅžiti", "favorite": "Priljubljen", @@ -990,8 +1010,8 @@ "file_name_or_extension": "Ime ali končnica datoteke", "filename": "Ime datoteke", "filetype": "Vrsta datoteke", - "filter": "Filter", "filter_people": "Filtriraj ljudi", + "filter_places": "Filtriraj kraje", "find_them_fast": "Z iskanjem jih hitro poiÅĄÄite po imenu", "fix_incorrect_match": "Popravi napačno ujemanje", "folder": "Mapa", @@ -1040,7 +1060,9 @@ "home_page_delete_remote_err_local": "Lokalna sredstva pri brisanju oddaljenega izbora, preskakujem", "home_page_favorite_err_local": "Lokalnih sredstev ÅĄe ni mogoče dodati med priljubljene, preskakujem", "home_page_favorite_err_partner": "Sredstev partnerja ÅĄe ni mogoče dodati med priljubljene, preskakujem", - "home_page_first_time_notice": "Če aplikacijo uporabljate prvič, se prepričajte, da ste izbrali rezervne albume, tako da lahko časovna premica zapolni fotografije in videoposnetke v albumih.", + "home_page_first_time_notice": "Če aplikacijo uporabljate prvič, se prepričajte, da ste izbrali rezervne albume, tako da lahko časovna premica zapolni fotografije in videoposnetke v albumih", + "home_page_locked_error_local": "Lokalnih sredstev ni mogoče premakniti v zaklenjeno mapo, preskakovanje", + "home_page_locked_error_partner": "Sredstev partnerjev ni mogoče premakniti v zaklenjeno mapo, preskakovanje", "home_page_share_err_local": "Lokalnih sredstev ni mogoče deliti prek povezave, preskakujem", "home_page_upload_err_limit": "Hkrati lahko naloÅžite največ 30 sredstev, preskakujem", "host": "Gostitelj", @@ -1066,17 +1088,16 @@ "immich_web_interface": "Immich spletni vmesnik", "import_from_json": "Uvoz iz JSON", "import_path": "Pot uvoza", - "in_albums": "V {count, plural, one {# album} other {# albumov}}", + "in_albums": "V {count, plural, one {# album} two {# albuma} few {# albume} other {# albumov}}", "in_archive": "V arhiv", - "include_archived": "Vključi arhivirane", + "include_archived": "Vključi arhivirano", "include_shared_albums": "Vključite skupne albume", "include_shared_partner_assets": "Vključite partnerjeva skupna sredstva", "individual_share": "Samostojna delitev", "individual_shares": "Posamezne delitve", - "info": "Info", "interval": { "day_at_onepm": "Vsak dan ob 13h", - "hours": "Vsakih {hours, plural, one {uro} other {{hours, number} ur/e}}", + "hours": "Vsakih {hours, plural, one {uro} two {uri} few {ure} other {{hours, number} ur}}", "night_at_midnight": "Vsak večer ob polnoči", "night_at_twoam": "Vsako noč ob 2h" }, @@ -1084,12 +1105,12 @@ "invalid_date_format": "Neveljavna oblika datuma", "invite_people": "Povabi ljudi", "invite_to_album": "Povabi v album", - "items_count": "{count, plural, one {# predmet} other {# predmetov}}", + "items_count": "{count, plural, one {# predmet} two {# predmeta} few {# predmeti} other {# predmetov}}", "jobs": "Opravila", "keep": "ObdrÅži", "keep_all": "ObdrÅži vse", "keep_this_delete_others": "ObdrÅži to, izbriÅĄi ostalo", - "kept_this_deleted_others": "ObdrÅži to sredstvo in izbriÅĄi {count, plural, one {# sredstvo} other {# sredstev}}", + "kept_this_deleted_others": "ObdrÅži to sredstvo in izbriÅĄi {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "keyboard_shortcuts": "BliÅžnjice na tipkovnici", "language": "Jezik", "language_setting_description": "Izberite Åželeni jezik", @@ -1120,12 +1141,14 @@ "local_network": "Lokalno omreÅžje", "local_network_sheet_info": "Aplikacija se bo povezala s streÅžnikom prek tega URL-ja, ko bo uporabljala navedeno omreÅžje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", - "location_permission_content": "Za uporabo funkcije samodejnega preklapljanja potrebuje Immich dovoljenje za natančno lokacijo, da lahko prebere ime trenutnega omreÅžja WiFi", + "location_permission_content": "Za uporabo funkcije samodejnega preklapljanja potrebuje Immich dovoljenje za natančno lokacijo, da lahko prebere ime trenutnega omreÅžja Wi-Fi", "location_picker_choose_on_map": "Izberi na zemljevidu", "location_picker_latitude_error": "Vnesi veljavno zemljepisno ÅĄirino", "location_picker_latitude_hint": "Tukaj vnesi svojo zemljepisno ÅĄirino", "location_picker_longitude_error": "Vnesi veljavno zemljepisno dolÅžino", "location_picker_longitude_hint": "Tukaj vnesi svojo zemljepisno dolÅžino", + "lock": "Zaklepanje", + "locked_folder": "Zaklenjena mapa", "log_out": "Odjava", "log_out_all_devices": "Odjava vseh naprav", "logged_out_all_devices": "Odjavljene so vse naprave", @@ -1170,8 +1193,8 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{} slika", - "map_assets_in_bounds": "{} slik", + "map_assets_in_bound": "{count} slika", + "map_assets_in_bounds": "{count} slik", "map_cannot_get_user_location": "Lokacije uporabnika ni mogoče dobiti", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Uporabi to lokacijo", @@ -1185,15 +1208,18 @@ "map_settings": "Nastavitve zemljevida", "map_settings_dark_mode": "Temni način", "map_settings_date_range_option_day": "Zadnjih 24 ur", - "map_settings_date_range_option_days": "Zadnjih {} dni", + "map_settings_date_range_option_days": "Zadnjih {days} dni", "map_settings_date_range_option_year": "Zadnje leto", - "map_settings_date_range_option_years": "Zadnjih {} let", + "map_settings_date_range_option_years": "Zadnjih {years} let", "map_settings_dialog_title": "Nastavitve zemljevida", "map_settings_include_show_archived": "Vključi arhivirane", "map_settings_include_show_partners": "Vključi partnerjeve", "map_settings_only_show_favorites": "PokaÅži samo priljubljene", "map_settings_theme_settings": "Tema zemljevida", "map_zoom_to_see_photos": "PomanjÅĄajte za ogled fotografij", + "mark_all_as_read": "Označi vse kot prebrano", + "mark_as_read": "Označi kot prebrano", + "marked_all_as_read": "Označeno vse kot prebrano", "matches": "Ujemanja", "media_type": "Vrsta medija", "memories": "Spomini", @@ -1202,8 +1228,6 @@ "memories_setting_description": "Upravljajte s tem, kar vidite v svojih spominih", "memories_start_over": "Začni od začetka", "memories_swipe_to_close": "Podrsaj gor za zapiranje", - "memories_year_ago": "Leto dni nazaj", - "memories_years_ago": "{} let nazaj", "memory": "Spomin", "memory_lane_title": "Spominski trak {title}", "menu": "Meni", @@ -1216,10 +1240,14 @@ "minimize": "ZmanjÅĄaj", "minute": "minuta", "missing": "manjka", - "model": "Model", "month": "Mesec", - "monthly_title_text_date_format": "MMMM y", "more": "Več", + "move": "Premakni", + "move_off_locked_folder": "Premakni iz zaklenjene mape", + "move_to_locked_folder": "Premakni v zaklenjeno mapo", + "move_to_locked_folder_confirmation": "Te fotografije in videoposnetki bodo odstranjeni iz vseh albumov in si jih bo mogoče ogledati le v zaklenjeni mapi", + "moved_to_archive": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v arhiv", + "moved_to_library": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v knjiÅžnico", "moved_to_trash": "Premaknjeno v smetnjak", "multiselect_grid_edit_date_time_err_read_only": "Ni mogoče urediti datuma sredstev samo za branje, preskočim", "multiselect_grid_edit_gps_err_read_only": "Ni mogoče urediti lokacije sredstev samo za branje, preskočim", @@ -1234,6 +1262,8 @@ "new_api_key": "Nov API ključ", "new_password": "Novo geslo", "new_person": "Nova oseba", + "new_pin_code": "Nova PIN koda", + "new_pin_code_subtitle": "To je vaÅĄ prvi dostop do zaklenjene mape. Ustvarite PIN kodo za varen dostop do te strani", "new_user_created": "Nov uporabnik ustvarjen", "new_version_available": "NA VOLJO JE NOVA RAZLIČICA", "newest_first": "Najprej najnovejÅĄe", @@ -1251,7 +1281,10 @@ "no_explore_results_message": "NaloÅžite več fotografij, da raziÅĄÄete svojo zbirko.", "no_favorites_message": "Dodajte priljubljene, da hitreje najdete svoje najboljÅĄe slike in videoposnetke", "no_libraries_message": "Ustvarite zunanjo knjiÅžnico za ogled svojih fotografij in videoposnetkov", + "no_locked_photos_message": "Fotografije in videoposnetki v zaklenjeni mapi so skriti in se ne bodo prikazali med brskanjem po knjiÅžnici.", "no_name": "Brez imena", + "no_notifications": "Ni obvestil", + "no_people_found": "Ni najdenih ustreznih oseb", "no_places": "Ni krajev", "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj sploÅĄno ključno besedo", @@ -1260,6 +1293,7 @@ "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če Åželite oznako za shranjevanje uporabiti za predhodno naloÅžena sredstva, zaÅženite", "notes": "Opombe", + "nothing_here_yet": "Tukaj ÅĄe ni ničesar", "notification_permission_dialog_content": "Če Åželite omogočiti obvestila, pojdite v Nastavitve in izberite Dovoli.", "notification_permission_list_tile_content": "Izdaj dovoljenje za omogočanje obvestil.", "notification_permission_list_tile_enable_button": "Omogoči obvestila", @@ -1267,7 +1301,6 @@ "notification_toggle_setting_description": "Omogoči e-poÅĄtna obvestila", "notifications": "Obvestila", "notifications_setting_description": "Upravljanje obvestil", - "oauth": "OAuth", "official_immich_resources": "Immich uradni viri", "offline": "Brez povezave", "offline_paths": "Poti brez povezave", @@ -1282,6 +1315,7 @@ "onboarding_welcome_user": "Pozdravljen/a, {user}", "online": "Povezano", "only_favorites": "Samo priljubljene", + "open": "Odpri", "open_in_map_view": "Odpri v pogledu zemljevida", "open_in_openstreetmap": "Odpri v OpenStreetMap", "open_the_search_filters": "Odpri iskalne filtre", @@ -1294,18 +1328,17 @@ "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", - "partner": "Partner", "partner_can_access": "{partner} ima dostop", "partner_can_access_assets": "Vse vaÅĄe fotografije in videoposnetki, razen tistih v arhivu in izbrisanih", "partner_can_access_location": "Lokacija, kjer so bile vaÅĄe fotografije posnete", - "partner_list_user_photos": "{user}ovih fotografij", + "partner_list_user_photos": "fotografije od {user}", "partner_list_view_all": "Poglej vse", "partner_page_empty_message": "VaÅĄe fotografije ÅĄe niso v skupni rabi z nobenim partnerjem.", "partner_page_no_more_users": "Ni več uporabnikov za dodajanje", "partner_page_partner_add_failed": "Partnerja ni bilo mogoče dodati", "partner_page_select_partner": "Izberi partnerja", "partner_page_shared_to_title": "V skupni rabi z", - "partner_page_stop_sharing_content": "{} ne bo imel več dostopa do vaÅĄih fotografij.", + "partner_page_stop_sharing_content": "{partner} ne bo imel več dostopa do vaÅĄih fotografij.", "partner_sharing": "Skupna raba s partnerjem", "partners": "Partnerji", "password": "Geslo", @@ -1313,9 +1346,9 @@ "password_required": "Zahtevano je geslo", "password_reset_success": "Ponastavitev gesla je uspela", "past_durations": { - "days": "Pretek-el/-lih {days, plural, one {dan} other {# dni}}", - "hours": "Pretek-lo/-lih {hours, plural, one {uro} other {# ur}}", - "years": "Pretek-lo/-lih {years, plural, one {leto} other {# let}}" + "days": "{days, plural, one {Pretekel dan} two {Pretekla # dni} few {Pretekle # dni} other {Preteklih # dni}}", + "hours": "{hours, plural, one {Preteklo uro} two {Pretekli # uri} few {Pretekle # ure} other {Preteklih # ur}}", + "years": "Zadnjih {years, plural, one {leto} other {# let}}" }, "path": "Pot", "pattern": "Vzorec", @@ -1324,13 +1357,13 @@ "paused": "Zaustavljeno", "pending": "V teku", "people": "Osebe", - "people_edits_count": "Urejen-a/-ih {count, plural, one {# oseba} other {# oseb}}", + "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrÅĄÄenih po osebah", "people_sidebar_description": "PrikaÅžite povezavo do Ljudje v stranski vrstici", "permanent_deletion_warning": "Opozorilo o trajnem izbrisu", "permanent_deletion_warning_setting_description": "PokaÅži opozorilo pri trajnem brisanju sredstev", "permanently_delete": "Trajno izbriÅĄi", - "permanently_delete_assets_count": "Trajno izbriÅĄi {count, plural, one {sredstvo} other {sredstev}}", + "permanently_delete_assets_count": "Trajno izbriÅĄi {count, plural, one {sredstvo} two {sredstvi} few {sredstva} other {sredstev}}", "permanently_delete_assets_prompt": "Ali ste prepričani, da Åželite trajno izbrisati {count, plural, one {to sredstvo?} other {ta # sredstva?}} S tem boste odstranili tudi {count, plural, one {tega od teh} other {telih iz telih}} album- /-ov.", "permanently_deleted_asset": "Trajno izbrisano sredstvo", "permanently_deleted_assets_count": "Trajno izbrisano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", @@ -1348,16 +1381,21 @@ "photo_shared_all_users": "Videti je, da ste svoje fotografije delili z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi jih delili.", "photos": "Slike", "photos_and_videos": "Fotografije & videi", - "photos_count": "{count, plural, one {{count, number} slika} other {{count, number} slik}}", + "photos_count": "{count, plural, one {{count, number} slika} two {{count, number} sliki} few {{count, number} slike} other {{count, number} slik}}", "photos_from_previous_years": "Fotografije iz prejÅĄnjih let", "pick_a_location": "Izberi lokacijo", + "pin_code_changed_successfully": "PIN koda je bila uspeÅĄno spremenjena", + "pin_code_reset_successfully": "PIN koda je bila uspeÅĄno ponastavljena", + "pin_code_setup_successfully": "UspeÅĄno nastavljena PIN koda", + "pin_verification": "Preverjanje PIN kode", "place": "Lokacija", "places": "Lokacije", - "places_count": "{count, plural, one {{count, number} kraj} other {{count, number} krajev}}", + "places_count": "{count, plural, one {{count, number} kraj} two {{count, number} kraja} few {{count, number} kraji} other {{count, number} krajev}}", "play": "Predvajaj", "play_memories": "Predvajaj spomine", "play_motion_photo": "Predvajaj premikajočo fotografijo", "play_or_pause_video": "Predvajaj ali zaustavi video", + "please_auth_to_access": "Za dostop se prijavite", "port": "Vrata", "preferences_settings_subtitle": "Upravljaj nastavitve aplikacije", "preferences_settings_title": "Nastavitve", @@ -1368,11 +1406,11 @@ "previous_or_next_photo": "PrejÅĄnja ali naslednja fotografija", "primary": "Primarni", "privacy": "Zasebnost", + "profile": "Profil", "profile_drawer_app_logs": "Dnevniki", "profile_drawer_client_out_of_date_major": "Mobilna aplikacija je zastarela. Posodobite na najnovejÅĄo glavno različico.", "profile_drawer_client_out_of_date_minor": "Mobilna aplikacija je zastarela. Posodobite na najnovejÅĄo manjÅĄo različico.", "profile_drawer_client_server_up_to_date": "Odjemalec in streÅžnik sta posodobljena", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "StreÅžnik je zastarel. Posodobite na najnovejÅĄo glavno različico.", "profile_drawer_server_out_of_date_minor": "StreÅžnik je zastarel. Posodobite na najnovejÅĄo manjÅĄo različico.", "profile_image_of_user": "Profilna slika uporabnika {user}", @@ -1381,7 +1419,7 @@ "public_share": "Javno deljenje", "purchase_account_info": "Podpornik", "purchase_activated_subtitle": "Hvala, ker podpirate Immich in odprtokodno programsko opremo", - "purchase_activated_time": "Aktivirano {date, date}", + "purchase_activated_time": "Aktivirano {date}", "purchase_activated_title": "VaÅĄ ključ je bil uspeÅĄno aktiviran", "purchase_button_activate": "Aktiviraj", "purchase_button_buy": "Kupi", @@ -1418,7 +1456,7 @@ "reaction_options": "MoÅžnosti reakcije", "read_changelog": "Preberi dnevnik sprememb", "reassign": "Prerazporedi", - "reassigned_assets_to_existing_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} other {# sredstev}} za {name, select, null {an existing person} other {{name}}}", + "reassigned_assets_to_existing_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za {name, select, null {an existing person} other {{name}}}", "reassigned_assets_to_new_person": "Ponovno dodeljeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} za novo osebo", "reassing_hint": "Dodeli izbrana sredstva obstoječi osebi", "recent": "Nedavno", @@ -1426,6 +1464,8 @@ "recent_searches": "Nedavna iskanja", "recently_added": "Nedavno dodano", "recently_added_page_title": "Nedavno dodano", + "recently_taken": "Nedavno posneto", + "recently_taken_page_title": "Nedavno Uporabljen", "refresh": "OsveÅži", "refresh_encoded_videos": "OsveÅži kodirane videoposnetke", "refresh_faces": "OsveÅži obraze", @@ -1445,6 +1485,8 @@ "remove_deleted_assets": "Odstrani izbrisana sredstva", "remove_from_album": "Odstrani iz albuma", "remove_from_favorites": "Odstrani iz priljubljenih", + "remove_from_locked_folder": "Odstrani iz zaklenjene mape", + "remove_from_locked_folder_confirmation": "Ali ste prepričani, da Åželite premakniti te fotografije in videoposnetke iz zaklenjene mape? Vidni bodo v vaÅĄi knjiÅžnici", "remove_from_shared_link": "Odstrani iz skupne povezave", "remove_memory": "Odstrani spomin", "remove_photo_from_memory": "Odstrani fotografijo iz tega spomina", @@ -1468,6 +1510,7 @@ "reset": "Ponastavi", "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", + "reset_pin_code": "Ponastavi PIN kodo", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "RazreÅĄi dvojnike", "resolved_all_duplicates": "RazreÅĄeni vsi dvojniki", @@ -1540,7 +1583,7 @@ "search_result_page_new_search_hint": "Novo iskanje", "search_settings": "Nastavitve iskanja", "search_state": "Iskanje deÅžele...", - "search_suggestion_list_smart_search_hint_1": "Pametno iskanje je privzeto omogočeno, za iskanje metapodatkov uporabite sintakso", + "search_suggestion_list_smart_search_hint_1": "Pametno iskanje je privzeto omogočeno, za iskanje metapodatkov uporabite sintakso ", "search_suggestion_list_smart_search_hint_2": "m:vaÅĄ-iskani-pojem", "search_tags": "Iskanje oznak...", "search_timezone": "Iskanje časovnega pasu...", @@ -1560,6 +1603,7 @@ "select_keep_all": "Izberi obdrÅži vse", "select_library_owner": "Izberi lastnika knjiÅžnice", "select_new_face": "Izberi nov obraz", + "select_person_to_tag": "Izberite osebo, ki jo Åželite označiti", "select_photos": "Izberi fotografije", "select_trash_all": "Izberi vse v smetnjak", "select_user_for_sharing_page_err_album": "Albuma ni bilo mogoče ustvariti", @@ -1590,12 +1634,12 @@ "setting_languages_apply": "Uporabi", "setting_languages_subtitle": "Spremeni jezik aplikacije", "setting_languages_title": "Jeziki", - "setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {}", - "setting_notifications_notify_hours": "{} ur", + "setting_notifications_notify_failures_grace_period": "Obvesti o napakah varnostnega kopiranja v ozadju: {duration}", + "setting_notifications_notify_hours": "{count} ur", "setting_notifications_notify_immediately": "takoj", - "setting_notifications_notify_minutes": "{} minut", + "setting_notifications_notify_minutes": "{count} minut", "setting_notifications_notify_never": "nikoli", - "setting_notifications_notify_seconds": "{} sekund", + "setting_notifications_notify_seconds": "{count} sekund", "setting_notifications_single_progress_subtitle": "Podrobne informacije o napredku nalaganja po sredstvih", "setting_notifications_single_progress_title": "PokaÅži napredek varnostnega kopiranja v ozadju", "setting_notifications_subtitle": "Prilagodite svoje nastavitve obvestil", @@ -1607,10 +1651,12 @@ "settings": "Nastavitve", "settings_require_restart": "Znova zaÅženite Immich, da uporabite to nastavitev", "settings_saved": "Nastavitve shranjene", + "setup_pin_code": "Nastavi PIN kodo", "share": "Deli", "share_add_photos": "Dodaj fotografije", - "share_assets_selected": "{} izbrano", + "share_assets_selected": "{count} izbrano", "share_dialog_preparing": "Priprava...", + "share_link": "Deli povezavo", "shared": "V skupni rabi", "shared_album_activities_input_disable": "Komentiranje je onemogočeno", "shared_album_activity_remove_content": "Ali Åželite izbrisati to dejavnost?", @@ -1618,39 +1664,38 @@ "shared_album_section_people_action_error": "Napaka pri zapuÅĄÄanju/odstranjevanju iz albuma", "shared_album_section_people_action_leave": "Odstrani uporabnika iz albuma", "shared_album_section_people_action_remove_user": "Odstrani uporabnika iz albuma", - "shared_album_section_people_title": "LJUDJE", + "shared_album_section_people_title": "OSEBE", "shared_by": "Skupna raba s/z", "shared_by_user": "Skupna raba s/z {user}", "shared_by_you": "DeliÅĄ", "shared_from_partner": "Fotografije od {partner}", - "shared_intent_upload_button_progress_text": "{} / {} naloÅženo", + "shared_intent_upload_button_progress_text": "{current} / {total} naloÅženo", "shared_link_app_bar_title": "Povezave v skupni rabi", "shared_link_clipboard_copied_massage": "Kopirano v odloÅžiÅĄÄe", - "shared_link_clipboard_text": "Povezava: {}\nGeslo: {}", + "shared_link_clipboard_text": "Povezava: {link}\nGeslo: {password}", "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", "shared_link_edit_description_hint": "Vnesi opis skupne rabe", "shared_link_edit_expire_after_option_day": "1 dan", - "shared_link_edit_expire_after_option_days": "{} dni", + "shared_link_edit_expire_after_option_days": "{count} dni", "shared_link_edit_expire_after_option_hour": "1 ura", - "shared_link_edit_expire_after_option_hours": "{} ur", + "shared_link_edit_expire_after_option_hours": "{count} ur", "shared_link_edit_expire_after_option_minute": "1 minuta", - "shared_link_edit_expire_after_option_minutes": "{} minut", - "shared_link_edit_expire_after_option_months": "{} mesecev", - "shared_link_edit_expire_after_option_year": "{} let", + "shared_link_edit_expire_after_option_minutes": "{count} minut", + "shared_link_edit_expire_after_option_months": "{count} mesecev", + "shared_link_edit_expire_after_option_year": "{count} let", "shared_link_edit_password_hint": "Vnesi geslo za skupno rabo", "shared_link_edit_submit_button": "Posodobi povezavo", "shared_link_error_server_url_fetch": "URL-ja streÅžnika ni mogoče pridobiti", - "shared_link_expires_day": "Poteče čez {} dan", - "shared_link_expires_days": "Poteče čez {} dni", - "shared_link_expires_hour": "Poteče čez {} uro", - "shared_link_expires_hours": "Poteče čez {} ur", - "shared_link_expires_minute": "Poteče čez {} minuto\n", - "shared_link_expires_minutes": "Poteče čez {} minut", + "shared_link_expires_day": "Poteče čez {count} dan", + "shared_link_expires_days": "Poteče čez {count} dni", + "shared_link_expires_hour": "Poteče čez {count} uro", + "shared_link_expires_hours": "Poteče čez {count} ur", + "shared_link_expires_minute": "Poteče čez {count} minuto", + "shared_link_expires_minutes": "Poteče čez {count} minut", "shared_link_expires_never": "Poteče ∞", - "shared_link_expires_second": "Poteče čez {} sekundo", - "shared_link_expires_seconds": "Poteče čez {} sekund", + "shared_link_expires_second": "Poteče čez {count} sekundo", + "shared_link_expires_seconds": "Poteče čez {count} sekund", "shared_link_individual_shared": "Individualno deljeno", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", "shared_link_options": "MoÅžnosti skupne povezave", "shared_links": "Povezave v skupni rabi", @@ -1716,13 +1761,13 @@ "start": "Začetek", "start_date": "Datum začetka", "state": "DeÅžela", - "status": "Status", "stop_motion_photo": "Zaustavi gibljivo fotografijo", "stop_photo_sharing": "ÅŊelite prenehati deliti svoje fotografije?", "stop_photo_sharing_description": "{partner} ne bo mogel več dostopati do vaÅĄih fotografij.", "stop_sharing_photos_with_user": "Prenehaj deliti svoje fotografije s tem uporabnikom", "storage": "Prostor za shranjevanje", "storage_label": "Oznaka za shranjevanje", + "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "PredloÅži", "suggestions": "Predlogi", @@ -1749,7 +1794,7 @@ "theme_selection": "Izbira teme", "theme_selection_description": "Samodejno nastavi temo na svetlo ali temno glede na sistemske nastavitve brskalnika", "theme_setting_asset_list_storage_indicator_title": "PokaÅži indikator shrambe na ploÅĄÄicah sredstev", - "theme_setting_asset_list_tiles_per_row_title": "Å tevilo sredstev na vrstico ({})", + "theme_setting_asset_list_tiles_per_row_title": "Å tevilo sredstev na vrstico ({count})", "theme_setting_colorful_interface_subtitle": "Nanesi primarno barvo na povrÅĄine ozadja.", "theme_setting_colorful_interface_title": "Barvit vmesnik", "theme_setting_image_viewer_quality_subtitle": "Prilagodite kakovost podrobnega pregledovalnika slik", @@ -1784,13 +1829,15 @@ "trash_no_results_message": "Fotografije in videoposnetki, ki so v smetnjaku, bodo prikazani tukaj.", "trash_page_delete_all": "IzbriÅĄi vse", "trash_page_empty_trash_dialog_content": "Ali Åželite izprazniti svoja sredstva v smeti? Ti elementi bodo trajno odstranjeni iz Immicha", - "trash_page_info": "Elementi v smeteh bodo trajno izbrisani po {} dneh", + "trash_page_info": "Elementi v smeteh bodo trajno izbrisani po {days} dneh", "trash_page_no_assets": "Ni sredstev v smeteh", "trash_page_restore_all": "Obnovi vse", "trash_page_select_assets_btn": "Izberite sredstva", - "trash_page_title": "Smetnjak ({})", + "trash_page_title": "Smetnjak ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementi v smetnjaku bodo trajno izbrisani po {days, plural, one {# dnevu} two {# dnevih} few {# dnevih} other {# dneh}}.", "type": "Vrsta", + "unable_to_change_pin_code": "PIN kode ni mogoče spremeniti", + "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", "unfavorite": "Odznači priljubljeno", @@ -1814,6 +1861,7 @@ "untracked_files": "Nesledene datoteke", "untracked_files_decription": "Tem datotekam aplikacija ne sledi. Lahko so posledica neuspelih premikov, prekinjenih ali zaostalih nalaganj zaradi hroÅĄÄa", "up_next": "Naslednja", + "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "NaloÅži", "upload_concurrency": "Sočasnost nalaganja", @@ -1826,15 +1874,18 @@ "upload_status_errors": "Napake", "upload_status_uploaded": "NaloÅženo", "upload_success": "Nalaganje je uspelo, osveÅžite stran, da vidite nova sredstva za nalaganje.", - "upload_to_immich": "NaloÅži v Immich ({})", + "upload_to_immich": "NaloÅži v Immich ({count})", "uploading": "Nalagam", - "url": "URL", "usage": "Uporaba", + "use_biometric": "Uporabite biometrične podatke", "use_current_connection": "uporabi trenutno povezavo", "use_custom_date_range": "Namesto tega uporabite časovno obdobje po meri", "user": "Uporabnik", + "user_has_been_deleted": "Ta uporabnik je bil izbrisan.", "user_id": "ID uporabnika", "user_liked": "{user} je vÅĄeč {type, select, photo {ta fotografija} video {ta video} asset {to sredstvo} other {to}}", + "user_pin_code_settings": "PIN koda", + "user_pin_code_settings_description": "Upravljaj svojo PIN kodo", "user_purchase_settings": "Nakup", "user_purchase_settings_description": "Upravljajte svoj nakup", "user_role_set": "Nastavi {user} kot {role}", @@ -1852,12 +1903,11 @@ "version_announcement_message": "Pozdravljeni! Na voljo je nova različica Immich. Vzemite si nekaj časa in preberite opombe ob izdaji, da zagotovite, da so vaÅĄe nastavitve posodobljene, da preprečite morebitne napačne konfiguracije, zlasti če uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄ primerek Immich.", "version_announcement_overlay_release_notes": "opombe ob izdaji", "version_announcement_overlay_text_1": "ÅŊivjo prijatelj, na voljo je nova izdaja", - "version_announcement_overlay_text_2": "vzemi si čas in obiÅĄÄi", - "version_announcement_overlay_text_3": "in zagotovite, da sta vaÅĄa nastavitev docker-compose in .env posodobljena, da preprečite morebitne napačne konfiguracije, zlasti če uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄo streÅžniÅĄko aplikacijo.", + "version_announcement_overlay_text_2": "vzemi si čas in obiÅĄÄi ", + "version_announcement_overlay_text_3": " in zagotovite, da sta vaÅĄa nastavitev docker-compose in .env posodobljena, da preprečite morebitne napačne konfiguracije, zlasti če uporabljate WatchTower ali kateri koli mehanizem, ki samodejno posodablja vaÅĄo streÅžniÅĄko aplikacijo.", "version_announcement_overlay_title": "Na voljo je nova različica streÅžnika 🎉", "version_history": "Zgodovina različic", "version_history_item": "{version} nameÅĄÄena {date}", - "video": "Video", "video_hover_setting": "Predvajaj sličico videoposnetka ob lebdenju", "video_hover_setting_description": "Predvajaj sličico videoposnetka, ko se miÅĄka pomakne nad element. Tudi ko je onemogočeno, lahko predvajanje začnete tako, da miÅĄkin kazalec premaknete nad ikono za predvajanje.", "videos": "Videoposnetki", @@ -1883,11 +1933,12 @@ "week": "Teden", "welcome": "DobrodoÅĄli", "welcome_to_immich": "DobrodoÅĄli v Immich", - "wifi_name": "WiFi ime", + "wifi_name": "Wi-Fi ime", + "wrong_pin_code": "Napačna PIN koda", "year": "Leto", "years_ago": "{years, plural, one {# leto} two {# leti} few {# leta} other {# let}} nazaj", "yes": "Da", "you_dont_have_any_shared_links": "Nimate nobenih skupnih povezav", - "your_wifi_name": "VaÅĄe ime WiFi", + "your_wifi_name": "VaÅĄe ime Wi-Fi", "zoom_image": "Povečava slike" } diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index c8aa6a9f27..872418b548 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -1,21 +1,21 @@ { - "about": "О АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ¸", - "account": "ĐŸŅ€ĐžŅ„Đ¸Đģ", + "about": "О аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ¸", + "account": "НаĐģĐžĐŗ", "account_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° Са ĐŸŅ€ĐžŅ„Đ¸Đģ", "acknowledge": "ĐŸĐžŅ‚Đ˛Ņ€Đ´Đ¸", "action": "ĐŸĐžŅŅ‚ŅƒĐŋаĐē", - "action_common_update": "Update", + "action_common_update": "ĐŖĐŋdate", "actions": "ĐŸĐžŅŅ‚ŅƒĐŋŅ†Đ¸", "active": "АĐēŅ‚Đ¸Đ˛ĐŊи", "activity": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚", - "activity_changed": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ ҘĐĩ {enabled, select, true {ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊа} other {oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊа}}", + "activity_changed": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ ҘĐĩ {enabled, select, true {ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа} other {oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа}}", "add": "Đ”ĐžĐ´Đ°Ņ˜", "add_a_description": "Đ”ĐžĐ´Đ°Ņ˜ ĐžĐŋĐ¸Ņ", "add_a_location": "Đ”ĐžĐ´Đ°Ņ˜ ЛоĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", "add_a_name": "Đ”ĐžĐ´Đ°Ņ˜ иĐŧĐĩ", "add_a_title": "Đ”ĐžĐ´Đ°Ņ˜ ĐŊĐ°ŅĐģОв", - "add_endpoint": "Add endpoint", - "add_exclusion_pattern": "Đ”ĐžĐ´Đ°Ņ˜ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", + "add_endpoint": "Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ ĐēŅ€Đ°Ņ˜ŅšŅƒ Ņ‚Đ°Ņ‡Đē҃", + "add_exclusion_pattern": "Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", "add_import_path": "Đ”ĐžĐ´Đ°Ņ˜ ĐŋŅƒŅ‚Đ°ŅšŅƒ Са ĐŋŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ", "add_location": "Đ”ĐžĐ´Đ°Ņ˜ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", "add_more_users": "Đ”ĐžĐ´Đ°Ņ˜ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ", @@ -24,156 +24,157 @@ "add_photos": "Đ”ĐžĐ´Đ°Ņ˜ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", "add_to": "Đ”ĐžĐ´Đ°Ņ˜ ҃â€Ļ", "add_to_album": "Đ”ĐžĐ´Đ°Ņ˜ ҃ аĐģĐąŅƒĐŧ", - "add_to_album_bottom_sheet_added": "Added to {album}", - "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_added": "Đ”ĐžĐ´Đ°Ņ‚Đž ҃ {album}", + "add_to_album_bottom_sheet_already_exists": "ВĐĩŅ› ҃ {album}", "add_to_shared_album": "Đ”ĐžĐ´Đ°Ņ˜ ҃ Đ´ĐĩŅ™ĐĩĐŊ аĐģĐąŅƒĐŧ", "add_url": "Đ”ĐžĐ´Đ°Ņ˜ URL", "added_to_archive": "Đ”ĐžĐ´Đ°Ņ‚Đž ҃ Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ", "added_to_favorites": "Đ”ĐžĐ´Đ°Ņ‚Đž ҃ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ", "added_to_favorites_count": "Đ”ĐžĐ´Đ°Ņ‚Đž {count, number} ҃ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ", "admin": { - "add_exclusion_pattern_description": "Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ ĐžĐąŅ€Đ°ŅŅ†Đĩ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩŅšĐ°. ĐšĐžŅ€Đ¸ŅˆŅ‚ĐĩҚĐĩ *, ** и ? ҘĐĩ ĐŋĐžĐ´Ņ€ĐļаĐŊĐž. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ йиĐģĐž ĐēĐžĐŧ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧ҃ ĐŋОд ĐŊаСивОĐŧ „Рав“, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „**/Рав/**“. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ҁĐĩ ĐˇĐ°Đ˛Ņ€ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐŊа „.Ņ‚Đ¸Ņ„â€œ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „**/*.Ņ‚Đ¸Ņ„â€œ. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи аĐŋŅĐžĐģŅƒŅ‚ĐŊ҃ ĐŋŅƒŅ‚Đ°ŅšŅƒ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „/path/to/ignore/**“.", - "asset_offline_description": "Ово ĐĩĐēҁ҂ĐĩŅ€ĐŊĐž йийĐģĐ¸ĐžŅ‚Đĩ҇ĐēĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž ҁĐĩ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐŊаĐģаСи ĐŊа Đ´Đ¸ŅĐē҃ и ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž ҘĐĩ ҃ ҁĐŧĐĩŅ›Đĩ. АĐēĐž ҘĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊа ҃ĐŊŅƒŅ‚Đ°Ņ€ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐē҃ ĐģиĐŊĐ¸Ņ˜Ņƒ Са ĐŊОвО ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ›Đĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đž. Да ĐąĐ¸ŅŅ‚Đĩ Đ˛Ņ€Đ°Ņ‚Đ¸Đģи ОвО ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ҁĐĩ да ИĐŧĐŧĐ¸Ņ†Ņ… ĐŧĐžĐļĐĩ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи Đ´ĐžĐģĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐžŅ˜ ĐŋŅƒŅ‚Đ°ŅšĐ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃.", + "add_exclusion_pattern_description": "Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ ĐžĐąŅ€Đ°ŅŅ†Đĩ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩŅšĐ°. ĐšĐžŅ€Đ¸ŅˆŅ‚ĐĩҚĐĩ *, ** и ? ҘĐĩ ĐŋĐžĐ´Ņ€ĐļаĐŊĐž. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ йиĐģĐž ĐēĐžĐŧ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧ҃ ĐŋОд ĐŊаСивОĐŧ „Рав“, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „**/Рав/**“. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ҁĐĩ ĐˇĐ°Đ˛Ņ€ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐŊа „.Ņ‚Đ¸Ņ„â€œ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „**/*.Ņ‚Đ¸Ņ„â€œ. Да ĐąĐ¸ŅŅ‚Đĩ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°Đģи аĐŋŅĐžĐģŅƒŅ‚ĐŊ҃ ĐŋŅƒŅ‚Đ°ŅšŅƒ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ „/ĐŋĐ°Ņ‚Ņ…/Ņ‚Đž/Đ¸ĐŗĐŊĐžŅ€Đĩ/**“.", + "asset_offline_description": "Ово ĐĩĐēҁ҂ĐĩŅ€ĐŊĐž йийĐģĐ¸ĐžŅ‚Đĩ҇ĐēĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž ҁĐĩ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐŊаĐģаСи ĐŊа Đ´Đ¸ŅĐē҃ и ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž ҘĐĩ ҃ ҁĐŧĐĩ҆ˁĐĩ. АĐēĐž ҘĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊа ҃ĐŊŅƒŅ‚Đ°Ņ€ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐē҃ ĐģиĐŊĐ¸Ņ˜Ņƒ Са ĐŊОвО ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ†ĖĐĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đž. Да ĐąĐ¸ŅŅ‚Đĩ Đ˛Ņ€Đ°Ņ‚Đ¸Đģи ОвО ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ҁĐĩ да Immich ĐŧĐžĐļĐĩ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи Đ´ĐžĐģĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐžŅ˜ ĐŋŅƒŅ‚Đ°ŅšĐ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃.", "authentication_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° Са Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", - "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐģОСиĐŊĐēĐžĐŧ, OAuth-om и Đ´Ņ€ŅƒĐŗĐ¸Đŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", - "authentication_settings_disable_all": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да oneĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ŅĐ˛Đĩ ĐŧĐĩŅ‚ĐžĐ´Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ°? ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ° Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅ‚Đŋ҃ĐŊĐž oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊа.", - "authentication_settings_reenable": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐŊОвО ĐžĐŧĐžĐŗŅƒŅ›Đ¸Đģи, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐēĐžĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", + "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐģОСиĐŊĐēĐžĐŧ, OAuth-ĐžĐŧ и Đ´Ņ€ŅƒĐŗĐ¸Đŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "authentication_settings_disable_all": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да oneĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ŅĐ˛Đĩ ĐŧĐĩŅ‚ĐžĐ´Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ°? ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅ‚Đŋ҃ĐŊĐž oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа.", + "authentication_settings_reenable": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐŊОвО ĐžĐŧĐžĐŗŅƒŅ†ĖĐ¸Đģи, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐēĐžĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "background_task_job": "ПозадиĐŊҁĐēи ĐˇĐ°Đ´Đ°Ņ†Đ¸", - "backup_database": "Đ ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", - "backup_database_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", - "backup_keep_last_amount": "КоĐģĐ¸Ņ‡Đ¸ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐ¸Ņ… Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° Са Ņ‡ŅƒĐ˛Đ°ŅšĐĩ", - "backup_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ", - "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēаĐŧа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "backup_database": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋĐ¸Ņ˜Ņƒ йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "backup_database_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ даĐŧĐŋОвĐĩ йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "backup_keep_last_amount": "КоĐģĐ¸Ņ‡Đ¸ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐ¸Ņ… даĐŧĐŋОва ĐēĐžŅ˜Đĩ ҂ҀĐĩйа ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸", + "backup_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° даĐŧĐŋа йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа даĐŧĐŋа йаСĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа. НаĐŋĐžĐŧĐĩĐŊа: Ови ĐŋĐžŅĐģОви ҁĐĩ ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đĩ и ĐŊĐĩ҆ˁĐĩŅ‚Đĩ ĐąĐ¸Ņ‚Đ¸ ОйавĐĩŅˆŅ‚ĐĩĐŊи Đž ĐŊĐĩ҃ҁĐŋĐĩŅ…Ņƒ.", "check_all": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ ŅĐ˛Đĩ", - "cleanup": "Đ§Đ¸ŅˆŅ›ĐĩҚĐĩ", - "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ›ĐĩĐŊи ĐŋĐžŅĐģОви Са {job}", + "cleanup": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ", + "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ†ĖĐĩĐŊи ĐŋĐžŅĐģОви Са: {job}", "config_set_by_file": "КоĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Ņƒ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸ĐžĐŊи Ņ„Đ°Ņ˜Đģ", "confirm_delete_library": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ {library} ?", - "confirm_delete_library_assets": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Ņƒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃? Ово Ņ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ {count, plural, one {1 ŅĐ°Đ´Ņ€ĐļĐĩĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# ŅĐ°Đ´Ņ€ĐļĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# ŅĐ°Đ´Ņ€ĐļĐĩĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}} иС Immich-a и аĐēŅ†Đ¸Ņ˜Đ° ҁĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐžĐŋĐžĐˇĐ˛Đ°Ņ‚Đ¸. Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ Ņ›Đĩ ĐžŅŅ‚Đ°Ņ‚Đ¸ ĐŊа Đ´Đ¸ŅĐē҃.", + "confirm_delete_library_assets": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Ņƒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃? Ово ҆ˁĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ {count, plural, one {1 ŅĐ°Đ´Ņ€ĐļĐĩĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# ŅĐ°Đ´Ņ€ĐļĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# ŅĐ°Đ´Ņ€ĐļĐĩĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}} иС Immich-a и аĐēŅ†Đ¸Ņ˜Đ° ҁĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐžĐŋĐžĐˇĐ˛Đ°Ņ‚Đ¸. Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҆ˁĐĩ ĐžŅŅ‚Đ°Ņ‚Đ¸ ĐŊа Đ´Đ¸ŅĐē҃.", "confirm_email_below": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Đģи, ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ \"{email}\" Đ¸ŅĐŋОд", - "confirm_reprocess_all_faces": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŋĐžĐŊОвО ĐžĐąŅ€Đ°Đ´Đ¸Ņ‚Đĩ ŅĐ˛Đ° ĐģĐ¸Ņ†Đ°? Ово Ņ›Đĩ Ņ‚Đ°ĐēĐžŅ’Đĩ ĐžĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ иĐŧĐĩĐŊОваĐŊĐĩ ĐžŅĐžĐąĐĩ.", + "confirm_reprocess_all_faces": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŋĐžĐŊОвО ĐžĐąŅ€Đ°Đ´Đ¸Ņ‚Đĩ ŅĐ˛Đ° ĐģĐ¸Ņ†Đ°? Ово ҆ˁĐĩ Ņ‚Đ°ĐēĐžŅ’Đĩ ĐžĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ иĐŧĐĩĐŊОваĐŊĐĩ ĐžŅĐžĐąĐĩ.", "confirm_user_password_reset": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ€ĐĩҁĐĩŅ‚ŅƒŅ˜ĐĩŅ‚Đĩ ĐģОСиĐŊĐē҃ ĐēĐžŅ€Đ¸ŅĐŊиĐēа {user}?", + "confirm_user_pin_code_reset": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ€ĐĩҁĐĩŅ‚ŅƒŅ˜ĐĩŅ‚Đĩ ПИН ĐēОд ĐēĐžŅ€Đ¸ŅĐŊиĐēа {user}?", "create_job": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŋĐžŅĐ°Đž", - "cron_expression": "Cron Đ¸ĐˇŅ€Đ°Đˇ (expression)", - "cron_expression_description": "ПодĐĩŅĐ¸Ņ‚Đĩ иĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐ° ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ›Đ¸ cron Ņ„ĐžŅ€ĐŧĐ°Ņ‚. За Đ˛Đ¸ŅˆĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ĐŊĐŋŅ€. Crontab Guru", - "cron_expression_presets": "ĐŸŅ€ĐĩĐ´ĐĩŅ„Đ¸ĐŊĐ¸ŅĐ°ĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° Cron Đ¸ĐˇŅ€Đ°ĐˇĐ° (expression)", - "disable_login": "oneĐŧĐžĐŗŅƒŅ›Đ¸ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", + "cron_expression": "ĐĻŅ€ĐžĐŊ Đ¸ĐˇŅ€Đ°Đˇ (ĐĩxĐŋŅ€ĐĩŅŅĐ¸ĐžĐŊ)", + "cron_expression_description": "ПодĐĩŅĐ¸Ņ‚Đĩ иĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐ° ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ†ĖĐ¸ Ņ†Ņ€ĐžĐŊ Ņ„ĐžŅ€ĐŧĐ°Ņ‚. За Đ˛Đ¸ŅˆĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ĐŊĐŋŅ€. ĐĻŅ€ĐžĐŊŅ‚Đ°Đą Đ“ŅƒŅ€Ņƒ", + "cron_expression_presets": "ĐŸŅ€ĐĩĐ´ĐĩŅ„Đ¸ĐŊĐ¸ŅĐ°ĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐĻŅ€ĐžĐŊ Đ¸ĐˇŅ€Đ°ĐˇĐ° (ĐĩxĐŋŅ€ĐĩŅŅĐ¸ĐžĐŊ)", + "disable_login": "ОĐŊĐĩĐŧĐžĐŗŅƒŅ†ĖĐ¸ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", "duplicate_detection_job_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ¸Đŧа да ĐąĐ¸ŅŅ‚Đĩ ĐžŅ‚ĐēŅ€Đ¸Đģи ҁĐģĐ¸Ņ‡ĐŊĐĩ ҁĐģиĐēĐĩ. ĐžŅĐģĐ°ŅšĐ° ҁĐĩ ĐŊа ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", - "exclusion_pattern_description": "ĐžĐąŅ€Đ°ŅŅ†Đ¸ Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ° ваĐŧ ĐžĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ°Ņ˜Ņƒ да Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩŅ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ Đēада ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃. Ово ҘĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐž аĐēĐž иĐŧĐ°Ņ‚Đĩ Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ ĐēĐžŅ˜Đĩ ŅĐ°Đ´Ņ€ĐļĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ĐŊĐĩ ĐļĐĩĐģĐ¸Ņ‚Đĩ да ŅƒĐ˛ĐĩСĐĩŅ‚Đĩ, ĐēаО ŅˆŅ‚Đž ҁ҃ RAW Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", + "exclusion_pattern_description": "ĐžĐąŅ€Đ°ŅŅ†Đ¸ Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ° ваĐŧ ĐžĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ°Ņ˜Ņƒ да Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩŅ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ Đēада ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃. Ово ҘĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐž аĐēĐž иĐŧĐ°Ņ‚Đĩ Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ ĐēĐžŅ˜Đĩ ŅĐ°Đ´Ņ€ĐļĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ĐŊĐĩ ĐļĐĩĐģĐ¸Ņ‚Đĩ да ŅƒĐ˛ĐĩСĐĩŅ‚Đĩ, ĐēаО ŅˆŅ‚Đž ҁ҃ РАW Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", "external_library_created_at": "ЕĐēҁ҂ĐĩŅ€ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа (ĐŊаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа {date})", "external_library_management": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐĩĐēҁ҂ĐĩŅ€ĐŊиĐŧ йийĐģĐ¸ĐžŅ‚ĐĩĐēаĐŧа", "face_detection": "ДĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đ° ĐģĐ¸Ņ†Đ°", - "face_detection_description": "ĐžŅ‚ĐēŅ€Đ¸Ņ˜Ņ‚Đĩ ĐģĐ¸Ņ†Đ° ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēаĐŧа ĐŋĐžĐŧĐžŅ›Ņƒ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°. За видĐĩĐž ҁĐŊиĐŧĐēĐĩ ҁĐĩ ŅƒĐˇĐ¸Đŧа ҃ ĐžĐąĐˇĐ¸Ņ€ ŅĐ°ĐŧĐž ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ°. â€žĐžŅĐ˛ĐĩĐļи“ (ĐŋĐžĐŊОвĐŊĐž) ĐžĐąŅ€Đ°Ņ’ŅƒŅ˜Đĩ ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. „РĐĩҁĐĩŅ‚ĐžĐ˛Đ°ŅšĐĩ“ Đ´ĐžĐ´Đ°Ņ‚ĐŊĐž ĐąŅ€Đ¸ŅˆĐĩ ŅĐ˛Đĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ Đž ĐģĐ¸Ņ†Ņƒ. „НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒâ€œ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ Ņ€ĐĩĐ´Ņƒ ĐēĐžŅ˜Đĩ Ņ˜ĐžŅˆ ĐŊĐ¸ŅŅƒ ĐžĐąŅ€Đ°Ņ’ĐĩĐŊĐĩ. ĐžŅ‚ĐēŅ€Đ¸Đ˛ĐĩĐŊа ĐģĐ¸Ņ†Đ° Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊа ҃ Ņ€ĐĩĐ´ Са ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐˇĐ°Đ˛Ņ€ŅˆĐ¸, ĐŗŅ€ŅƒĐŋĐ¸ŅˆŅƒŅ›Đ¸ Đ¸Ņ… ҃ ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›Đĩ иĐģи ĐŊОвĐĩ ĐžŅĐžĐąĐĩ.", - "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋа ҘĐĩ Đ´ĐĩŅ‚ĐĩĐēŅ‚ĐžĐ˛Đ°Đģа ĐģĐ¸Ņ†Đ° и дОдаĐģа Đ¸Ņ… ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›Đ¸Đŧ Ņ™ŅƒĐ´Đ¸Đŧа. ĐžĐ˛Đ°Ņ˜ ĐēĐžŅ€Đ°Đē ҁĐĩ ĐŋĐžĐēŅ€ĐĩŅ›Đĩ ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҘĐĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž. „РĐĩҁĐĩŅ‚â€œ (ĐŋĐžĐŊОвĐŊĐž) ĐŗŅ€ŅƒĐŋĐ¸ŅˆĐĩ ŅĐ˛Đ° ĐģĐ¸Ņ†Đ°. „НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒâ€œ ĐģĐ¸Ņ†Đ° ҃ Ņ€ĐĩдОвиĐŧа ĐēĐžŅ˜Đ¸Đŧа ĐŊĐ¸Ņ˜Đĩ дОдĐĩŅ™ĐĩĐŊа ĐžŅĐžĐąĐ°.", - "failed_job_command": "КоĐŧаĐŊда {command} ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģа Са ĐŋĐžŅĐ°Đž {job}", - "force_delete_user_warning": "ĐŖĐŸĐžĐ—ĐžĐ Đ•ĐŠĐ•: Ovo cˁe odmah ukloniti korisnika i sve datoteke. Ovo se ne moÅže opozvati i datoteke se ne mogu oporaviti.", + "face_detection_description": "ĐžŅ‚ĐēŅ€Đ¸Ņ˜Ņ‚Đĩ ĐģĐ¸Ņ†Đ° ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēаĐŧа ĐŋĐžĐŧĐžŅ†ĖŅƒ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°. За видĐĩĐž ҁĐŊиĐŧĐēĐĩ ҁĐĩ ŅƒĐˇĐ¸Đŧа ҃ ĐžĐąĐˇĐ¸Ņ€ ŅĐ°ĐŧĐž ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ°. â€žĐžŅĐ˛ĐĩĐļи“ (ĐŋĐžĐŊОвĐŊĐž) ĐžĐąŅ€Đ°Ņ’ŅƒŅ˜Đĩ ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. „РĐĩҁĐĩŅ‚ĐžĐ˛Đ°ŅšĐĩ“ Đ´ĐžĐ´Đ°Ņ‚ĐŊĐž ĐąŅ€Đ¸ŅˆĐĩ ŅĐ˛Đĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ Đž ĐģĐ¸Ņ†Ņƒ. „НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒâ€œ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ Ņ€ĐĩĐ´Ņƒ ĐēĐžŅ˜Đĩ Ņ˜ĐžŅˆ ĐŊĐ¸ŅŅƒ ĐžĐąŅ€Đ°Ņ’ĐĩĐŊĐĩ. ĐžŅ‚ĐēŅ€Đ¸Đ˛ĐĩĐŊа ĐģĐ¸Ņ†Đ° ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊа ҃ Ņ€ĐĩĐ´ Са ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐˇĐ°Đ˛Ņ€ŅˆĐ¸, ĐŗŅ€ŅƒĐŋĐ¸ŅˆŅƒŅ†ĖĐ¸ Đ¸Ņ… ҃ ĐŋĐžŅŅ‚ĐžŅ˜Đĩ҆ˁĐĩ иĐģи ĐŊОвĐĩ ĐžŅĐžĐąĐĩ.", + "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋа ҘĐĩ Đ´ĐĩŅ‚ĐĩĐēŅ‚ĐžĐ˛Đ°Đģа ĐģĐ¸Ņ†Đ° и дОдаĐģа Đ¸Ņ… ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ†ĖĐ¸Đŧ ĐžŅĐžĐąĐ°Đŧа. ĐžĐ˛Đ°Ņ˜ ĐēĐžŅ€Đ°Đē ҁĐĩ ĐŋĐžĐēŅ€Đĩ҆ˁĐĩ ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҘĐĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž. „РĐĩҁĐĩŅ‚ŅƒŅ˜â€œ (ĐŋĐžĐŊОвĐŊĐž) ĐŗŅ€ŅƒĐŋĐ¸ŅˆĐĩ ŅĐ˛Đ° ĐģĐ¸Ņ†Đ°. „НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒâ€œ ĐģĐ¸Ņ†Đ° ҃ Ņ€ĐĩдОвиĐŧа ĐēĐžŅ˜Đ¸Đŧа ĐŊĐ¸Ņ˜Đĩ дОдĐĩŅ™ĐĩĐŊа ĐžŅĐžĐąĐ°.", + "failed_job_command": "КоĐŧаĐŊда {command} ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģа Са ĐŋĐžŅĐ°Đž: {job}", + "force_delete_user_warning": "ĐŖĐŸĐžĐ—ĐžĐ Đ•ĐĐˆĐ•: Ово ҆ˁĐĩ ОдĐŧĐ°Ņ… ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа и ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. Ово ҁĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐžĐŋĐžĐˇĐ˛Đ°Ņ‚Đ¸ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁĐĩ ĐŊĐĩ ĐŧĐžĐŗŅƒ ĐžĐŋĐžŅ€Đ°Đ˛Đ¸Ņ‚Đ¸.", "forcing_refresh_library_files": "ĐŸŅ€Đ¸ĐŊŅƒĐ´ĐŊĐž ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ŅĐ˛Đ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "image_format": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚", - "image_format_description": "WebP ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ Од ЈПЕГ, аĐģи ҁĐĩ ҁĐŋĐžŅ€Đ¸Ņ˜Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°.", - "image_fullsize_description": "ĐĄĐģиĐēа ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи ŅĐ° ĐžĐŗĐžŅ™ĐĩĐŊиĐŧ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ Đēада ҘĐĩ ŅƒĐ˛ĐĩŅ›Đ°ĐŊа", - "image_fullsize_enabled": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŗĐĩĐŊĐĩŅ€Đ¸ŅĐ°ŅšĐĩ ҁĐģиĐēĐĩ ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи", - "image_fullsize_enabled_description": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ҁĐģиĐē҃ Đŋ҃ĐŊĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Са Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐēĐžŅ˜Đ¸ ĐŊĐ¸ŅŅƒ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи вĐĩĐąŅƒ. Када ҘĐĩ â€žĐŸŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ˜ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐŋŅ€ĐĩĐŗĐģĐĩд“ ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊ, ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐŋŅ€ĐĩĐŗĐģĐĩди ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đĩ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐŊĐž ĐąĐĩС ĐēĐžĐŊвĐĩŅ€ĐˇĐ¸Ņ˜Đĩ. НĐĩ ŅƒŅ‚Đ¸Ņ‡Đĩ ĐŊа Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊĐĩ вĐĩĐąŅƒ ĐēаО ŅˆŅ‚Đž ҘĐĩ JPEG.", - "image_fullsize_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ҁĐģиĐēĐĩ ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", + "image_format_description": "WĐĩбП ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ Од ЈПЕГ, аĐģи ҁĐĩ ҁĐŋĐžŅ€Đ¸Ņ˜Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°.", + "image_fullsize_description": "ĐĄĐģиĐēа ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи ŅĐ° ĐžĐŗĐžŅ™ĐĩĐŊиĐŧ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ Đēада ҘĐĩ ŅƒĐ˛ĐĩŅ†ĖĐ°ĐŊа", + "image_fullsize_enabled": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŗĐĩĐŊĐĩŅ€Đ¸ŅĐ°ŅšĐĩ ҁĐģиĐēĐĩ ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи", + "image_fullsize_enabled_description": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ҁĐģиĐē҃ Đŋ҃ĐŊĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Са Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐēĐžŅ˜Đ¸ ĐŊĐ¸ŅŅƒ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи вĐĩĐąŅƒ. Када ҘĐĩ â€žĐŸŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ˜ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐŋŅ€ĐĩĐŗĐģĐĩд“ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊ, ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐŋŅ€ĐĩĐŗĐģĐĩди ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đĩ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐŊĐž ĐąĐĩС ĐēĐžĐŊвĐĩŅ€ĐˇĐ¸Ņ˜Đĩ. НĐĩ ŅƒŅ‚Đ¸Ņ‡Đĩ ĐŊа Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊĐĩ вĐĩĐąŅƒ ĐēаО ŅˆŅ‚Đž ҘĐĩ ЈПЕГ.", + "image_fullsize_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ҁĐģиĐēĐĩ ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", "image_fullsize_title": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐģиĐēĐĩ ҃ Đŋ҃ĐŊĐžŅ˜ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊи", "image_prefer_embedded_preview": "ĐŸŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐŋŅ€ĐĩĐŗĐģĐĩĐ´", - "image_prefer_embedded_preview_setting_description": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊĐĩ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đĩ ҃ RAW Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ĐēаО ҃ĐģаС Са ĐžĐąŅ€Đ°Đ´Ņƒ ҁĐģиĐēĐĩ Đēада ҁ҃ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ. Ово ĐŧĐžĐļĐĩ да ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´Đĩ ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐŊĐ¸Ņ˜Đĩ ĐąĐžŅ˜Đĩ Са ĐŊĐĩĐēĐĩ ҁĐģиĐēĐĩ, аĐģи ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€ĐĩĐŗĐģĐĩда ĐˇĐ°Đ˛Đ¸ŅĐ¸ Од ĐēаĐŧĐĩŅ€Đĩ и ҁĐģиĐēа ĐŧĐžĐļĐĩ иĐŧĐ°Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐžŅŅ‚Đ¸ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ.", + "image_prefer_embedded_preview_setting_description": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊĐĩ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đĩ ҃ РАW Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ĐēаО ҃ĐģаС Са ĐžĐąŅ€Đ°Đ´Ņƒ ҁĐģиĐēĐĩ Đēада ҁ҃ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ. Ово ĐŧĐžĐļĐĩ да ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´Đĩ ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐŊĐ¸Ņ˜Đĩ ĐąĐžŅ˜Đĩ Са ĐŊĐĩĐēĐĩ ҁĐģиĐēĐĩ, аĐģи ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€ĐĩĐŗĐģĐĩда ĐˇĐ°Đ˛Đ¸ŅĐ¸ Од ĐēаĐŧĐĩŅ€Đĩ и ҁĐģиĐēа ĐŧĐžĐļĐĩ иĐŧĐ°Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐžŅŅ‚Đ¸ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ.", "image_prefer_wide_gamut": "ĐŸŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅˆĐ¸Ņ€ĐžĐē ҁĐŋĐĩĐēŅ‚Đ°Ņ€", - "image_prefer_wide_gamut_setting_description": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ Display П3 Са ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ. Ово ĐąĐžŅ™Đĩ Ņ‡ŅƒĐ˛Đ° ĐļивОĐŋĐ¸ŅĐŊĐžŅŅ‚ ҁĐģиĐēа ŅĐ° ŅˆĐ¸Ņ€ĐžĐēиĐŧ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ¸Đŧа ĐąĐžŅ˜Đ°, аĐģи ҁĐģиĐēĐĩ ĐŧĐžĐŗŅƒ Đ¸ĐˇĐŗĐģĐĩĐ´Đ°Ņ‚Đ¸ Đ´Ņ€ŅƒĐŗĐ°Ņ‡Đ¸Ņ˜Đĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа ŅĐ° ŅŅ‚Đ°Ņ€ĐžĐŧ вĐĩŅ€ĐˇĐ¸Ņ˜ĐžĐŧ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡Đ°. ŅĐ Đ“Đ‘ ҁĐģиĐēĐĩ ҁĐĩ Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ ĐēаО ŅĐ Đ“Đ‘ да йи ҁĐĩ иСйĐĩĐŗĐģĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ ĐąĐžŅ˜Đ°.", + "image_prefer_wide_gamut_setting_description": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ Đ”Đ¸ŅĐŋĐģаy П3 Са ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ. Ово ĐąĐžŅ™Đĩ Ņ‡ŅƒĐ˛Đ° ĐļивОĐŋĐ¸ŅĐŊĐžŅŅ‚ ҁĐģиĐēа ŅĐ° ŅˆĐ¸Ņ€ĐžĐēиĐŧ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ¸Đŧа ĐąĐžŅ˜Đ°, аĐģи ҁĐģиĐēĐĩ ĐŧĐžĐŗŅƒ Đ¸ĐˇĐŗĐģĐĩĐ´Đ°Ņ‚Đ¸ Đ´Ņ€ŅƒĐŗĐ°Ņ‡Đ¸Ņ˜Đĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа ŅĐ° ŅŅ‚Đ°Ņ€ĐžĐŧ вĐĩŅ€ĐˇĐ¸Ņ˜ĐžĐŧ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡Đ°. ŅĐ Đ“Đ‘ ҁĐģиĐēĐĩ ҁĐĩ Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ ĐēаО ŅĐ Đ“Đ‘ да йи ҁĐĩ иСйĐĩĐŗĐģĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ ĐąĐžŅ˜Đ°.", "image_preview_description": "ĐĄĐģиĐēа ҁҀĐĩĐ´ŅšĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ ŅĐ° ҃ĐēĐģĐžŅšĐĩĐŊиĐŧ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа, ĐēĐžŅ˜Đ° ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ ĐŋŅ€ĐĩĐŗĐģĐĩда ҘĐĩĐ´ĐŊĐžĐŗ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ° и Са ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ", - "image_preview_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€ĐĩĐŗĐģĐĩда Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŧĐžĐļĐĩ ҁĐŧĐ°ŅšĐ¸Ņ‚Đ¸ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ. ĐŸĐžŅŅ‚Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐŊĐ¸ŅĐēĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŧĐžĐļĐĩ ŅƒŅ‚Đ¸Ņ†Đ°Ņ‚Đ¸ ĐŊа ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°.", + "image_preview_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€ĐĩĐŗĐģĐĩда Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŧĐžĐļĐĩ ҁĐŧĐ°ŅšĐ¸Ņ‚Đ¸ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ. ĐŸĐžŅŅ‚Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐŊĐ¸ŅĐēĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŧĐžĐļĐĩ ŅƒŅ‚Đ¸Ņ†Đ°Ņ‚Đ¸ ĐŊа ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°.", "image_preview_title": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŋŅ€ĐĩĐŗĐģĐĩда", "image_quality": "КваĐģĐ¸Ņ‚ĐĩŅ‚", "image_resolution": "Đ ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ°", - "image_resolution_description": "ВĐĩŅ›Đĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒ да ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°, аĐģи иĐŧ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ, иĐŧĐ°Ņ˜Ņƒ вĐĩŅ›Đĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа и ĐŧĐžĐŗŅƒ да ҁĐŧĐ°ŅšĐĩ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", + "image_resolution_description": "ВĐĩ҆ˁĐĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒ да ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°, аĐģи иĐŧ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ, иĐŧĐ°Ņ˜Ņƒ вĐĩ҆ˁĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа и ĐŧĐžĐŗŅƒ да ҁĐŧĐ°ŅšĐĩ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", "image_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐģиĐēĐĩ", "image_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ĐžĐŧ и Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜ĐžĐŧ ĐŗĐĩĐŊĐĩŅ€Đ¸ŅĐ°ĐŊĐ¸Ņ… ҁĐģиĐēа", "image_thumbnail_description": "МаĐģа ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ° ŅĐ° ĐžĐŗĐžŅ™ĐĩĐŊиĐŧ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа, ĐēĐžŅ˜Đ° ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ ĐŋŅ€ĐĩĐŗĐģĐĩда ĐŗŅ€ŅƒĐŋа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° ĐēаО ŅˆŅ‚Đž ҘĐĩ ĐŗĐģавĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēа ĐģиĐŊĐ¸Ņ˜Đ°", - "image_thumbnail_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ° Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŧĐžĐļĐĩ ҁĐŧĐ°ŅšĐ¸Ņ‚Đ¸ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", + "image_thumbnail_quality_description": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ° Од 1-100. Đ’Đ¸ŅˆĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŧĐžĐļĐĩ ҁĐŧĐ°ŅšĐ¸Ņ‚Đ¸ ОдСив аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", "image_thumbnail_title": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ°", "job_concurrency": "{job} ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐžŅŅ‚", "job_created": "ĐŸĐžŅĐ°Đž ĐēŅ€ĐĩĐ¸Ņ€Đ°ĐŊ", "job_not_concurrency_safe": "ĐžĐ˛Đ°Ņ˜ ĐŋĐžŅĐ°Đž ĐŊĐ¸Ņ˜Đĩ ĐąĐĩСйĐĩдаĐŊ да ĐąŅƒĐ´Đĩ ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐž аĐēŅ‚Đ¸Đ˛Đ°ĐŊ.", "job_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŋĐžŅĐģа", - "job_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐžŅˆŅ›Ņƒ ĐŋĐžŅĐģОва", + "job_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐžŅˆŅ†ĖŅƒ ĐŋĐžŅĐģОва", "job_status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŋĐžŅĐģа", - "jobs_delayed": "{jobCount, plural, other {# ОдĐģĐžĐļĐĩĐŊĐ¸Ņ…}}", - "jobs_failed": "{jobCount, plural, other {# ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ…}}", - "library_created": "НаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа {library}", + "jobs_delayed": "{jobCount, plural, one {# ОдĐģĐžĐļĐĩĐŊи} few {# ОдĐģĐžĐļĐĩĐŊа} other {# ОдĐģĐžĐļĐĩĐŊĐ¸Ņ…}}", + "jobs_failed": "{jobCount, plural, one {# ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊи} few {# ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊа} other {# ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ…}}", + "library_created": "НаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа: {library}", "library_deleted": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа ҘĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊа", - "library_import_path_description": "ĐžĐ´Ņ€ĐĩĐ´Đ¸Ņ‚Đĩ Ņ„Đ°ŅŅ†Đ¸ĐēĐģ҃ Са ŅƒĐ˛ĐžĐˇ. Ова Ņ„Đ°ŅŅ†Đ¸ĐēĐģа, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ›Đ¸ ĐŋĐžĐ´Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ, ĐąĐ¸Ņ›Đĩ ҁĐēĐĩĐŊĐ¸Ņ€Đ°ĐŊа Са ҁĐģиĐēĐĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ.", + "library_import_path_description": "ĐžĐ´Ņ€ĐĩĐ´Đ¸Ņ‚Đĩ Ņ„Đ°ŅŅ†Đ¸ĐēĐģ҃ Са ŅƒĐ˛ĐžĐˇ. Ова Ņ„Đ°ŅŅ†Đ¸ĐēĐģа, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ†ĖĐ¸ ĐŋĐžĐ´Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ, ĐąĐ¸Ņ†ĖĐĩ ҁĐēĐĩĐŊĐ¸Ņ€Đ°ĐŊа Са ҁĐģиĐēĐĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ.", "library_scanning": "ПĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ", "library_scanning_description": "КоĐŊŅ„Đ¸ĐŗŅƒŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", - "library_scanning_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", + "library_scanning_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "library_settings": "ĐĄĐŋĐžŅ™ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "library_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", - "library_tasks_description": "ĐĄĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ ҃ ĐŋĐžŅ‚Ņ€Đ°ĐˇĐ¸ Са ĐŊОвиĐŧ и/иĐģи ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊиĐŧ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ¸Đŧа", + "library_tasks_description": "ĐžĐąĐ°Đ˛Ņ™Đ°Ņ˜ ĐˇĐ°Đ´Đ°Ņ‚ĐēĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "library_watching_enable_description": "ĐŸŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ Са ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "library_watching_settings": "ĐĐ°Đ´ĐŗĐģĐĩĐ´Đ°ŅšĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ (ЕКСПЕРИМЕНĐĸАЛНО)", "library_watching_settings_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "logging_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸ ĐĩвидĐĩĐŊŅ‚Đ¸Ņ€Đ°ŅšĐĩ", - "logging_level_description": "Када ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž, ĐēĐžŅ˜Đ¸ ĐŊивО ĐĩвидĐĩĐŊŅ†Đ¸Ņ˜Đĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đ¸.", + "logging_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ ĐĩвидĐĩĐŊŅ‚Đ¸Ņ€Đ°ŅšĐĩ", + "logging_level_description": "Када ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž, ĐēĐžŅ˜Đ¸ ĐŊивО ĐĩвидĐĩĐŊŅ†Đ¸Ņ˜Đĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đ¸.", "logging_settings": "ЕвидĐĩĐŊŅ‚Đ¸Ņ€Đ°ŅšĐĩ", "machine_learning_clip_model": "МодĐĩĐģ ĐĻЛИП", "machine_learning_clip_model_description": "Назив ĐĻЛИП ĐŧОдĐĩĐģа ҘĐĩ ĐŊавĐĩĐ´ĐĩĐŊ ОвдĐĩ. ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ĐŧĐžŅ€Đ°Ņ‚Đĩ ĐŋĐžĐŊОвО да ĐŋĐžĐēŅ€ĐĩĐŊĐĩŅ‚Đĩ ĐŋĐžŅĐ°Đž „ПаĐŧĐĩŅ‚ĐŊĐž ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°ŅšĐĩ“ Са ŅĐ˛Đĩ ҁĐģиĐēĐĩ ĐŊаĐēĐžĐŊ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ ĐŧОдĐĩĐģа.", "machine_learning_duplicate_detection": "ДĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đ° Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°", - "machine_learning_duplicate_detection_enabled": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ŅšĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°", - "machine_learning_duplicate_detection_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž, ĐŋĐžŅ‚Đŋ҃ĐŊĐž идĐĩĐŊŅ‚Đ¸Ņ‡ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° Ņ›Đĩ и Đ´Đ°Ņ™Đĩ ĐąĐ¸Ņ‚Đ¸ ҃ĐēĐģĐžŅšĐĩĐŊа.", + "machine_learning_duplicate_detection_enabled": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ŅšĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°", + "machine_learning_duplicate_detection_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž, ĐŋĐžŅ‚Đŋ҃ĐŊĐž идĐĩĐŊŅ‚Đ¸Ņ‡ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ҆ˁĐĩ и Đ´Đ°Ņ™Đĩ ĐąĐ¸Ņ‚Đ¸ ҃ĐēĐģĐžŅšĐĩĐŊа.", "machine_learning_duplicate_detection_setting_description": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊ ĐĻЛИП да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐžĐŊĐ°ŅˆĐģи вĐĩŅ€ĐžĐ˛Đ°Ņ‚ĐŊĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ", - "machine_learning_enabled": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ", - "machine_learning_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž, ŅĐ˛Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ МЛ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐĩ ĐąĐĩС ĐžĐąĐˇĐ¸Ņ€Đ° ĐŊа Đ´ĐžĐģĐĩ-ĐŊавĐĩĐ´ĐĩĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°.", + "machine_learning_enabled": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ", + "machine_learning_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž, ŅĐ˛Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ МЛ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐĩ ĐąĐĩС ĐžĐąĐˇĐ¸Ņ€Đ° ĐŊа Đ´ĐžĐģĐĩ-ĐŊавĐĩĐ´ĐĩĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°.", "machine_learning_facial_recognition": "ĐŸŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", "machine_learning_facial_recognition_description": "ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ŅšĐĩ, ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ и ĐŗŅ€ŅƒĐŋĐ¸ŅĐ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐŊа ҁĐģиĐēаĐŧа", "machine_learning_facial_recognition_model": "МодĐĩĐģ Са ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", - "machine_learning_facial_recognition_model_description": "МодĐĩĐģи ҁ҃ ĐŊавĐĩĐ´ĐĩĐŊи ҃ ĐžĐŋĐ°Đ´Đ°Ņ˜ŅƒŅ›ĐĩĐŧ Ņ€ĐĩĐ´ĐžŅĐģĐĩĐ´Ņƒ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ. ВĐĩŅ›Đ¸ ĐŧОдĐĩĐģи ҁ҃ ҁĐŋĐžŅ€Đ¸Ņ˜Đ¸ и ĐēĐžŅ€Đ¸ŅŅ‚Đĩ Đ˛Đ¸ŅˆĐĩ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ, аĐģи Đ´Đ°Ņ˜Ņƒ ĐąĐžŅ™Đĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đĩ. ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ĐŧĐžŅ€Đ°Ņ‚Đĩ ĐŋĐžĐŊОвО да ĐŋĐžĐēŅ€ĐĩĐŊĐĩŅ‚Đĩ ĐˇĐ°Đ´Đ°Ņ‚Đ°Đē Đ´ĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đĩ ĐģĐ¸Ņ†Đ° Са ŅĐ˛Đĩ ҁĐģиĐēĐĩ ĐŊаĐēĐžĐŊ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ ĐŧОдĐĩĐģа.", - "machine_learning_facial_recognition_setting": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", - "machine_learning_facial_recognition_setting_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž, ҁĐģиĐēĐĩ ĐŊĐĩŅ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ Са ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° и ĐŊĐĩŅ›Đĩ ĐŋĐžĐŋŅƒŅšĐ°Đ˛Đ°Ņ‚Đ¸ ОдĐĩŅ™Đ°Đē Đ‰ŅƒĐ´Đ¸ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ¸ Đ˜ŅŅ‚Ņ€Đ°Đļи.", + "machine_learning_facial_recognition_model_description": "МодĐĩĐģи ҁ҃ ĐŊавĐĩĐ´ĐĩĐŊи ҃ ĐžĐŋĐ°Đ´Đ°Ņ˜ŅƒŅ†ĖĐĩĐŧ Ņ€ĐĩĐ´ĐžŅĐģĐĩĐ´Ņƒ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ. ВĐĩŅ†ĖĐ¸ ĐŧОдĐĩĐģи ҁ҃ ҁĐŋĐžŅ€Đ¸Ņ˜Đ¸ и ĐēĐžŅ€Đ¸ŅŅ‚Đĩ Đ˛Đ¸ŅˆĐĩ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ, аĐģи Đ´Đ°Ņ˜Ņƒ ĐąĐžŅ™Đĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đĩ. ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ĐŧĐžŅ€Đ°Ņ‚Đĩ ĐŋĐžĐŊОвО да ĐŋĐžĐēŅ€ĐĩĐŊĐĩŅ‚Đĩ ĐˇĐ°Đ´Đ°Ņ‚Đ°Đē Đ´ĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đĩ ĐģĐ¸Ņ†Đ° Са ŅĐ˛Đĩ ҁĐģиĐēĐĩ ĐŊаĐēĐžĐŊ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ ĐŧОдĐĩĐģа.", + "machine_learning_facial_recognition_setting": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", + "machine_learning_facial_recognition_setting_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž, ҁĐģиĐēĐĩ ĐŊĐĩ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ Са ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° и ĐŊĐĩ҆ˁĐĩ ĐŋĐžĐŋŅƒŅšĐ°Đ˛Đ°Ņ‚Đ¸ ОдĐĩŅ™Đ°Đē Đ‰ŅƒĐ´Đ¸ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ¸ Đ˜ŅŅ‚Ņ€Đ°Đļи.", "machine_learning_max_detection_distance": "МаĐēŅĐ¸ĐŧаĐģĐŊа ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžŅŅ‚ Đ´ĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đĩ", - "machine_learning_max_detection_distance_description": "МаĐēŅĐ¸ĐŧаĐģĐŊĐž Ņ€Đ°ŅŅ‚ĐžŅ˜Đ°ŅšĐĩ иСĐŧĐĩŅ’Ņƒ двĐĩ ҁĐģиĐēĐĩ да ҁĐĩ ҁĐŧĐ°Ņ‚Ņ€Đ°Ņ˜Ņƒ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ¸Đŧа, ҃ Ņ€Đ°ŅĐŋĐžĐŊ҃ Од 0,001-0,1. ВĐĩŅ›Đĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ Ņ›Đĩ ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°, аĐģи ĐŧĐžĐŗŅƒ дОвĐĩŅŅ‚Đ¸ Đ´Đž ĐģаĐļĐŊĐ¸Ņ… ĐŋĐžĐˇĐ¸Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°.", + "machine_learning_max_detection_distance_description": "МаĐēŅĐ¸ĐŧаĐģĐŊĐž Ņ€Đ°ŅŅ‚ĐžŅ˜Đ°ŅšĐĩ иСĐŧĐĩŅ’Ņƒ двĐĩ ҁĐģиĐēĐĩ да ҁĐĩ ҁĐŧĐ°Ņ‚Ņ€Đ°Ņ˜Ņƒ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ¸Đŧа, ҃ Ņ€Đ°ŅĐŋĐžĐŊ҃ Од 0,001-0,1. ВĐĩ҆ˁĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҆ˁĐĩ ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°, аĐģи ĐŧĐžĐŗŅƒ дОвĐĩŅŅ‚Đ¸ Đ´Đž ĐģаĐļĐŊĐ¸Ņ… ĐŋĐžĐˇĐ¸Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°.", "machine_learning_max_recognition_distance": "МаĐēŅĐ¸ĐŧаĐģĐŊа ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžŅŅ‚ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐ°", - "machine_learning_max_recognition_distance_description": "МаĐēŅĐ¸ĐŧаĐģĐŊа ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžŅŅ‚ иСĐŧĐĩŅ’Ņƒ два ĐģĐ¸Ņ†Đ° ĐēĐžŅ˜Đ° ҁĐĩ ҁĐŧĐ°Ņ‚Ņ€Đ° Đ¸ŅŅ‚ĐžĐŧ ĐžŅĐžĐąĐžĐŧ, ҃ Ņ€Đ°ŅĐŋĐžĐŊ҃ Од 0-2. ĐĄĐŧĐ°ŅšĐĩҚĐĩ ĐžĐ˛ĐžĐŗ ĐąŅ€ĐžŅ˜Đ° ĐŧĐžĐļĐĩ ҁĐŋŅ€ĐĩŅ‡Đ¸Ņ‚Đ¸ ОСĐŊĐ°Ņ‡Đ°Đ˛Đ°ŅšĐĩ двĐĩ ĐžŅĐžĐąĐĩ ĐēаО Đ¸ŅŅ‚Đĩ ĐžŅĐžĐąĐĩ, Đ´ĐžĐē ĐŋОвĐĩŅ›Đ°ŅšĐĩ ĐŧĐžĐļĐĩ ҁĐŋŅ€ĐĩŅ‡Đ¸Ņ‚Đ¸ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ¸Ņ€Đ°ŅšĐĩ Đ¸ŅŅ‚Đĩ ĐžŅĐžĐąĐĩ ĐēаО двĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Ņ‚Đĩ ĐžŅĐžĐąĐĩ. ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ҘĐĩ ĐģаĐē҈Đĩ ҁĐŋĐžŅ˜Đ¸Ņ‚Đ¸ двĐĩ ĐžŅĐžĐąĐĩ ĐŊĐĩĐŗĐž ĐŋОдĐĩĐģĐ¸Ņ‚Đ¸ ҘĐĩĐ´ĐŊ҃ ĐžŅĐžĐąŅƒ ĐŊа Đ´Đ˛ĐžŅ˜Đĩ, Đŋа ĐŋĐžĐŗŅ€ĐĩŅˆĐ¸Ņ‚Đĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊи ĐŊиĐļĐĩĐŗ ĐŋŅ€Đ°ĐŗĐ° Đēада ҘĐĩ Ņ‚Đž ĐŧĐžĐŗŅƒŅ›Đĩ.", + "machine_learning_max_recognition_distance_description": "МаĐēŅĐ¸ĐŧаĐģĐŊа ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžŅŅ‚ иСĐŧĐĩŅ’Ņƒ два ĐģĐ¸Ņ†Đ° ĐēĐžŅ˜Đ° ҁĐĩ ҁĐŧĐ°Ņ‚Ņ€Đ° Đ¸ŅŅ‚ĐžĐŧ ĐžŅĐžĐąĐžĐŧ, ҃ Ņ€Đ°ŅĐŋĐžĐŊ҃ Од 0-2. ĐĄĐŧĐ°ŅšĐĩҚĐĩ ĐžĐ˛ĐžĐŗ ĐąŅ€ĐžŅ˜Đ° ĐŧĐžĐļĐĩ ҁĐŋŅ€ĐĩŅ‡Đ¸Ņ‚Đ¸ ОСĐŊĐ°Ņ‡Đ°Đ˛Đ°ŅšĐĩ двĐĩ ĐžŅĐžĐąĐĩ ĐēаО Đ¸ŅŅ‚Đĩ ĐžŅĐžĐąĐĩ, Đ´ĐžĐē ĐŋОвĐĩŅ†ĖĐ°ŅšĐĩ ĐŧĐžĐļĐĩ ҁĐŋŅ€ĐĩŅ‡Đ¸Ņ‚Đ¸ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ¸Ņ€Đ°ŅšĐĩ Đ¸ŅŅ‚Đĩ ĐžŅĐžĐąĐĩ ĐēаО двĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Ņ‚Đĩ ĐžŅĐžĐąĐĩ. ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ҘĐĩ ĐģаĐē҈Đĩ ҁĐŋĐžŅ˜Đ¸Ņ‚Đ¸ двĐĩ ĐžŅĐžĐąĐĩ ĐŊĐĩĐŗĐž ĐŋОдĐĩĐģĐ¸Ņ‚Đ¸ ҘĐĩĐ´ĐŊ҃ ĐžŅĐžĐąŅƒ ĐŊа Đ´Đ˛ĐžŅ˜Đĩ, Đŋа ĐŋĐžĐŗŅ€ĐĩŅˆĐ¸Ņ‚Đĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊи ĐŊиĐļĐĩĐŗ ĐŋŅ€Đ°ĐŗĐ° Đēада ҘĐĩ Ņ‚Đž ĐŧĐžĐŗŅƒŅ†ĖĐĩ.", "machine_learning_min_detection_score": "ĐĐ°Ņ˜ĐŧĐ°ŅšĐ¸ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚ Đ´ĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đĩ", - "machine_learning_min_detection_score_description": "МиĐŊиĐŧаĐģĐŊи Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚ ĐŋĐžŅƒĐˇĐ´Đ°ĐŊĐžŅŅ‚Đ¸ Са ĐģĐ¸Ņ†Đĩ ĐēĐžŅ˜Đĩ ҂ҀĐĩйа ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Од 0-1. НиĐļĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ Ņ›Đĩ ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ ĐģĐ¸Ņ†Đ°, аĐģи ĐŧĐžĐŗŅƒ дОвĐĩŅŅ‚Đ¸ Đ´Đž ĐģаĐļĐŊĐ¸Ņ… ĐŋĐžĐˇĐ¸Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°.", + "machine_learning_min_detection_score_description": "МиĐŊиĐŧаĐģĐŊи Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚ ĐŋĐžŅƒĐˇĐ´Đ°ĐŊĐžŅŅ‚Đ¸ Са ĐģĐ¸Ņ†Đĩ ĐēĐžŅ˜Đĩ ҂ҀĐĩйа ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Од 0-1. НиĐļĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҆ˁĐĩ ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ˛Đ¸ŅˆĐĩ ĐģĐ¸Ņ†Đ°, аĐģи ĐŧĐžĐŗŅƒ дОвĐĩŅŅ‚Đ¸ Đ´Đž ĐģаĐļĐŊĐ¸Ņ… ĐŋĐžĐˇĐ¸Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°.", "machine_learning_min_recognized_faces": "ĐĐ°Ņ˜ĐŧĐ°ŅšĐĩ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Ņ‚Đ¸Ņ… ĐģĐ¸Ņ†Đ°", - "machine_learning_min_recognized_faces_description": "МиĐŊиĐŧаĐģĐŊи ĐąŅ€ĐžŅ˜ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Ņ‚Đ¸Ņ… ĐģĐ¸Ņ†Đ° Са ĐēŅ€ĐĩĐ¸Ņ€Đ°ŅšĐĩ ĐžŅĐžĐąĐĩ. ПовĐĩŅ›Đ°ŅšĐĩ ĐžĐ˛ĐžĐŗĐ° Ņ‡Đ¸ĐŊи ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐŊĐ¸Ņ˜Đ¸Đŧ ĐŋĐž ҆ĐĩĐŊ҃ ĐŋОвĐĩŅ›Đ°ŅšĐ° ŅˆĐ°ĐŊҁĐĩ да ĐģĐ¸Ņ†Đĩ ĐŊĐ¸Ņ˜Đĩ дОдĐĩŅ™ĐĩĐŊĐž ĐžŅĐžĐąĐ¸.", + "machine_learning_min_recognized_faces_description": "МиĐŊиĐŧаĐģĐŊи ĐąŅ€ĐžŅ˜ ĐŋŅ€ĐĩĐŋОСĐŊĐ°Ņ‚Đ¸Ņ… ĐģĐ¸Ņ†Đ° Са ĐēŅ€ĐĩĐ¸Ņ€Đ°ŅšĐĩ ĐžŅĐžĐąĐĩ. ПовĐĩŅ†ĖĐ°ŅšĐĩ ĐžĐ˛ĐžĐŗĐ° Ņ‡Đ¸ĐŊи ĐŋŅ€ĐĩĐŋОСĐŊĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ° ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐŊĐ¸Ņ˜Đ¸Đŧ ĐŋĐž ҆ĐĩĐŊ҃ ĐŋОвĐĩŅ†ĖĐ°ŅšĐ° ŅˆĐ°ĐŊҁĐĩ да ĐģĐ¸Ņ†Đĩ ĐŊĐ¸Ņ˜Đĩ дОдĐĩŅ™ĐĩĐŊĐž ĐžŅĐžĐąĐ¸.", "machine_learning_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°", "machine_learning_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ°Đŧа и ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐžĐŗ ŅƒŅ‡ĐĩŅšĐ°", "machine_learning_smart_search": "ПаĐŧĐĩŅ‚ĐŊа ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ°", - "machine_learning_smart_search_description": "ĐŸĐžŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ҁĐģиĐēĐĩ ҁĐĩĐŧаĐŊŅ‚Đ¸Ņ‡Đēи ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ›Đ¸ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐĻЛИП", - "machine_learning_smart_search_enabled": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", - "machine_learning_smart_search_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž, ҁĐģиĐēĐĩ ĐŊĐĩŅ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ Са ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ.", - "machine_learning_url_description": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Са ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ. АĐēĐž ҘĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐž Đ˛Đ¸ŅˆĐĩ Од ҘĐĩĐ´ĐŊĐĩ URL Đ°Đ´Ņ€ĐĩҁĐĩ, ŅĐ˛Đ°Đēи ҁĐĩŅ€Đ˛ĐĩŅ€ Ņ›Đĩ ҁĐĩ ĐŋĐžĐēŅƒŅˆĐ°Đ˛Đ°Ņ‚Đ¸ ҘĐĩдаĐŊ ĐŋĐž ҘĐĩдаĐŊ Đ´ĐžĐē ҘĐĩдаĐŊ ĐŊĐĩ ĐžĐ´ĐŗĐžĐ˛ĐžŅ€Đ¸ ҃ҁĐŋĐĩ҈ĐŊĐž, Ņ€ĐĩĐ´ĐžĐŧ Од ĐŋŅ€Đ˛ĐžĐŗ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ŅšĐĩĐŗ. ĐĄĐĩŅ€Đ˛ĐĩŅ€Đ¸ ĐēĐžŅ˜Đ¸ ĐŊĐĩ Ņ€ĐĩĐ°ĐŗŅƒŅ˜Ņƒ ĐąĐ¸Ņ›Đĩ ĐŋŅ€Đ¸Đ˛Ņ€ĐĩĐŧĐĩĐŊĐž СаĐŊĐĩĐŧĐ°Ņ€ĐĩĐŊи Đ´ĐžĐē ҁĐĩ ĐŊĐĩ Đ˛Ņ€Đ°Ņ‚Đĩ ĐŊа ĐŧŅ€ĐĩĐļ҃.", - "manage_concurrency": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐžŅˆŅ›Ņƒ", + "machine_learning_smart_search_description": "ĐŸĐžŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ҁĐģиĐēĐĩ ҁĐĩĐŧаĐŊŅ‚Đ¸Ņ‡Đēи ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ†ĖĐ¸ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊи ĐĻЛИП", + "machine_learning_smart_search_enabled": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", + "machine_learning_smart_search_enabled_description": "АĐēĐž ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž, ҁĐģиĐēĐĩ ĐŊĐĩ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ Са ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ.", + "machine_learning_url_description": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Са ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ. АĐēĐž ҘĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐž Đ˛Đ¸ŅˆĐĩ URL Đ°Đ´Ņ€ĐĩŅĐ°, ŅĐ˛Đ°Đēи ҁĐĩŅ€Đ˛ĐĩŅ€ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžĐēŅƒŅˆĐ°Đ˛Đ°ĐŊ ĐŋĐžŅ˜ĐĩдиĐŊĐ°Ņ‡ĐŊĐž Đ´ĐžĐē ĐŊĐĩ ĐžĐ´ĐŗĐžĐ˛ĐžŅ€Đ¸ ҃ҁĐŋĐĩ҈ĐŊĐž, Ņ€ĐĩĐ´ĐžĐŧ Од ĐŋŅ€Đ˛ĐžĐŗ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ŅšĐĩĐŗ. ĐĄĐĩŅ€Đ˛ĐĩŅ€Đ¸ ĐēĐžŅ˜Đ¸ ĐŊĐĩ ĐžĐ´ĐŗĐžĐ˛ĐžŅ€Đĩ ĐąĐ¸Ņ†ĖĐĩ ĐŋŅ€Đ¸Đ˛Ņ€ĐĩĐŧĐĩĐŊĐž Đ¸ĐŗĐŊĐžŅ€Đ¸ŅĐ°ĐŊи Đ´ĐžĐē ҁĐĩ ĐŋĐžĐŊОвО ĐŊĐĩ ĐŋОвĐĩĐļ҃ ŅĐ° ĐŧŅ€ĐĩĐļĐžĐŧ.", + "manage_concurrency": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐŋĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐžŅˆŅ†ĖŅƒ", "manage_log_settings": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐĩвидĐĩĐŊŅ†Đ¸Ņ˜Đĩ", "map_dark_style": "ĐĸаĐŧĐŊи ŅŅ‚Đ¸Đģ", - "map_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐēĐ°Ņ€Đ°ĐēŅ‚ĐĩŅ€Đ¸ŅŅ‚Đ¸ĐēĐĩ ĐŧаĐŋĐĩ", + "map_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐēĐ°Ņ€Đ°ĐēŅ‚ĐĩŅ€Đ¸ŅŅ‚Đ¸ĐēĐĩ ĐŧаĐŋĐĩ", "map_gps_settings": "МаĐŋ & ГПС ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "map_gps_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēаĐŧа ĐŧаĐŋĐĩ и ГПС-а (ĐžĐąŅ€ĐŊŅƒŅ‚Đž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ)", - "map_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ĐŧаĐŋĐĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ° ĐŊа ĐĩĐēҁ҂ĐĩŅ€ĐŊ҃ ҃ҁĐģŅƒĐŗŅƒ ĐŋĐģĐžŅ‡Đ¸Ņ†Đ° (tiles.immich.cloud)", + "map_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ĐŧаĐŋĐĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ° ĐŊа ĐĩĐēҁ҂ĐĩŅ€ĐŊ҃ ҃ҁĐģŅƒĐŗŅƒ ĐŋĐģĐžŅ‡Đ¸Ņ†Đ° (Ņ‚Đ¸ĐģĐĩҁ.иĐŧĐŧĐ¸Ņ†Ņ….҆ĐģĐžŅƒĐ´)", "map_light_style": "ХвĐĩŅ‚Đģи ŅŅ‚Đ¸Đģ", "map_manage_reverse_geocoding_settings": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐžĐąŅ€ĐŊŅƒŅ‚Đž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", "map_reverse_geocoding": "ĐžĐąŅ€ĐŊŅƒŅ‚Đž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", - "map_reverse_geocoding_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐžĐąŅ€ĐŊŅƒŅ‚Đž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", + "map_reverse_geocoding_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐžĐąŅ€ĐŊŅƒŅ‚Đž ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", "map_reverse_geocoding_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐžĐąŅ€ĐŊŅƒŅ‚ĐžĐŗ ĐŗĐĩĐžĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°", "map_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŧаĐŋĐĩ", "map_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐŧаĐŋĐĩ", - "map_style_description": "ĐŖĐ Đ› Đ´Đž style.json ĐŧаĐŋĐĩ Ņ‚ĐĩĐŧа Đ¸ĐˇĐŗĐģĐĩда", - "memory_cleanup_job": "Đ§Đ¸ŅˆŅ›ĐĩҚĐĩ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ", + "map_style_description": "URL Đ´Đž ҁ҂yĐģĐĩ.Ņ˜ŅĐžĐŊ ĐŧаĐŋĐĩ Ņ‚ĐĩĐŧа Đ¸ĐˇĐŗĐģĐĩда", + "memory_cleanup_job": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ", "memory_generate_job": "ГĐĩĐŊĐĩŅ€Đ°Ņ†Đ¸Ņ˜Đ° ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ", "metadata_extraction_job": "ИСвОд ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", - "metadata_extraction_job_description": "Đ˜ĐˇĐ˛ŅƒŅ†Đ¸Ņ‚Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа иС ŅĐ˛Đ°ĐēĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ, ĐēаО ŅˆŅ‚Đž ҁ҃ GPS, ĐģĐ¸Ņ†Đ° и Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ°", - "metadata_faces_import_setting": "ОĐŧĐžĐŗŅƒŅ›Đ¸ (enable) ŅƒĐ˛ĐžĐˇ ĐģĐ¸Ņ†Đ°", - "metadata_faces_import_setting_description": "ĐŖĐ˛ĐĩĐˇĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†Đ° иС EXIF ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа ҁĐģиĐēа и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ŅĐ° ĐąĐžŅ‡ĐŊĐĩ Ņ‚Ņ€Đ°ĐēĐĩ", - "metadata_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "metadata_extraction_job_description": "Đ˜ĐˇĐ˛ŅƒŅ†Đ¸Ņ‚Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ†Đ¸Đŧа иС ŅĐ˛Đ°ĐēĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ, ĐēаО ŅˆŅ‚Đž ҁ҃ ГПС, ĐģĐ¸Ņ†Đ° и Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ°", + "metadata_faces_import_setting": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ (ĐĩĐŊайĐģĐĩ) Đ´ĐžĐ´Đ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", + "metadata_faces_import_setting_description": "Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ ĐģĐ¸Ņ†Đ° иС EXIF ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа ҁĐģиĐēĐĩ и ҁĐģĐ¸Ņ‡ĐŊĐ¸Ņ… ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "metadata_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", "metadata_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", "migration_job": "ĐœĐ¸ĐŗŅ€Đ°Ņ†Đ¸Ņ˜Đĩ", "migration_job_description": "ĐŸŅ€ĐĩĐŊĐĩŅĐ¸Ņ‚Đĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа и ĐģĐ¸Ņ†Đ° ҃ ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Ņƒ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Ņƒ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧа", "no_paths_added": "НĐĩĐŧа Đ´ĐžĐ´Đ°Ņ‚Đ¸Ņ… ĐŋŅƒŅ‚Đ°ŅšĐ°", "no_pattern_added": "ĐĐ¸Ņ˜Đĩ Đ´ĐžĐ´Đ°Ņ‚ ĐžĐąŅ€Đ°ĐˇĐ°Ņ†", - "note_apply_storage_label_previous_assets": "НаĐŋĐžĐŧĐĩĐŊа: Da biste primenili oznaku za skladiÅĄtenje na prethodno otpremljena sredstva, pokrenite", - "note_cannot_be_changed_later": "НАПОМЕНА: Ovo se kasnije ne moÅže promeniti!", + "note_apply_storage_label_previous_assets": "НаĐŋĐžĐŧĐĩĐŊа: Да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊиĐģи ОСĐŊаĐē҃ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°, ĐŋĐžĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ", + "note_cannot_be_changed_later": "НАПОМЕНА: Ово ҁĐĩ ĐēĐ°ŅĐŊĐ¸Ņ˜Đĩ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸!", "notification_email_from_address": "Ха Đ°Đ´Ņ€ĐĩҁĐĩ", - "notification_email_from_address_description": "ĐĐ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ ĐŋĐžŅˆĐ¸Ņ™Đ°ĐžŅ†Đ°, ĐŊа ĐŋŅ€Đ¸ĐŧĐĩŅ€: \"Immich foto server \"", - "notification_email_host_description": "ĐĨĐžŅŅ‚ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ (ĐŊĐŋŅ€. smtp.immich.app)", + "notification_email_from_address_description": "ĐĐ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ ĐŋĐžŅˆĐ¸Ņ™Đ°ĐžŅ†Đ°, ĐŊа ĐŋŅ€Đ¸ĐŧĐĩŅ€: \"Immich Ņ„ĐžŅ‚Đž ҁĐĩŅ€Đ˛ĐĩŅ€ <ĐŊĐžŅ€ĐĩĐŋĐģy@ĐĩxаĐŧĐŋĐģĐĩ.Ņ†ĐžĐŧ>\"", + "notification_email_host_description": "ĐĨĐžŅŅ‚ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ (ĐŊĐŋŅ€. ҁĐŧŅ‚Đŋ.иĐŧĐŧĐ¸Ņ†Ņ….аĐŋĐŋ)", "notification_email_ignore_certificate_errors": "ЗаĐŊĐĩĐŧĐ°Ņ€Đ¸Ņ‚Đĩ ĐŗŅ€Đĩ҈ĐēĐĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ°", "notification_email_ignore_certificate_errors_description": "Đ˜ĐŗĐŊĐžŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ĐŗŅ€Đĩ҈ĐēĐĩ ҃ ваĐģĐ¸Đ´Đ°Ņ†Đ¸Ņ˜Đ¸ ĐĸЛС ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° (ĐŊĐĩ ĐŋŅ€ĐĩĐŋĐžŅ€ŅƒŅ‡ŅƒŅ˜Đĩ ҁĐĩ)", "notification_email_password_description": "ЛозиĐŊĐēа Са ҃ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒ ĐŋŅ€Đ¸ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đ¸ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", @@ -184,45 +185,41 @@ "notification_email_test_email_failed": "ĐĄĐģĐ°ŅšĐĩ ĐŋŅ€ĐžĐąĐŊĐĩ Đĩ-ĐŋĐžŅˆŅ‚Đĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸", "notification_email_test_email_sent": "ĐŸŅ€ĐžĐąĐŊа Đĩ-ĐŋĐžŅˆŅ‚Đ° ҘĐĩ ĐŋĐžŅĐģĐ°Ņ‚Đ° ĐŊа {email}. ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đĩ ĐŋŅ€Đ¸Ņ˜ĐĩĐŧĐŊĐž ŅĐ°ĐŊĐ´ŅƒŅ‡Đĩ.", "notification_email_username_description": "ĐšĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēĐž иĐŧĐĩ ĐēĐžŅ˜Đĩ ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", - "notification_enable_email_notifications": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", + "notification_enable_email_notifications": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", "notification_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ОйавĐĩŅˆŅ‚ĐĩŅšĐ°", - "notification_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ОйавĐĩŅˆŅ‚ĐĩŅšĐ°, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ›Đ¸ Đĩ-ĐŋĐžŅˆŅ‚Ņƒ", + "notification_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ОйавĐĩŅˆŅ‚ĐĩŅšĐ°, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ†ĖĐ¸ Đĩ-ĐŋĐžŅˆŅ‚Ņƒ", "oauth_auto_launch": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋĐžĐēŅ€ĐĩŅ‚Đ°ŅšĐĩ", "oauth_auto_launch_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ OAuth Ņ‚ĐžĐē ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ° Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŊаĐēĐžĐŊ ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ˜Đĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", "oauth_auto_register": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ°", - "oauth_auto_register_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€ŅƒŅ˜Ņ‚Đĩ ĐŊОвĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋĐžĐŧĐžŅ›Ņƒ OAuth-a", + "oauth_auto_register_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€ŅƒŅ˜Ņ‚Đĩ ĐŊОвĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ OAuth-а", "oauth_button_text": "ĐĸĐĩĐēҁ҂ Đ´ŅƒĐŗĐŧĐĩŅ‚Đ°", - "oauth_client_id": "ИД ĐēĐģĐ¸Ņ˜ĐĩĐŊŅ‚Đ°", - "oauth_client_secret": "ĐĸĐ°Ņ˜ĐŊа ĐēĐģĐ¸Ņ˜ĐĩĐŊŅ‚Đ°", - "oauth_enable_description": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐŧĐžŅ›Ņƒ OAuth-a", - "oauth_issuer_url": "ĐŖĐ Đ› Đ¸ĐˇĐ´Đ°Đ˛Đ°Ņ‡Đ°", + "oauth_client_secret_description": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐŊĐž аĐēĐž OAuth ĐŋŅ€ĐžĐ˛Đ°Ņ˜Đ´ĐĩŅ€ ĐŊĐĩ ĐŋĐžĐ´Ņ€Đļава ПКĐĻЕ (ĐŸŅ€ĐžĐžŅ„ КĐĩy Ņ„ĐžŅ€ ĐĻОдĐĩ ЕxŅ†Ņ…Đ°ĐŊĐŗĐĩ)", + "oauth_enable_description": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ OAuth-а", "oauth_mobile_redirect_uri": "ĐŖĐ Đ˜ Са ĐŋŅ€Đĩ҃ҁĐŧĐĩŅ€Đ°Đ˛Đ°ŅšĐĩ ĐŧОйиĐģĐŊĐ¸Ņ… ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", "oauth_mobile_redirect_uri_override": "ЗаĐŧĐĩĐŊа ĐŖĐ Đ˜-Ņ˜Đ° ĐŧОйиĐģĐŊĐžĐŗ ĐŋŅ€Đĩ҃ҁĐŧĐĩŅ€Đ°Đ˛Đ°ŅšĐ°", - "oauth_mobile_redirect_uri_override_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸ Đēада ОАuth Đ´ĐžĐąĐ°Đ˛Ņ™Đ°Ņ‡ (provider) ĐŊĐĩ Đ´ĐžĐˇĐ˛ĐžŅ™Đ°Đ˛Đ° ĐŧОйиĐģĐŊи URI, ĐēаО ŅˆŅ‚Đž ҘĐĩ '{callback}'", - "oauth_profile_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đ°Đŧ Са ĐŋĐžŅ‚ĐŋĐ¸ŅĐ¸Đ˛Đ°ŅšĐĩ ĐŋŅ€ĐžŅ„Đ¸Đģа", - "oauth_profile_signing_algorithm_description": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đ°Đŧ ĐēĐžŅ˜Đ¸ ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Са ĐŋĐžŅ‚ĐŋĐ¸ŅĐ¸Đ˛Đ°ŅšĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēĐžĐŗ ĐŋŅ€ĐžŅ„Đ¸Đģа.", - "oauth_scope": "ОбиĐŧ", + "oauth_mobile_redirect_uri_override_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ Đēада OAuth Đ´ĐžĐąĐ°Đ˛Ņ™Đ°Ņ‡ (ĐŋŅ€ĐžĐ˛Đ¸Đ´ĐĩŅ€) ĐŊĐĩ Đ´ĐžĐˇĐ˛ĐžŅ™Đ°Đ˛Đ° ĐŧОйиĐģĐŊи ĐŖĐ Đ˜, ĐēаО ŅˆŅ‚Đž ҘĐĩ '{Ņ†Đ°ĐģĐģĐąĐ°Ņ†Đē}'", "oauth_settings": "ĐžĐŅƒŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°", "oauth_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ŅĐ° ĐžĐŅƒŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ˜ĐžĐŧ", "oauth_settings_more_details": "За Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ° Đž ĐžĐ˛ĐžŅ˜ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ¸ ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đĩ.", - "oauth_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đ°Đŧ ĐŋĐžŅ‚ĐŋĐ¸ŅĐ¸Đ˛Đ°ŅšĐ°", "oauth_storage_label_claim": "Đ—Đ°Ņ…Ņ‚Đĩв Са ĐŊаĐģĐĩĐŋĐŊĐ¸Ņ†Ņƒ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", "oauth_storage_label_claim_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋОдĐĩŅĐ¸Ņ‚Đĩ ОСĐŊаĐē҃ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ĐŊа Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐžĐ˛ĐžĐŗ ĐˇĐ°Ņ…Ņ‚Đĩва.", "oauth_storage_quota_claim": "Đ—Đ°Ņ…Ņ‚Đĩв Са ĐēĐ˛ĐžŅ‚Ņƒ ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩŅšĐ°", "oauth_storage_quota_claim_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋОдĐĩŅĐ¸Ņ‚Đĩ ĐēĐ˛ĐžŅ‚Ņƒ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜ŅĐēĐžĐŗ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ĐŊа Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐžĐ˛ĐžĐŗ ĐˇĐ°Ņ…Ņ‚Đĩва.", - "oauth_storage_quota_default": "ĐŸĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа ĐēĐ˛ĐžŅ‚Đ° Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ (GiB)", + "oauth_storage_quota_default": "ĐŸĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа ĐēĐ˛ĐžŅ‚Đ° Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ (ГиБ)", "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° ҃ ГиБ ĐēĐžŅ˜Đ° ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Đēада ĐŊĐĩĐŧа ĐŋĐžŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°ŅšĐ° (҃ĐŊĐĩŅĐ¸Ņ‚Đĩ 0 Са ĐŊĐĩĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ҃ ĐēĐ˛ĐžŅ‚Ņƒ).", + "oauth_timeout": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ĐˇĐ°Ņ…Ņ‚Đĩва", + "oauth_timeout_description": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ Са ĐˇĐ°Ņ…Ņ‚ĐĩвĐĩ ҃ ĐŧиĐģĐ¸ŅĐĩĐē҃ĐŊдаĐŧа", "offline_paths": "ВаĐŊĐŧŅ€ĐĩĐļĐŊĐĩ ĐŋŅƒŅ‚Đ°ŅšĐĩ", "offline_paths_description": "Ови Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đ° Ņ€ŅƒŅ‡ĐŊĐžĐŗ ĐąŅ€Đ¸ŅĐ°ŅšĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ Đ´ĐĩĐž ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", - "password_enable_description": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐŧĐžŅ›Ņƒ Đĩ-ĐŋĐžŅˆŅ‚Đĩ и ĐģОСиĐŊĐēĐĩ", + "password_enable_description": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ Đĩ-ĐŋĐžŅˆŅ‚Đĩ и ĐģОСиĐŊĐēĐĩ", "password_settings": "ЛозиĐŊĐēа Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", "password_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ĐģОСиĐŊĐēĐžĐŧ", "paths_validated_successfully": "ХвĐĩ ĐŋŅƒŅ‚Đ°ŅšĐĩ ҁ҃ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžŅ‚Đ˛Ņ€Ņ’ĐĩĐŊĐĩ", - "person_cleanup_job": "Đ§Đ¸ŅˆŅ›ĐĩҚĐĩ ĐžŅĐžĐąĐ°", + "person_cleanup_job": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ ĐžŅĐžĐąĐ°", "quota_size_gib": "ВĐĩĐģĐ¸Ņ‡Đ¸ĐŊа ĐēĐ˛ĐžŅ‚Đĩ (ГиБ)", "refreshing_all_libraries": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ŅĐ˛Đ¸Ņ… йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "registration": "Đ ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ° адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "registration_description": "ĐŸĐžŅˆŅ‚Đž ҁ҂Đĩ ĐŋŅ€Đ˛Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐē ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧ҃, ĐąĐ¸Ņ›ĐĩŅ‚Đĩ дОдĐĩŅ™ĐĩĐŊи ĐēаО АдĐŧиĐŊ и ĐžĐ´ĐŗĐžĐ˛ĐžŅ€ĐŊи ҁ҂Đĩ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊĐĩ ĐˇĐ°Đ´Đ°Ņ‚ĐēĐĩ, а Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ Ņ›ĐĩŅ‚Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ ви.", + "registration_description": "ĐŸĐžŅˆŅ‚Đž ҁ҂Đĩ ĐŋŅ€Đ˛Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐē ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧ҃, ĐąĐ¸Ņ†ĖĐĩŅ‚Đĩ дОдĐĩŅ™ĐĩĐŊи ĐēаО АдĐŧиĐŊ и ĐžĐ´ĐŗĐžĐ˛ĐžŅ€ĐŊи ҁ҂Đĩ Са адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊĐĩ ĐˇĐ°Đ´Đ°Ņ‚ĐēĐĩ, а Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ҆ˁĐĩŅ‚Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ ви.", "repair_all": "ПоĐŋŅ€Đ°Đ˛Đ¸ ŅĐ˛Đĩ", "repair_matched_items": "ПоĐēĐģаĐŋа ҁĐĩ ŅĐ° {count, plural, one {1 ŅŅ‚Đ°Đ˛ĐēĐžĐŧ} few {# ŅŅ‚Đ°Đ˛ĐēĐĩ} other {# ŅŅ‚Đ°Đ˛Đēи}}", "repaired_items": "{count, plural, one {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа 1 ŅŅ‚Đ°Đ˛Đēа} few {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊĐĩ # ŅŅ‚Đ°Đ˛ĐēĐĩ} other {ПоĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊĐĩ # ŅŅ‚Đ°Đ˛Đēи}}", @@ -233,9 +230,9 @@ "search_jobs": "ĐĸŅ€Đ°Đļи ĐŋĐžŅĐģОвĐĩâ€Ļ", "send_welcome_email": "ĐŸĐžŅˆĐ°Ņ™Đ¸Ņ‚Đĩ Đĩ-ĐŋĐžŅˆŅ‚Ņƒ Đ´ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģĐ¸Ņ†Đĩ", "server_external_domain_settings": "ЕĐēҁ҂ĐĩŅ€ĐŊи Đ´ĐžĐŧаиĐŊ", - "server_external_domain_settings_description": "ДоĐŧаиĐŊ Са Ņ˜Đ°Đ˛ĐŊĐĩ Đ´ĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ›Đ¸ http(s)://", + "server_external_domain_settings_description": "ДоĐŧаиĐŊ Са Ņ˜Đ°Đ˛ĐŊĐĩ Đ´ĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ, ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ†ĖĐ¸ ҅҂҂Đŋ(ҁ)://", "server_public_users": "ЈавĐŊи ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸", - "server_public_users_description": "Хви ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ (иĐŧĐĩ и Đ°Đ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ) ҁ҃ ĐŊавĐĩĐ´ĐĩĐŊи ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҃ Đ´ĐĩŅ™ĐĩĐŊĐĩ аĐģĐąŅƒĐŧĐĩ. Када ҘĐĩ ĐžĐŊĐĩĐŧĐžĐŗŅƒŅ›ĐĩĐŊа, ĐģĐ¸ŅŅ‚Đ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋĐŊа ŅĐ°ĐŧĐž адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ¸Đŧа.", + "server_public_users_description": "Хви ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ (иĐŧĐĩ и Đ°Đ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ) ҁ҃ ĐŊавĐĩĐ´ĐĩĐŊи ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҃ Đ´ĐĩŅ™ĐĩĐŊĐĩ аĐģĐąŅƒĐŧĐĩ. Када ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа, ĐģĐ¸ŅŅ‚Đ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋĐŊа ŅĐ°ĐŧĐž адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ¸Đŧа.", "server_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_welcome_message": "ĐŸĐžŅ€ŅƒĐēа Đ´ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģĐ¸Ņ†Đĩ", @@ -246,24 +243,24 @@ "smart_search_job_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ ĐŊа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēаĐŧа да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐ´Ņ€ĐļаĐģи ĐŋаĐŧĐĩŅ‚ĐŊ҃ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", "storage_template_date_time_description": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēа ОСĐŊаĐēа ĐēŅ€ĐĩĐ¸Ņ€Đ°ŅšĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Са иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž Đ´Đ°Ņ‚ŅƒĐŧ҃ и Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃", "storage_template_date_time_sample": "ĐŸŅ€Đ¸ĐŧĐĩŅ€ Đ˛Ņ€ĐĩĐŧĐĩĐŊа {date}", - "storage_template_enable_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸ ĐŧĐĩŅ…Đ°ĐŊиСаĐŧ Са ŅˆĐ°ĐąĐģone Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", + "storage_template_enable_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ ĐŧĐĩŅ…Đ°ĐŊиСаĐŧ Са ŅˆĐ°ĐąĐģone Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", "storage_template_hash_verification_enabled": "ĐĨĐĩ҈ вĐĩŅ€Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊа", - "storage_template_hash_verification_enabled_description": "ОĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ° Ņ…Đĩ҈ вĐĩŅ€Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Ņƒ, ĐŊĐĩ oneĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ°Ņ˜Ņ‚Đĩ ОвО ĐžŅĐ¸Đŧ аĐēĐž ĐŊĐ¸ŅŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи ҃ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đĩ", + "storage_template_hash_verification_enabled_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ° Ņ…Đĩ҈ вĐĩŅ€Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Ņƒ, ĐŊĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ°Ņ˜Ņ‚Đĩ ОвО ĐžŅĐ¸Đŧ аĐēĐž ĐŊĐ¸ŅŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи ҃ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đĩ", "storage_template_migration": "ĐœĐ¸ĐŗŅ€Đ°Ņ†Đ¸Ņ˜Đ° ŅˆĐ°ĐąĐģĐžĐŊа Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", "storage_template_migration_description": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊи {template} ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đĩ", - "storage_template_migration_info": "ШайĐģĐžĐŊ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ Ņ›Đĩ ĐŋŅ€ĐĩŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ŅĐ˛Đĩ ĐĩĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đĩ ҃ ĐŧаĐģа ҁĐģОва. ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ŅˆĐ°ĐąĐģĐžĐŊа Ņ›Đĩ ҁĐĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ŅĐ°ĐŧĐž ĐŊа ĐŊОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. Да ĐąĐ¸ŅŅ‚Đĩ Ņ€ĐĩŅ‚Ņ€ĐžĐ°ĐēŅ‚Đ¸Đ˛ĐŊĐž ĐŋŅ€Đ¸ĐŧĐĩĐŊиĐģи ŅˆĐ°ĐąĐģĐžĐŊ ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ, ĐŋĐžĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ {job}.", + "storage_template_migration_info": "ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ŅˆĐ°ĐąĐģĐžĐŊа ҆ˁĐĩ ҁĐĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ŅĐ°ĐŧĐž ĐŊа ĐŊОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. Да ĐąĐ¸ŅŅ‚Đĩ Ņ€ĐĩŅ‚Ņ€ĐžĐ°ĐēŅ‚Đ¸Đ˛ĐŊĐž ĐŋŅ€Đ¸ĐŧĐĩĐŊиĐģи ŅˆĐ°ĐąĐģĐžĐŊ ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ, ĐŋĐžĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ {job}.", "storage_template_migration_job": "ĐŸĐžŅĐ°Đž ĐŧĐ¸ĐŗŅ€Đ°Ņ†Đ¸Ņ˜Đĩ ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đ°", "storage_template_more_details": "За Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ° Đž ĐžĐ˛ĐžŅ˜ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ¸ ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ШайĐģĐžĐŊ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ и ҚĐĩĐŗĐžĐ˛Đĩ иĐŧĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", - "storage_template_onboarding_description": "Када ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊа, Ова Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° Ņ›Đĩ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐžĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ ŅˆĐ°ĐąĐģĐžĐŊа ĐēĐžŅ˜Đ¸ Đ´ĐĩŅ„Đ¸ĐŊĐ¸ŅˆĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐē. Đ—ĐąĐžĐŗ ĐŋŅ€ĐžĐąĐģĐĩĐŧа ŅĐ° ŅŅ‚Đ°ĐąĐ¸ĐģĐŊĐžŅˆŅ›Ņƒ Ова Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊа. За Đ˛Đ¸ŅˆĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Ņƒ.", + "storage_template_onboarding_description": "Када ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа, Ова Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ҆ˁĐĩ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐžĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ ŅˆĐ°ĐąĐģĐžĐŊа ĐēĐžŅ˜Đ¸ Đ´ĐĩŅ„Đ¸ĐŊĐ¸ŅˆĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐē. Đ—ĐąĐžĐŗ ĐŋŅ€ĐžĐąĐģĐĩĐŧа ŅĐ° ŅŅ‚Đ°ĐąĐ¸ĐģĐŊĐžŅˆŅ†ĖŅƒ Ова Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊа. За Đ˛Đ¸ŅˆĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Ņƒ.", "storage_template_path_length": "ĐŸŅ€Đ¸ĐąĐģиĐļĐŊĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ Đ´ŅƒĐļиĐŊĐĩ ĐŋŅƒŅ‚Đ°ŅšĐĩ: {length, number}/{limit, number}", "storage_template_settings": "ШайĐģĐžĐŊ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", "storage_template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžĐŧ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧа и иĐŧĐĩĐŊĐžĐŧ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° Са ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ", "storage_template_user_label": "{label} ҘĐĩ ОСĐŊаĐēа Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "system_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅĐ¸ŅŅ‚ĐĩĐŧа", - "tag_cleanup_job": "Đ§Đ¸ŅˆŅ›ĐĩҚĐĩ ОСĐŊаĐēа (tags)", - "template_email_available_tags": "МоĐļĐĩŅ‚Đĩ да ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ҁĐģĐĩĐ´ĐĩŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊŅ™Đ¸Đ˛Đĩ ҃ ŅĐ˛ĐžĐŧ ŅˆĐ°ĐąĐģĐžĐŊ҃: {tags}", - "template_email_if_empty": "АĐēĐž ҘĐĩ ŅˆĐ°ĐąĐģĐžĐŊ ĐŋŅ€Đ°ĐˇĐ°ĐŊ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ›Đĩ ҁĐĩ ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа Đ°Đ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ.", - "template_email_invite_album": "ШайĐģĐžĐŊ аĐģĐąŅƒĐŧа ĐŋОСива", + "tag_cleanup_job": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ ОСĐŊаĐēа (tags)", + "template_email_available_tags": "МоĐļĐĩŅ‚Đĩ да ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ҁĐģĐĩĐ´Đĩ҆ˁĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊŅ™Đ¸Đ˛Đĩ ҃ ŅĐ˛ĐžĐŧ ŅˆĐ°ĐąĐģĐžĐŊ҃: {tags}", + "template_email_if_empty": "АĐēĐž ҘĐĩ ŅˆĐ°ĐąĐģĐžĐŊ ĐŋŅ€Đ°ĐˇĐ°ĐŊ, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ†ĖĐĩ ҁĐĩ ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа Đ°Đ´Ņ€ĐĩŅĐ° Đĩ-ĐŋĐžŅˆŅ‚Đĩ.", + "template_email_invite_album": "ШайĐģĐžĐŊ Са ĐŋОСив ҃ аĐģĐąŅƒĐŧ", "template_email_preview": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "template_email_settings": "ШайĐģĐžĐŊи Đĩ-ĐŋĐžŅˆŅ‚Đĩ", "template_email_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊиĐŧ ŅˆĐ°ĐąĐģĐžĐŊиĐŧа ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", @@ -271,99 +268,100 @@ "template_email_welcome": "ШайĐģĐžĐŊ Đĩ-ĐŋĐžŅˆŅ‚Đĩ Đ´ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģĐ¸Ņ†Đĩ", "template_settings": "ШайĐģĐžĐŊи ОйавĐĩŅˆŅ‚ĐĩŅšĐ°", "template_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊиĐŧ ŅˆĐ°ĐąĐģĐžĐŊиĐŧа Са ОйавĐĩŅˆŅ‚ĐĩŅšĐ°.", - "theme_custom_css_settings": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи CSS", - "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊи ĐģĐ¸ŅŅ‚ĐžĐ˛Đ¸ ŅŅ‚Đ¸ĐģОва (CSS) ĐžĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ°Ņ˜Ņƒ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩ Đ´Đ¸ĐˇĐ°Ņ˜ĐŊа Immich-a.", + "theme_custom_css_settings": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи ĐĻĐĄĐĄ", + "theme_custom_css_settings_description": "ĐšĐ°ŅĐēадĐŊи ĐģĐ¸ŅŅ‚ĐžĐ˛Đ¸ ŅŅ‚Đ¸ĐģОва (ĐĻĐĄĐĄ) ĐžĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ°Ņ˜Ņƒ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩ Đ´Đ¸ĐˇĐ°Ņ˜ĐŊа Immich-a.", "theme_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ Ņ‚ĐĩĐŧа", - "theme_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩĐŧ Immich web иĐŊŅ‚ĐĩҀ҄ĐĩŅ˜ŅĐ°", + "theme_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’Đ°Đ˛Đ°ŅšĐĩĐŧ Immich wĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩŅ˜ŅĐ°", "these_files_matched_by_checksum": "ОвиĐŧ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēаĐŧа ҁĐĩ ĐŋĐžĐ´ŅƒĐ´Đ°Ņ€Đ°Ņ˜Ņƒ ŅšĐ¸Ņ…ĐžĐ˛Đ¸ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊи-ĐˇĐąĐ¸Ņ€ĐžĐ˛Đ¸", "thumbnail_generation_job": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ", - "thumbnail_generation_job_description": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ вĐĩĐģиĐēĐĩ, ĐŧаĐģĐĩ и СаĐŧŅƒŅ›ĐĩĐŊĐĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°ĐēĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ĐēаО и ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°Đē҃ ĐžŅĐžĐąŅƒ", + "thumbnail_generation_job_description": "ГĐĩĐŊĐĩŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ вĐĩĐģиĐēĐĩ, ĐŧаĐģĐĩ и СаĐŧŅƒŅ†ĖĐĩĐŊĐĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°ĐēĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ĐēаО и ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Са ŅĐ˛Đ°Đē҃ ĐžŅĐžĐąŅƒ", "transcoding_acceleration_api": "АПИ Са ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ", - "transcoding_acceleration_api_description": "АПИ ĐēĐžŅ˜Đ¸ Ņ›Đĩ ĐēĐžĐŧ҃ĐŊĐ¸Ņ†Đ¸Ņ€Đ°Ņ‚Đ¸ ŅĐ° Đ˛Đ°ŅˆĐ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜ĐĩĐŧ да йи ŅƒĐąŅ€ĐˇĐ°Đž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. Ово ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ҘĐĩ 'ĐŊĐ°Ņ˜ĐąĐžŅ™Đ¸ ĐŊаĐŋĐžŅ€': vracˁa se na softversko transkodiranje u slučaju neuspeha. VP9 moÅže ili ne mora da radi u zavisnosti od vaÅĄeg hardvera.", - "transcoding_acceleration_nvenc": "НВЕНĐĻ (ĐˇĐ°Ņ…Ņ‚Đĩва NVIDIA Đ“ĐŸĐŖ)", - "transcoding_acceleration_qsv": "Quick Sync (ĐˇĐ°Ņ…Ņ‚Đĩва ИĐŊŅ‚ĐĩĐģ CPU 7. ĐŗĐĩĐŊĐĩŅ€Đ°Ņ†Đ¸Ņ˜Đĩ иĐģи ĐŊĐžĐ˛Đ¸Ņ˜Đ¸)", - "transcoding_acceleration_rkmpp": "RKMPP (ŅĐ°ĐŧĐž ĐŊа Rockchip СОĐĻ-ОвиĐŧа)", + "transcoding_acceleration_api_description": "АПИ ĐēĐžŅ˜Đ¸ ҆ˁĐĩ ĐēĐžĐŧ҃ĐŊĐ¸Ņ†Đ¸Ņ€Đ°Ņ‚Đ¸ ŅĐ° Đ˛Đ°ŅˆĐ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜ĐĩĐŧ да йи ŅƒĐąŅ€ĐˇĐ°Đž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. Ово ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ҘĐĩ 'ĐŊĐ°Ņ˜ĐąĐžŅ™Đ¸ ĐŊаĐŋĐžŅ€': Đ˛Ņ€Đ°Ņ†ĖĐ° ҁĐĩ ĐŊа ŅĐžŅ„Ņ‚Đ˛ĐĩҀҁĐēĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ ҃ ҁĐģŅƒŅ‡Đ°Ņ˜Ņƒ ĐŊĐĩ҃ҁĐŋĐĩŅ…Đ°. VP9 ĐŧĐžĐļĐĩ иĐģи ĐŊĐĩ ĐŧĐžŅ€Đ° да Ņ€Đ°Đ´Đ¸ ҃ ĐˇĐ°Đ˛Đ¸ŅĐŊĐžŅŅ‚Đ¸ Од Đ˛Đ°ŅˆĐĩĐŗ Ņ…Đ°Ņ€Đ´Đ˛ĐĩŅ€Đ°.", + "transcoding_acceleration_nvenc": "НВЕНĐĻ (ĐˇĐ°Ņ…Ņ‚Đĩва НВИДИА Đ“ĐŸĐŖ)", + "transcoding_acceleration_qsv": "QŅƒĐ¸Ņ†Đē ĐĄyĐŊ҆ (ĐˇĐ°Ņ…Ņ‚Đĩва ИĐŊŅ‚ĐĩĐģ CPU 7. ĐŗĐĩĐŊĐĩŅ€Đ°Ņ†Đ¸Ņ˜Đĩ иĐģи ĐŊĐžĐ˛Đ¸Ņ˜Đ¸)", + "transcoding_acceleration_rkmpp": "РКМПП (ŅĐ°ĐŧĐž ĐŊа Đ ĐžŅ†ĐēŅ†Ņ…Đ¸Đŋ СОĐĻ-ОвиĐŧа)", "transcoding_acceleration_vaapi": "ВидĐĩĐž аĐē҆ĐĩĐģĐĩŅ€Đ°Ņ†Đ¸Ņ˜Đ° АПИ (ВААПИ)", - "transcoding_accepted_audio_codecs": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊи Đ°ŅƒĐ´Đ¸Đž ĐēОдĐĩŅ†Đ¸", + "transcoding_accepted_audio_codecs": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊи Đ°ŅƒĐ´Đ¸Đž ĐēОдĐĩŅ†Đ¸", "transcoding_accepted_audio_codecs_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐēĐžŅ˜Đĩ Đ°ŅƒĐ´Đ¸Đž ĐēОдĐĩĐēĐĩ ĐŊĐĩ ҂ҀĐĩйа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ‚Đ¸. ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ ŅĐ°ĐŧĐž Са ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐĩ ĐŋĐžĐģĐ¸Ņ‚Đ¸ĐēĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°.", - "transcoding_accepted_containers": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊи ĐēĐžĐŊŅ‚ĐĩҘĐŊĐĩŅ€Đ¸", - "transcoding_accepted_containers_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐēĐžŅ˜Đ¸ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đ¸ ĐēĐžĐŊŅ‚ĐĩҘĐŊĐĩŅ€Đ° ĐŊĐĩ ĐŧĐžŅ€Đ°Ņ˜Ņƒ да ҁĐĩ Ņ€ĐĩĐŧ҃Đēҁ҃Ҙ҃ ҃ МP4. ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ ŅĐ°ĐŧĐž Са ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐĩ ҃ҁĐģОвĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°.", - "transcoding_accepted_video_codecs": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊи видĐĩĐž ĐēОдĐĩŅ†Đ¸", + "transcoding_accepted_containers": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊи ĐēĐžĐŊŅ‚ĐĩҘĐŊĐĩŅ€Đ¸", + "transcoding_accepted_containers_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐēĐžŅ˜Đ¸ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đ¸ ĐēĐžĐŊŅ‚ĐĩҘĐŊĐĩŅ€Đ° ĐŊĐĩ ĐŧĐžŅ€Đ°Ņ˜Ņƒ да ҁĐĩ Ņ€ĐĩĐŧ҃Đēҁ҃Ҙ҃ ҃ МП4. ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ ŅĐ°ĐŧĐž Са ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐĩ ҃ҁĐģОвĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°.", + "transcoding_accepted_video_codecs": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊи видĐĩĐž ĐēОдĐĩŅ†Đ¸", "transcoding_accepted_video_codecs_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐēĐžŅ˜Đĩ видĐĩĐž ĐēОдĐĩĐēĐĩ ĐŊĐ¸Ņ˜Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ‚Đ¸. ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ ŅĐ°ĐŧĐž Са ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐĩ ĐŋĐžĐģĐ¸Ņ‚Đ¸ĐēĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°.", - "transcoding_advanced_options_description": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐēĐžŅ˜Đĩ вĐĩŅ›Đ¸ĐŊа ĐēĐžŅ€Đ¸ŅĐŊиĐēа ĐŊĐĩ йи ҂ҀĐĩйаĐģĐž да ĐŧĐĩŅšĐ°Ņ˜Ņƒ", + "transcoding_advanced_options_description": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐēĐžŅ˜Đĩ вĐĩŅ†ĖĐ¸ĐŊа ĐēĐžŅ€Đ¸ŅĐŊиĐēа ĐŊĐĩ йи ҂ҀĐĩйаĐģĐž да ĐŧĐĩŅšĐ°Ņ˜Ņƒ", "transcoding_audio_codec": "ĐŅƒĐ´Đ¸Đž ĐēОдĐĩĐē", "transcoding_audio_codec_description": "ОĐŋ҃ҁ ҘĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đ° ĐŊĐ°Ņ˜Đ˛Đ¸ŅˆĐĩĐŗ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ°, аĐģи иĐŧа ĐģĐžŅˆĐ¸Ņ˜Ņƒ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚ ŅĐ° ŅŅ‚Đ°Ņ€Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа иĐģи ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€ĐžĐŧ.", - "transcoding_bitrate_description": "ВидĐĩĐž ҁĐŊиĐŧŅ†Đ¸ вĐĩŅ›Đ¸ Од ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐĩ ĐąŅ€ĐˇĐ¸ĐŊĐĩ ĐŋŅ€ĐĩĐŊĐžŅĐ° иĐģи ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", - "transcoding_codecs_learn_more": "Да ĐąĐ¸ŅŅ‚Đĩ ŅĐ°ĐˇĐŊаĐģи Đ˛Đ¸ŅˆĐĩ Đž Ņ‚ĐĩŅ€ĐŧиĐŊĐžĐģĐžĐŗĐ¸Ņ˜Đ¸ ĐēĐžŅ˜Đ° ҁĐĩ ОвдĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸, ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ FFmpeg Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Ņƒ Са H.264 ĐēОдĐĩĐē, HEVC ĐēОдĐĩĐē и VP9 ĐēОдĐĩĐē.", + "transcoding_bitrate_description": "ВидĐĩĐž ҁĐŊиĐŧŅ†Đ¸ вĐĩŅ†ĖĐ¸ Од ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐĩ ĐąŅ€ĐˇĐ¸ĐŊĐĩ ĐŋŅ€ĐĩĐŊĐžŅĐ° иĐģи ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", + "transcoding_codecs_learn_more": "Да ĐąĐ¸ŅŅ‚Đĩ ŅĐ°ĐˇĐŊаĐģи Đ˛Đ¸ŅˆĐĩ Đž Ņ‚ĐĩŅ€ĐŧиĐŊĐžĐģĐžĐŗĐ¸Ņ˜Đ¸ ĐēĐžŅ˜Đ° ҁĐĩ ОвдĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸, ĐŋĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ФФĐŧĐŋĐĩĐŗ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Ņƒ Са H.264 ĐēОдĐĩĐē, HEVC ĐēОдĐĩĐē и VP9 ĐēОдĐĩĐē.", "transcoding_constant_quality_mode": "Đ ĐĩĐļиĐŧ ĐēĐžĐŊŅŅ‚Đ°ĐŊŅ‚ĐŊĐžĐŗ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ°", - "transcoding_constant_quality_mode_description": "ICQ ҘĐĩ ĐąĐžŅ™Đ¸ Од CQP-a, аĐģи ĐŊĐĩĐēи ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸ Са Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ ĐŊĐĩ ĐŋĐžĐ´Ņ€ĐļĐ°Đ˛Đ°Ņ˜Ņƒ ĐžĐ˛Đ°Ņ˜ Ņ€ĐĩĐļиĐŧ. ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ОвĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ Ņ›Đĩ ĐŋŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŊавĐĩĐ´ĐĩĐŊи Ņ€ĐĩĐļиĐŧ Đēада ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ ĐˇĐ°ŅĐŊОваĐŊĐž ĐŊа ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Ņƒ. НВЕНĐĻ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ҘĐĩŅ€ ĐŊĐĩ ĐŋĐžĐ´Ņ€Đļава ICQ.", + "transcoding_constant_quality_mode_description": "ИĐĻQ ҘĐĩ ĐąĐžŅ™Đ¸ Од ĐĻQП-а, аĐģи ĐŊĐĩĐēи ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸ Са Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ ĐŊĐĩ ĐŋĐžĐ´Ņ€ĐļĐ°Đ˛Đ°Ņ˜Ņƒ ĐžĐ˛Đ°Ņ˜ Ņ€ĐĩĐļиĐŧ. ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ОвĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ ҆ˁĐĩ ĐŋŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŊавĐĩĐ´ĐĩĐŊи Ņ€ĐĩĐļиĐŧ Đēада ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ ĐˇĐ°ŅĐŊОваĐŊĐž ĐŊа ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Ņƒ. НВЕНĐĻ Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ҘĐĩŅ€ ĐŊĐĩ ĐŋĐžĐ´Ņ€Đļава ИĐĻQ.", "transcoding_constant_rate_factor": "ФаĐēŅ‚ĐžŅ€ ĐēĐžĐŊŅŅ‚Đ°ĐŊŅ‚ĐŊĐĩ ŅŅ‚ĐžĐŋĐĩ (-҆Ҁ҄)", - "transcoding_constant_rate_factor_description": "Ниво ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ° видĐĩа. ĐĸиĐŋĐ¸Ņ‡ĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҁ҃ 23 Са ĐĨ.264, 28 Са ĐĨЕВĐĻ, 31 Са ВП9 и 35 Са АВ1. НиĐļĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", + "transcoding_constant_rate_factor_description": "Ниво ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ° видĐĩа. ĐĸиĐŋĐ¸Ņ‡ĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҁ҃ 23 Са H.264, 28 Са HEVC, 31 Са VP9 и 35 Са АВ1. НиĐļĐĩ ҘĐĩ ĐąĐžŅ™Đĩ, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ вĐĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ.", "transcoding_disabled_description": "НĐĩĐŧĐžŅ˜Ņ‚Đĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŊĐ¸Ņ˜ĐĩдаĐŊ видĐĩĐž, ĐŧĐžĐļĐĩ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸ Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅ†Đ¸Ņ˜Ņƒ ĐŊа ĐŊĐĩĐēиĐŧ ĐēĐģĐ¸Ņ˜ĐĩĐŊŅ‚Đ¸Đŧа", "transcoding_encoding_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐšĐžĐ´Đ¸Ņ€Đ°ŅšĐ°", "transcoding_encoding_options_description": "ПодĐĩŅĐ¸Ņ‚Đĩ ĐēОдĐĩĐēĐĩ, Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Ņƒ, ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ и Đ´Ņ€ŅƒĐŗĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ Са ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ", "transcoding_hardware_acceleration": "ĐĨĐ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ", - "transcoding_hardware_acceleration_description": "ЕĐēĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž; ĐŧĐŊĐžĐŗĐž ĐąŅ€ĐļĐĩ, аĐģи Ņ›Đĩ иĐŧĐ°Ņ‚Đ¸ ĐŊиĐļи ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€Đ¸ Đ¸ŅŅ‚ĐžŅ˜ ĐąŅ€ĐˇĐ¸ĐŊи ĐŋŅ€ĐĩĐŊĐžŅĐ°", + "transcoding_hardware_acceleration_description": "ЕĐēĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž; ĐŧĐŊĐžĐŗĐž ĐąŅ€ĐļĐĩ, аĐģи ҆ˁĐĩ иĐŧĐ°Ņ‚Đ¸ ĐŊиĐļи ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€Đ¸ Đ¸ŅŅ‚ĐžŅ˜ ĐąŅ€ĐˇĐ¸ĐŊи ĐŋŅ€ĐĩĐŊĐžŅĐ°", "transcoding_hardware_decoding": "ĐĨĐ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž Đ´ĐĩĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", - "transcoding_hardware_decoding_setting_description": "ОĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ° ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ Од ĐēŅ€Đ°Ņ˜Đ° Đ´Đž ĐēŅ€Đ°Ņ˜Đ° ҃ĐŧĐĩŅŅ‚Đž да ŅĐ°ĐŧĐž ŅƒĐąŅ€ĐˇĐ°Đ˛Đ° ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐĩŅ›Đĩ Ņ€Đ°Đ´Đ¸Ņ‚Đ¸ ĐŊа ŅĐ˛Đ¸Đŧ видĐĩĐž ҁĐŊиĐŧŅ†Đ¸Đŧа.", - "transcoding_hevc_codec": "ĐĨЕВĐĻ ĐēОдĐĩĐē", + "transcoding_hardware_decoding_setting_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ° ŅƒĐąŅ€ĐˇĐ°ŅšĐĩ Од ĐēŅ€Đ°Ņ˜Đ° Đ´Đž ĐēŅ€Đ°Ņ˜Đ° ҃ĐŧĐĩŅŅ‚Đž да ŅĐ°ĐŧĐž ŅƒĐąŅ€ĐˇĐ°Đ˛Đ° ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐĩ҆ˁĐĩ Ņ€Đ°Đ´Đ¸Ņ‚Đ¸ ĐŊа ŅĐ˛Đ¸Đŧ видĐĩĐž ҁĐŊиĐŧŅ†Đ¸Đŧа.", + "transcoding_hevc_codec": "HEVC ĐēОдĐĩĐē", "transcoding_max_b_frames": "МаĐēŅĐ¸ĐŧаĐģĐŊи Б-ĐēĐ°Đ´Ņ€Đ¸", - "transcoding_max_b_frames_description": "Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐąĐžŅ™ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ, аĐģи ҃ҁĐŋĐžŅ€Đ°Đ˛Đ°Ņ˜Ņƒ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐ¸Ņ˜Đĩ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐž ŅĐ° Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēиĐŧ ŅƒĐąŅ€ĐˇĐ°ŅšĐĩĐŧ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа. 0 oneĐŧĐžĐŗŅƒŅ›Đ°Đ˛Đ° Б-ĐēĐ°Đ´Ņ€Đĩ, Đ´ĐžĐē -1 Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐžĐ˛Ņƒ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚.", + "transcoding_max_b_frames_description": "Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐąĐžŅ™ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ, аĐģи ҃ҁĐŋĐžŅ€Đ°Đ˛Đ°Ņ˜Ņƒ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļда ĐŊĐ¸Ņ˜Đĩ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐž ŅĐ° Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēиĐŧ ŅƒĐąŅ€ĐˇĐ°ŅšĐĩĐŧ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа. 0 oneĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ° Б-ĐēĐ°Đ´Ņ€Đĩ, Đ´ĐžĐē -1 Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐžĐ˛Ņƒ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚.", "transcoding_max_bitrate": "МаĐēŅĐ¸ĐŧаĐģĐŊи ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ", - "transcoding_max_bitrate_description": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžĐŗ ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ-а ĐŧĐžĐļĐĩ ŅƒŅ‡Đ¸ĐŊĐ¸Ņ‚Đ¸ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŋŅ€ĐĩĐ´Đ˛Đ¸Đ´Ņ™Đ¸Đ˛Đ¸Ņ˜Đ¸Đŧ ŅƒĐˇ ĐŧĐ°ŅšŅƒ ҆ĐĩĐŊ҃ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ°. ĐŸŅ€Đ¸ 720Đŋ, Ņ‚Đ¸ĐŋĐ¸Ņ‡ĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҁ҃ 2600kbit/s Са ВП9 иĐģи ĐĨЕВĐĻ, иĐģи 4500kbit/s Са ĐĨ.264. oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž аĐēĐž ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊĐž ĐŊа 0.", - "transcoding_max_keyframe_interval": "МаĐēŅĐ¸ĐŧаĐģĐŊи иĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ keyframe-a", + "transcoding_max_bitrate_description": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžĐŗ ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ-а ĐŧĐžĐļĐĩ ŅƒŅ‡Đ¸ĐŊĐ¸Ņ‚Đ¸ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŋŅ€ĐĩĐ´Đ˛Đ¸Đ´Ņ™Đ¸Đ˛Đ¸Ņ˜Đ¸Đŧ ŅƒĐˇ ĐŧĐ°ŅšŅƒ ҆ĐĩĐŊ҃ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Đ°. ĐŸŅ€Đ¸ 720Đŋ, Ņ‚Đ¸ĐŋĐ¸Ņ‡ĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ҁ҃ 2600Đē Са VP9 иĐģи HEVC, иĐģи 4500Đē Са H.264. ОĐŊĐĩĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž аĐēĐž ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊĐž ĐŊа 0.", + "transcoding_max_keyframe_interval": "МаĐēŅĐ¸ĐŧаĐģĐŊи иĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ĐēĐĩyŅ„Ņ€Đ°ĐŧĐĩ-а", "transcoding_max_keyframe_interval_description": "ĐŸĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐŧаĐēŅĐ¸ĐŧаĐģĐŊ҃ ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžŅŅ‚ ĐēĐ°Đ´Ņ€ĐžĐ˛Đ° иСĐŧĐĩŅ’Ņƒ ĐēŅ™ŅƒŅ‡ĐŊĐ¸Ņ… ĐēĐ°Đ´Ņ€ĐžĐ˛Đ°. НиĐļĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐŗĐžŅ€ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ, аĐģи ĐŋĐžĐąĐžŅ™ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ Đ˛Ņ€ĐĩĐŧĐĩ Ņ‚Ņ€Đ°ĐļĐĩŅšĐ° и ĐŧĐžĐŗŅƒ ĐŋĐžĐąĐžŅ™ŅˆĐ°Ņ‚Đ¸ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ҁ҆ĐĩĐŊа ŅĐ° ĐąŅ€ĐˇĐ¸Đŧ ĐēŅ€ĐĩŅ‚Đ°ŅšĐĩĐŧ. 0 Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐžĐ˛Ņƒ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚.", - "transcoding_optimal_description": "ВидĐĩĐž ҁĐŊиĐŧŅ†Đ¸ вĐĩŅ›Đ¸ Од Ņ†Đ¸Ņ™ĐŊĐĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ иĐģи ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", + "transcoding_optimal_description": "ВидĐĩĐž ҁĐŊиĐŧŅ†Đ¸ вĐĩŅ†ĖĐ¸ Од Ņ†Đ¸Ņ™ĐŊĐĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ иĐģи ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", "transcoding_policy": "ĐŖŅĐģОви ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°", "transcoding_policy_description": "ĐžĐ´Ņ€Đĩди Đēад да ҁĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ° видĐĩĐž", "transcoding_preferred_hardware_device": "ЖĐĩŅ™ĐĩĐŊи Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēи ŅƒŅ€ĐĩŅ’Đ°Ņ˜", - "transcoding_preferred_hardware_device_description": "ОдĐŊĐžŅĐ¸ ҁĐĩ ŅĐ°ĐŧĐž ĐŊа ВААПИ и QSV. ĐŸĐžŅŅ‚Đ°Đ˛Ņ™Đ° Đ´Ņ€Đ¸ ĐŊОдĐĩ ĐēĐžŅ˜Đ¸ ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Са Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ.", + "transcoding_preferred_hardware_device_description": "ОдĐŊĐžŅĐ¸ ҁĐĩ ŅĐ°ĐŧĐž ĐŊа ВААПИ и QСВ. ĐŸĐžŅŅ‚Đ°Đ˛Ņ™Đ° Đ´Ņ€Đ¸ ĐŊОдĐĩ ĐēĐžŅ˜Đ¸ ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ Са Ņ…Đ°Ņ€Đ´Đ˛ĐĩҀҁĐēĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ.", "transcoding_preset_preset": "ĐŖĐŊаĐŋŅ€ĐĩĐ´ ĐŋОдĐĩ҈ĐĩĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° (-ĐŋŅ€ĐĩҁĐĩŅ‚)", - "transcoding_preset_preset_description": "Đ‘Ņ€ĐˇĐ¸ĐŊа ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ. ĐĄĐŋĐžŅ€Đ¸Ņ˜Đĩ ҃ĐŊаĐŋŅ€ĐĩĐ´ ĐŋОдĐĩ҈ĐĩĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đĩ ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŋОвĐĩŅ›Đ°Đ˛Đ°Ņ˜Ņƒ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ Đēада Ņ†Đ¸Ņ™Đ°Ņ‚Đĩ ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊ҃ ĐąŅ€ĐˇĐ¸ĐŊ҃ ĐŋŅ€ĐĩĐŊĐžŅĐ°. ВП9 Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ĐąŅ€ĐˇĐ¸ĐŊĐĩ иСĐŊад 'ĐąŅ€ĐļĐĩ'.", + "transcoding_preset_preset_description": "Đ‘Ņ€ĐˇĐ¸ĐŊа ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ. ĐĄĐŋĐžŅ€Đ¸Ņ˜Đĩ ҃ĐŊаĐŋŅ€ĐĩĐ´ ĐŋОдĐĩ҈ĐĩĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đĩ ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŋОвĐĩŅ†ĖĐ°Đ˛Đ°Ņ˜Ņƒ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ Đēада Ņ†Đ¸Ņ™Đ°Ņ‚Đĩ ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊ҃ ĐąŅ€ĐˇĐ¸ĐŊ҃ ĐŋŅ€ĐĩĐŊĐžŅĐ°. VP9 Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ĐąŅ€ĐˇĐ¸ĐŊĐĩ иСĐŊад 'ĐąŅ€ĐļĐĩ'.", "transcoding_reference_frames": "Đ ĐĩŅ„ĐĩŅ€ĐĩĐŊŅ‚ĐŊи ĐžĐēĐ˛Đ¸Ņ€Đ¸ (Ņ„Ņ€Đ°ĐŧĐĩҁ)", "transcoding_reference_frames_description": "Đ‘Ņ€ĐžŅ˜ ĐžĐēĐ˛Đ¸Ņ€Đ° (Ņ„Ņ€Đ°ĐŧĐĩҁ) Са Ņ€ĐĩŅ„ĐĩŅ€ĐĩĐŊŅ†Ņƒ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ Đ´Đ°Ņ‚ĐžĐŗ ĐžĐēĐ˛Đ¸Ņ€Đ°. Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ ĐŋĐžĐąĐžŅ™ŅˆĐ°Đ˛Đ°Ņ˜Ņƒ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ ĐēĐžĐŧĐŋŅ€ĐĩŅĐ¸Ņ˜Đĩ, аĐģи ҃ҁĐŋĐžŅ€Đ°Đ˛Đ°Ņ˜Ņƒ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. 0 Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Ņ™Đ° ĐžĐ˛Ņƒ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚.", - "transcoding_required_description": "ХаĐŧĐž видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ ĐēĐžŅ˜Đ¸ ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ›ĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", + "transcoding_required_description": "ХаĐŧĐž видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ ĐēĐžŅ˜Đ¸ ĐŊĐ¸ŅŅƒ ҃ ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ†ĖĐĩĐŊĐžĐŧ Ņ„ĐžŅ€ĐŧĐ°Ņ‚Ņƒ", "transcoding_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° видĐĩĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°", - "transcoding_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐēĐžŅ˜Đĩ видĐĩĐž ҁĐŊиĐŧĐēĐĩ ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´ŅƒŅ˜ĐĩŅ‚Đĩ и ĐēаĐēĐž Đ¸Ņ… ĐžĐąŅ€Đ°Đ´Đ¸Ņ‚Đ¸", + "transcoding_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜ĐžĐŧ и иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ°Đŧа Đž ĐēĐžĐ´Đ¸Ņ€Đ°ŅšŅƒ видĐĩĐž Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "transcoding_target_resolution": "ĐĻĐ¸Ņ™Đ°ĐŊа Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ°", - "transcoding_target_resolution_description": "ВĐĩŅ›Đĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒ да ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°, аĐģи иĐŧ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ, иĐŧĐ°Ņ˜Ņƒ вĐĩŅ›Đĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа и ĐŧĐžĐŗŅƒ да ҁĐŧĐ°ŅšĐĩ ĐąŅ€ĐˇĐ¸ĐŊ҃ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", - "transcoding_temporal_aq": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēи (ĐĸĐĩĐŧĐŋĐžŅ€Đ°Đģ) AQ", - "transcoding_temporal_aq_description": "ОдĐŊĐžŅĐ¸ ҁĐĩ ŅĐ°ĐŧĐž ĐŊа НВЕНĐĻ. ПовĐĩŅ›Đ°Đ˛Đ° ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ҁ҆ĐĩĐŊа ŅĐ° Đ˛Đ¸ŅĐžĐēиĐŧ Đ´ĐĩŅ‚Đ°Ņ™Đ¸Đŧа и ĐŊĐ¸ŅĐēиĐŧ ĐŋĐžĐēŅ€ĐĩŅ‚Đ¸Đŧа. МоĐļда ĐŊĐ¸Ņ˜Đĩ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģаĐŊ ŅĐ° ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа.", - "transcoding_threads": "ĐĐ¸Ņ‚Đ¸ (threads)", - "transcoding_threads_description": "Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ дОвОдĐĩ Đ´Đž ĐąŅ€ĐļĐĩĐŗ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°, аĐģи ĐžŅŅ‚Đ°Đ˛Ņ™Đ°Ņ˜Ņƒ ĐŧĐ°ŅšĐĩ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Са ĐžĐąŅ€Đ°Đ´Ņƒ Đ´Ņ€ŅƒĐŗĐ¸Ņ… ĐˇĐ°Đ´Đ°Ņ‚Đ°Đēа Đ´ĐžĐē ҘĐĩ аĐēŅ‚Đ¸Đ˛Đ°ĐŊ. Ова Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐŊĐĩ йи ҂ҀĐĩйаĐģĐž да ĐąŅƒĐ´Đĩ вĐĩŅ›Đ° Од ĐąŅ€ĐžŅ˜Đ° CPU ҘĐĩĐˇĐŗĐ°Ņ€Đ°. МаĐēŅĐ¸ĐŧĐ¸ĐˇĐ¸Ņ€Đ° Đ¸ŅĐēĐžŅ€Đ¸ŅˆŅ›ĐĩĐŊĐžŅŅ‚ аĐēĐž ҘĐĩ ĐŋОдĐĩ҈ĐĩĐŊĐž ĐŊа 0.", - "transcoding_tone_mapping": "МаĐŋĐ¸Ņ€Đ°ŅšĐĩ (tone-mapping)", + "transcoding_target_resolution_description": "ВĐĩ҆ˁĐĩ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒ да ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜Ņƒ Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°, аĐģи иĐŧ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ, иĐŧĐ°Ņ˜Ņƒ вĐĩ҆ˁĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа и ĐŧĐžĐŗŅƒ да ҁĐŧĐ°ŅšĐĩ ĐąŅ€ĐˇĐ¸ĐŊ҃ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", + "transcoding_temporal_aq": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēи (ĐĸĐĩĐŧĐŋĐžŅ€Đ°Đģ) АQ", + "transcoding_temporal_aq_description": "ОдĐŊĐžŅĐ¸ ҁĐĩ ŅĐ°ĐŧĐž ĐŊа НВЕНĐĻ. ПовĐĩŅ†ĖĐ°Đ˛Đ° ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ҁ҆ĐĩĐŊа ŅĐ° Đ˛Đ¸ŅĐžĐēиĐŧ Đ´ĐĩŅ‚Đ°Ņ™Đ¸Đŧа и ĐŊĐ¸ŅĐēиĐŧ ĐŋĐžĐēŅ€ĐĩŅ‚Đ¸Đŧа. МоĐļда ĐŊĐ¸Ņ˜Đĩ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģаĐŊ ŅĐ° ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа.", + "transcoding_threads": "ĐĐ¸Ņ‚Đ¸ (҂҅ҀĐĩĐ°Đ´Ņ)", + "transcoding_threads_description": "Đ’Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸ дОвОдĐĩ Đ´Đž ĐąŅ€ĐļĐĩĐŗ ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°, аĐģи ĐžŅŅ‚Đ°Đ˛Ņ™Đ°Ņ˜Ņƒ ĐŧĐ°ŅšĐĩ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Са ĐžĐąŅ€Đ°Đ´Ņƒ Đ´Ņ€ŅƒĐŗĐ¸Ņ… ĐˇĐ°Đ´Đ°Ņ‚Đ°Đēа Đ´ĐžĐē ҘĐĩ аĐēŅ‚Đ¸Đ˛Đ°ĐŊ. Ова Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐŊĐĩ йи ҂ҀĐĩйаĐģĐž да ĐąŅƒĐ´Đĩ вĐĩŅ†ĖĐ° Од ĐąŅ€ĐžŅ˜Đ° CPU ҘĐĩĐˇĐŗĐ°Ņ€Đ°. МаĐēŅĐ¸ĐŧĐ¸ĐˇĐ¸Ņ€Đ° Đ¸ŅĐēĐžŅ€Đ¸ŅˆŅ†ĖĐĩĐŊĐžŅŅ‚ аĐēĐž ҘĐĩ ĐŋОдĐĩ҈ĐĩĐŊĐž ĐŊа 0.", + "transcoding_tone_mapping": "МаĐŋĐ¸Ņ€Đ°ŅšĐĩ (Ņ‚one-ĐŧаĐŋĐŋиĐŊĐŗ)", "transcoding_tone_mapping_description": "ПоĐēŅƒŅˆĐ°Đ˛Đ° да ҁĐĩ ŅĐ°Ņ‡ŅƒĐ˛Đ° Đ¸ĐˇĐŗĐģĐĩĐ´ ĐĨДР видĐĩĐž СаĐŋĐ¸ŅĐ° Đēада ҁĐĩ ĐēĐžĐŊвĐĩŅ€Ņ‚ŅƒŅ˜Ņƒ ҃ СДР. ХваĐēи аĐģĐŗĐžŅ€Đ¸Ņ‚Đ°Đŧ ĐŋŅ€Đ°Đ˛Đ¸ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Ņ‚Đĩ ĐēĐžĐŧĐŋŅ€ĐžĐŧĐ¸ŅĐĩ Са ĐąĐžŅ˜Ņƒ, Đ´ĐĩŅ‚Đ°Ņ™Đĩ и ĐžŅĐ˛Đĩ҂ҙĐĩĐŊĐžŅŅ‚. ĐĨайĐģĐĩ Ņ‡ŅƒĐ˛Đ° Đ´ĐĩŅ‚Đ°Ņ™Đĩ, ĐœĐžĐąĐ¸ŅƒŅ Ņ‡ŅƒĐ˛Đ° ĐąĐžŅ˜Ņƒ, а РаĐĩиĐŊŅ…Đ°Ņ€Đ´ ŅĐ˛ĐĩŅ‚ĐģиĐŊ҃.", "transcoding_transcode_policy": "ĐŖŅĐģОви Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐ°", - "transcoding_transcode_policy_description": "ĐŖŅĐģОви Đž Ņ‚ĐžĐŧĐĩ Đēада видĐĩĐž ҂ҀĐĩйа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ‚Đ¸. ĐĨДР видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ Ņ›Đĩ ŅƒĐ˛ĐĩĐē ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ĐŊи (ĐžŅĐ¸Đŧ аĐēĐž ҘĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž).", + "transcoding_transcode_policy_description": "ĐŖŅĐģОви Đž Ņ‚ĐžĐŧĐĩ Đēада видĐĩĐž ҂ҀĐĩйа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ‚Đ¸. ĐĨДР видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ ҆ˁĐĩ ŅƒĐ˛ĐĩĐē ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ĐŊи (ĐžŅĐ¸Đŧ аĐēĐž ҘĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž).", "transcoding_two_pass_encoding": "ДвоĐŋŅ€ĐžĐģаСĐŊĐž ĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ", - "transcoding_two_pass_encoding_setting_description": "ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ҃ два ĐŋŅ€ĐžĐģаСа да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐģи ĐąĐžŅ™Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ. Када ҘĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊа ĐąŅ€ĐˇĐ¸ĐŊа ҃ ĐąĐ¸Ņ‚ĐžĐ˛Đ¸Đŧа ĐžĐŧĐžĐŗŅƒŅ›ĐĩĐŊа (ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊа Са Ņ€Đ°Đ´ ŅĐ° ĐĨ.264 и ĐĨЕВĐĻ), ĐžĐ˛Đ°Ņ˜ Ņ€ĐĩĐļиĐŧ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐžĐŋҁĐĩĐŗ ĐąŅ€ĐˇĐ¸ĐŊĐĩ ҃ ĐąĐ¸Ņ‚ĐžĐ˛Đ¸Đŧа ĐˇĐ°ŅĐŊОваĐŊ ĐŊа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ˜ ĐąŅ€ĐˇĐ¸ĐŊи (max ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ) и Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ĐĻРФ. За ВП9, ĐĻРФ ҁĐĩ ĐŧĐžĐļĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ аĐēĐž ҘĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊа ĐąŅ€ĐˇĐ¸ĐŊа ĐŋŅ€ĐĩĐŊĐžŅĐ° oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊа.", + "transcoding_two_pass_encoding_setting_description": "ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ҃ два ĐŋŅ€ĐžĐģаСа да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐģи ĐąĐžŅ™Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ. Када ҘĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊа ĐąŅ€ĐˇĐ¸ĐŊа ҃ ĐąĐ¸Ņ‚ĐžĐ˛Đ¸Đŧа ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа (ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊа Са Ņ€Đ°Đ´ ŅĐ° H.264 и HEVC), ĐžĐ˛Đ°Ņ˜ Ņ€ĐĩĐļиĐŧ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐžĐŋҁĐĩĐŗ ĐąŅ€ĐˇĐ¸ĐŊĐĩ ҃ ĐąĐ¸Ņ‚ĐžĐ˛Đ¸Đŧа ĐˇĐ°ŅĐŊОваĐŊ ĐŊа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ˜ ĐąŅ€ĐˇĐ¸ĐŊи (Đŧаx ĐąĐ¸Ņ‚Ņ€Đ°Ņ‚Đĩ) и Đ¸ĐŗĐŊĐžŅ€Đ¸ŅˆĐĩ ĐĻРФ. За VP9, ĐĻРФ ҁĐĩ ĐŧĐžĐļĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ аĐēĐž ҘĐĩ ĐŧаĐēŅĐ¸ĐŧаĐģĐŊа ĐąŅ€ĐˇĐ¸ĐŊа ĐŋŅ€ĐĩĐŊĐžŅĐ° oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа.", "transcoding_video_codec": "ВидĐĩĐž ĐēОдĐĩĐē", - "transcoding_video_codec_description": "ВП9 иĐŧа Đ˛Đ¸ŅĐžĐē҃ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ и web ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚, аĐģи Đŧ҃ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. ĐĨЕВĐĻ Ņ€Đ°Đ´Đ¸ ҁĐģĐ¸Ņ‡ĐŊĐž, аĐģи иĐŧа ĐŊиĐļ҃ web ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚. ĐĨ.264 ҘĐĩ ŅˆĐ¸Ņ€ĐžĐēĐž ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģаĐŊ и ĐąŅ€ĐˇĐž ҁĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ ĐŧĐŊĐžĐŗĐž вĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. АВ1 ҘĐĩ ĐŊĐ°Ņ˜ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐ¸Ņ˜Đ¸ ĐēОдĐĩĐē, аĐģи Đŧ҃ ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ ĐŋĐžĐ´Ņ€ŅˆĐēа ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа.", - "trash_enabled_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ĐžŅ‚Đŋада", + "transcoding_video_codec_description": "VP9 иĐŧа Đ˛Đ¸ŅĐžĐē҃ ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐžŅŅ‚ и wĐĩĐą ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚, аĐģи Đŧ҃ ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đ˛Đ¸ŅˆĐĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа Са Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. HEVC Ņ€Đ°Đ´Đ¸ ҁĐģĐ¸Ņ‡ĐŊĐž, аĐģи иĐŧа ĐŊиĐļ҃ wĐĩĐą ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚. H.264 ҘĐĩ ŅˆĐ¸Ņ€ĐžĐēĐž ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģаĐŊ и ĐąŅ€ĐˇĐž ҁĐĩ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°, аĐģи ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸ ĐŧĐŊĐžĐŗĐž вĐĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. АВ1 ҘĐĩ ĐŊĐ°Ņ˜ĐĩŅ„Đ¸ĐēĐ°ŅĐŊĐ¸Ņ˜Đ¸ ĐēОдĐĩĐē, аĐģи Đŧ҃ ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ ĐŋĐžĐ´Ņ€ŅˆĐēа ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đ¸Đŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа.", + "trash_enabled_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ĐžŅ‚Đŋада", "trash_number_of_days": "Đ‘Ņ€ĐžŅ˜ даĐŊа", "trash_number_of_days_description": "Đ‘Ņ€ĐžŅ˜ даĐŊа Са Đ´Ņ€ĐļĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ ĐŋŅ€Đĩ ĐŊĐĩĐŗĐž ŅˆŅ‚Đž Đ¸Ņ… Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ", - "trash_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐŧĐĩŅ›Đ°", - "trash_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ҁĐŧĐĩŅ›Đ°", - "untracked_files": "НĐĩĐŋŅ€Đ°Ņ›ĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "untracked_files_description": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đ¸ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. one ĐŧĐžĐŗŅƒ ĐŊĐ°ŅŅ‚Đ°Ņ‚Đ¸ ĐˇĐąĐžĐŗ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ… ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩŅšĐ°, ĐˇĐąĐžĐŗ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° иĐģи ĐēаО ĐŋŅ€ĐĩĐžŅŅ‚Đ°Ņ‚Đ°Đē ĐˇĐąĐžĐŗ ĐŗŅ€Đĩ҈ĐēĐĩ", - "user_cleanup_job": "Đ§Đ¸ŅˆŅ›ĐĩҚĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "user_delete_delay": "НаĐģĐžĐŗ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ {user} ĐąĐ¸Ņ›Đĩ СаĐēаСаĐŊи Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ Са {delay, plural, one {# даĐŊ} other {# даĐŊа}}.", + "trash_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐŧĐĩŅ†ĖĐ°", + "trash_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ҁĐŧĐĩŅ†ĖĐ°", + "untracked_files": "НĐĩĐŋŅ€Đ°Ņ†ĖĐĩĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "untracked_files_description": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đ¸ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. ОĐŊĐĩ ĐŧĐžĐŗŅƒ ĐŊĐ°ŅŅ‚Đ°Ņ‚Đ¸ ĐˇĐąĐžĐŗ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ… ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩŅšĐ°, ĐˇĐąĐžĐŗ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° иĐģи ĐēаО ĐŋŅ€ĐĩĐžŅŅ‚Đ°Ņ‚Đ°Đē ĐˇĐąĐžĐŗ ĐŗŅ€Đĩ҈ĐēĐĩ", + "user_cleanup_job": "Đ§Đ¸ŅˆŅ†ĖĐĩҚĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", + "user_delete_delay": "НаĐģĐžĐŗ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ {user} ĐąĐ¸Ņ†ĖĐĩ СаĐēаСаĐŊи Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ Са {delay, plural, one {# даĐŊ} other {# даĐŊа}}.", "user_delete_delay_settings": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ ŅƒĐˇ ĐēĐ°ŅˆŅšĐĩҚĐĩ", - "user_delete_delay_settings_description": "Đ‘Ņ€ĐžŅ˜ даĐŊа ĐŊаĐēĐžĐŊ ҃ĐēĐģĐ°ŅšĐ°ŅšĐ° Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēĐžĐŗ ĐŊаĐģĐžĐŗĐ° и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа. ĐŸĐžŅĐ°Đž ĐąŅ€Đ¸ŅĐ°ŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҁĐĩ ĐŋĐžĐēŅ€ĐĩŅ›Đĩ ҃ ĐŋĐžĐŊĐžŅ› да йи ҁĐĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Đģи ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ ĐēĐžŅ˜Đ¸ ҁ҃ ҁĐŋŅ€ĐĩĐŧĐŊи Са ĐąŅ€Đ¸ŅĐ°ŅšĐĩ. ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ОвĐĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēĐĩ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐŋŅ€ĐžŅ†ĐĩҚĐĩĐŊĐĩ ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ĐĩŅ›ĐĩĐŧ Đ¸ĐˇĐ˛Ņ€ŅˆĐĩҚ҃.", - "user_delete_immediately": "НаĐģĐžĐŗ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ {user} Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊи ĐŊа ҇ĐĩĐēĐ°ŅšĐĩ Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ОдĐŧĐ°Ņ….", + "user_delete_delay_settings_description": "Đ‘Ņ€ĐžŅ˜ даĐŊа ĐŊаĐēĐžĐŊ ҃ĐēĐģĐ°ŅšĐ°ŅšĐ° Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēĐžĐŗ ĐŊаĐģĐžĐŗĐ° и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа. ĐŸĐžŅĐ°Đž ĐąŅ€Đ¸ŅĐ°ŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҁĐĩ ĐŋĐžĐēŅ€Đĩ҆ˁĐĩ ҃ ĐŋĐžĐŊĐžŅ†Ė да йи ҁĐĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Đģи ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ ĐēĐžŅ˜Đ¸ ҁ҃ ҁĐŋŅ€ĐĩĐŧĐŊи Са ĐąŅ€Đ¸ŅĐ°ŅšĐĩ. ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ОвĐĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēĐĩ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐŋŅ€ĐžŅ†ĐĩҚĐĩĐŊĐĩ ĐŋŅ€Đ¸ ҁĐģĐĩĐ´Đĩ҆ˁĐĩĐŧ Đ¸ĐˇĐ˛Ņ€ŅˆĐĩҚ҃.", + "user_delete_immediately": "НаĐģĐžĐŗ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ {user} ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊи ĐŊа ҇ĐĩĐēĐ°ŅšĐĩ Са Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ОдĐŧĐ°Ņ….", "user_delete_immediately_checkbox": "ĐĄŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ Ņ€ĐĩĐ´ Са ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ", + "user_details": "ДĐĩŅ‚Đ°Ņ™Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "user_management": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°ŅšĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸Đŧа", "user_password_has_been_reset": "ЛозиĐŊĐēа ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҘĐĩ Ņ€ĐĩҁĐĩŅ‚ĐžĐ˛Đ°ĐŊа:", - "user_password_reset_description": "МоĐģиĐŧĐž да Đ´ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃ ĐģОСиĐŊĐē҃ ĐēĐžŅ€Đ¸ŅĐŊиĐē҃ и ОйавĐĩŅŅ‚Đ¸Ņ‚Đĩ ĐŗĐ° да Ņ›Đĩ ĐŧĐžŅ€Đ°Ņ‚Đ¸ да ĐŋŅ€ĐžĐŧĐĩĐŊи ĐģОСиĐŊĐē҃ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ ҁĐģĐĩĐ´ĐĩŅ›ĐĩĐŗ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ°.", - "user_restore_description": "НаĐģĐžĐŗ {user} Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ Đ˛Ņ€Đ°Ņ›ĐĩĐŊ.", + "user_password_reset_description": "МоĐģиĐŧĐž да Đ´ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃ ĐģОСиĐŊĐē҃ ĐēĐžŅ€Đ¸ŅĐŊиĐē҃ и ОйавĐĩŅŅ‚Đ¸Ņ‚Đĩ ĐŗĐ° да ҆ˁĐĩ ĐŧĐžŅ€Đ°Ņ‚Đ¸ да ĐŋŅ€ĐžĐŧĐĩĐŊи ĐģОСиĐŊĐē҃ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ ҁĐģĐĩĐ´Đĩ҆ˁĐĩĐŗ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ°.", + "user_restore_description": "НаĐģĐžĐŗ {user} ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Đ˛Ņ€Đ°Ņ†ĖĐĩĐŊ.", "user_restore_scheduled_removal": "Đ’Ņ€Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа - СаĐēаСаĐŊĐž ҃ĐēĐģĐ°ŅšĐ°ŅšĐĩ Са {date, date, ĐģĐžĐŊĐŗ}", "user_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "user_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēиĐŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа", "user_successfully_removed": "ĐšĐžŅ€Đ¸ŅĐŊиĐē {email} ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž ҃ĐēĐģĐžŅšĐĩĐŊ.", - "version_check_enabled_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ ĐŊĐžĐ˛Đ¸Ņ… Đ¸ĐˇĐ´Đ°ŅšĐ°", - "version_check_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ĐŋŅ€ĐžĐ˛ĐĩŅ€Đĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ° ĐŊа ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊ҃ ĐēĐžĐŧ҃ĐŊиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ŅĐ° github.com", + "version_check_enabled_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ ĐŊĐžĐ˛Đ¸Ņ… Đ¸ĐˇĐ´Đ°ŅšĐ°", + "version_check_implications": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ĐŋŅ€ĐžĐ˛ĐĩŅ€Đĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ° ĐŊа ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊ҃ ĐēĐžĐŧ҃ĐŊиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ŅĐ° ĐŗĐ¸Ņ‚Ņ…ŅƒĐą.Ņ†ĐžĐŧ", "version_check_settings": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ° вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ", - "version_check_settings_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ/oneĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩҚĐĩ Đž ĐŊĐžĐ˛ĐžŅ˜ вĐĩŅ€ĐˇĐ¸Ņ˜Đ¸", + "version_check_settings_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ/oneĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩҚĐĩ Đž ĐŊĐžĐ˛ĐžŅ˜ вĐĩŅ€ĐˇĐ¸Ņ˜Đ¸", "video_conversion_job": "ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ видĐĩĐž СаĐŋĐ¸ŅĐ°", "video_conversion_job_description": "ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ Са ŅˆĐ¸Ņ€Ņƒ ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐžŅŅ‚ ŅĐ° ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ¸Đŧа и ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа" }, @@ -371,26 +369,30 @@ "admin_password": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēа ЛозиĐŊĐēа", "administration": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đ°", "advanced": "НаĐŋŅ€ĐĩĐ´ĐŊĐž", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", - "age_months": "Starost{months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ¸}}", + "advanced_settings_enable_alternate_media_filter_subtitle": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐžĐŋŅ†Đ¸Ņ˜Ņƒ Са Ņ„Đ¸ĐģŅ‚Ņ€Đ¸Ņ€Đ°ŅšĐĩ ĐŧĐĩĐ´Đ¸Ņ˜Đ° Ņ‚ĐžĐēĐžĐŧ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊĐ¸Ņ… ĐēŅ€Đ¸Ņ‚ĐĩŅ€Đ¸Ņ˜ŅƒĐŧа. ПоĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ОвО ŅĐ°ĐŧĐž аĐēĐž иĐŧĐ°Ņ‚Đĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧа ŅĐ° аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜ĐžĐŧ да ĐžŅ‚ĐēŅ€Đ¸Ņ˜Đĩ ŅĐ˛Đĩ аĐģĐąŅƒĐŧĐĩ.", + "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНĐĸАЛНО] ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ Ņ„Đ¸ĐģŅ‚ĐĩŅ€ Са ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ аĐģĐąŅƒĐŧа ĐŊа аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊĐžĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ", + "advanced_settings_log_level_title": "Ниво ĐĩвидĐĩĐŊŅ†Đ¸Ņ˜Đĩ (ĐģĐžĐŗ): {level}", + "advanced_settings_prefer_remote_subtitle": "НĐĩĐēи ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸ вĐĩĐžĐŧа ҁĐŋĐžŅ€Đž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°Ņ˜Ņƒ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ ŅĐ° ҁҀĐĩĐ´ŅŅ‚Đ°Đ˛Đ° ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ОвО ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ да ĐąĐ¸ŅŅ‚Đĩ ҃ĐŧĐĩŅŅ‚Đž Ņ‚ĐžĐŗĐ° ŅƒŅ‡Đ¸Ņ‚Đ°Đģи ŅƒĐ´Đ°Ņ™ĐĩĐŊĐĩ ҁĐģиĐēĐĩ.", + "advanced_settings_prefer_remote_title": "ĐŸŅ€ĐĩŅ„ĐĩŅ€Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅƒĐ´Đ°Ņ™ĐĩĐŊĐĩ ҁĐģиĐēĐĩ", + "advanced_settings_proxy_headers_subtitle": "ДĐĩŅ„Đ¸ĐŊĐ¸ŅˆĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐēŅĐ¸ ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ™Đ° ĐēĐžŅ˜Đĩ Immich ҂ҀĐĩйа да ĐŋĐžŅˆĐ°Ņ™Đĩ ŅĐ° ŅĐ˛Đ°ĐēиĐŧ ĐŧŅ€ĐĩĐļĐŊиĐŧ ĐˇĐ°Ņ…Ņ‚ĐĩвОĐŧ", + "advanced_settings_proxy_headers_title": "ĐŸŅ€ĐžĐēŅĐ¸ ĐĨĐĩадĐĩŅ€Đ¸ (Ņ…ĐĩадĐĩҀҁ)", + "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐĩҁĐēĐ°Ņ‡Đĩ вĐĩŅ€Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Ņƒ SSL ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° Са ĐēŅ€Đ°Ņ˜ŅšŅƒ Ņ‚Đ°Ņ‡Đē҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°. ОбавĐĩСĐŊĐž Са ŅĐ°ĐŧĐžĐŋĐžŅ‚ĐŋĐ¸ŅĐ°ĐŊĐĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đĩ.", + "advanced_settings_self_signed_ssl_title": "ДозвоĐģи ŅĐ°ĐŧĐžĐŋĐžŅ‚ĐŋĐ¸ŅĐ°ĐŊĐĩ SSL ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đĩ", + "advanced_settings_sync_remote_deletions_subtitle": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи Đ¸ĐˇĐąŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ иĐģи Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đž ĐŊа ОвОĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ Đēада ҁĐĩ Ņ‚Đ° Ņ€Đ°Đ´ŅšĐ° ĐŋŅ€ĐĩĐ´ŅƒĐˇĐŧĐĩ ĐŊа вĐĩĐąŅƒ", + "advanced_settings_sync_remote_deletions_title": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜Ņ‚Đĩ ŅƒĐ´Đ°Ņ™ĐĩĐŊа ĐąŅ€Đ¸ŅĐ°ŅšĐ° [ЕКСПЕРИМЕНĐĸАЛНО]", + "advanced_settings_tile_subtitle": "НаĐŋŅ€ĐĩĐ´ĐŊа ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡Đēа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", + "advanced_settings_troubleshooting_subtitle": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ Са Ņ€ĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧа", + "advanced_settings_troubleshooting_title": "Đ ĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧа", + "age_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚{months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ¸}}", "age_year_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ 1 ĐŗĐžĐ´Đ¸ĐŊа, {months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩ҆(а/и)}}", "age_years": "{years, plural, other {ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ #}}", "album_added": "АĐģĐąŅƒĐŧ дОдаĐŊ", "album_added_notification_setting_description": "ĐŸŅ€Đ¸Đŧи ОйавĐĩŅˆŅ‚ĐĩҚĐĩ Đĩ-ĐŋĐžŅˆŅ‚ĐžĐŧ Đēад ĐąŅƒĐ´Đĩ҈ дОдаĐŊ ҃ Đ´ĐĩŅ™ĐĩĐŊ аĐģĐąŅƒĐŧ", "album_cover_updated": "ОĐŧĐžŅ‚ аĐģĐąŅƒĐŧа аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ", "album_delete_confirmation": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ аĐģĐąŅƒĐŧ {album}?", - "album_delete_confirmation_description": "АĐēĐž ҁĐĩ ĐžĐ˛Đ°Ņ˜ аĐģĐąŅƒĐŧ Đ´ĐĩĐģи, Đ´Ņ€ŅƒĐŗĐ¸ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩŅ›Đĩ ĐŧĐžŅ›Đ¸ да Đŧ҃ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐĩ.", - "album_info_card_backup_album_excluded": "EXCLUDED", - "album_info_card_backup_album_included": "INCLUDED", + "album_delete_confirmation_description": "АĐēĐž ҁĐĩ ĐžĐ˛Đ°Ņ˜ аĐģĐąŅƒĐŧ Đ´ĐĩĐģи, Đ´Ņ€ŅƒĐŗĐ¸ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ҆ˁĐĩ ĐŧĐžŅ†ĖĐ¸ да Đŧ҃ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐĩ.", + "album_info_card_backup_album_excluded": "Đ˜ĐĄĐšĐ›ĐˆĐŖĐ§Đ•ĐĐž", + "album_info_card_backup_album_included": "ĐŖĐšĐ›ĐˆĐŖĐ§Đ•ĐĐž", "album_info_updated": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° аĐģĐąŅƒĐŧа аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа", "album_leave": "НаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ аĐģĐąŅƒĐŧ?", "album_leave_confirmation": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ {album}?", @@ -399,239 +401,240 @@ "album_remove_user": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа?", "album_remove_user_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ {user}?", "album_share_no_users": "Đ˜ĐˇĐŗĐģĐĩда да ҁ҂Đĩ ĐŋОдĐĩĐģиĐģи ĐžĐ˛Đ°Ņ˜ аĐģĐąŅƒĐŧ ŅĐ° ŅĐ˛Đ¸Đŧ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸Đŧа иĐģи да ĐŊĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩĐ´ĐŊĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ŅĐ° ĐēĐžŅ˜Đ¸Đŧ ĐąĐ¸ŅŅ‚Đĩ Đ´ĐĩĐģиĐģи.", - "album_thumbnail_card_item": "1 item", - "album_thumbnail_card_items": "{} items", - "album_thumbnail_card_shared": " ¡ Shared", - "album_thumbnail_shared_by": "Shared by {}", + "album_thumbnail_card_item": "1 ŅŅ‚Đ°Đ˛Đēа", + "album_thumbnail_card_items": "{count} ŅŅ‚Đ°Đ˛Đēи", + "album_thumbnail_card_shared": " ДĐĩŅ™ĐĩĐŊĐž", + "album_thumbnail_shared_by": "ДĐĩĐģи {user}", "album_updated": "АĐģĐąŅƒĐŧ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ", "album_updated_setting_description": "ĐŸŅ€Đ¸ĐŧĐ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩҚĐĩ Đĩ-ĐŋĐžŅˆŅ‚ĐžĐŧ Đēада Đ´ĐĩŅ™ĐĩĐŊи аĐģĐąŅƒĐŧ иĐŧа ĐŊОва ŅĐ˛ĐžŅ˜ŅŅ‚Đ˛Đ°", "album_user_left": "НаĐŋŅƒŅŅ‚Đ¸Đž/Đģа {album}", "album_user_removed": "ĐŖĐēĐģĐžŅšĐĩĐŊ {user}", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", - "album_viewer_appbar_share_err_delete": "Failed to delete album", - "album_viewer_appbar_share_err_leave": "Failed to leave album", - "album_viewer_appbar_share_err_remove": "There are problems in removing assets from album", - "album_viewer_appbar_share_err_title": "Failed to change album title", - "album_viewer_appbar_share_leave": "Leave album", - "album_viewer_appbar_share_to": "Share To", - "album_viewer_page_share_add_users": "Add users", + "album_viewer_appbar_delete_confirm": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Đ°Ņ˜ аĐģĐąŅƒĐŧ ŅĐ° ŅĐ˛ĐžĐŗ ĐŊаĐģĐžĐŗĐ°?", + "album_viewer_appbar_share_err_delete": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐąŅ€Đ¸ŅĐ°ŅšĐĩ аĐģĐąŅƒĐŧа", + "album_viewer_appbar_share_err_leave": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž иСĐģаĐļĐĩҚĐĩ иС аĐģĐąŅƒĐŧа", + "album_viewer_appbar_share_err_remove": "ĐŸŅ€ĐžĐąĐģĐĩĐŧи ŅĐ° ĐąŅ€Đ¸ŅĐ°ŅšĐĩĐŧ СаĐŋĐ¸ŅĐ° иС аĐģĐąŅƒĐŧа", + "album_viewer_appbar_share_err_title": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŧĐĩŅšĐ°ŅšĐĩ ĐŊаСива аĐģĐąŅƒĐŧа", + "album_viewer_appbar_share_leave": "Đ˜ĐˇĐ°Ņ’Đ¸ иС аĐģĐąŅƒĐŧа", + "album_viewer_appbar_share_to": "ПодĐĩĐģи ŅĐ°", + "album_viewer_page_share_add_users": "Đ”ĐžĐ´Đ°Ņ˜ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ", "album_with_link_access": "НĐĩĐēа ŅĐ˛Đ°ĐēĐž ĐēĐž иĐŧа вĐĩĐˇŅƒ види Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и Ņ™ŅƒĐ´Đĩ ҃ ОвОĐŧ аĐģĐąŅƒĐŧ҃.", "albums": "АĐģĐąŅƒĐŧи", - "albums_count": "{count, plural, one {{count, number} АĐģĐąŅƒĐŧ} few {{count, number} АĐģĐąŅƒĐŧa} other {{count, number} АĐģĐąŅƒĐŧa}}", + "albums_count": "{count, plural, one {{count, number} АĐģĐąŅƒĐŧ} few {{count, number} АĐģĐąŅƒĐŧи} other {{count, number} АĐģĐąŅƒĐŧи}}", "all": "ХвĐĩ", "all_albums": "Хви аĐģĐąŅƒĐŧи", "all_people": "ХвĐĩ ĐžŅĐžĐąĐĩ", "all_videos": "Хви видĐĩĐž ҁĐŊиĐŧŅ†Đ¸", "allow_dark_mode": "ДозвоĐģи Ņ‚Đ°ĐŧĐŊи Ņ€ĐĩĐļиĐŧ", "allow_edits": "ДозвоĐģи ŅƒŅ€ĐĩŅ’ĐĩҚĐĩ", - "allow_public_user_to_download": "ДозвоĐģĐ¸Ņ‚Đĩ Ņ˜Đ°Đ˛ĐŊĐžĐŧ ĐēĐžŅ€Đ¸ŅĐŊиĐē҃ да ĐŋŅ€ĐĩŅƒĐˇĐŧĐĩ (download-uje)", + "allow_public_user_to_download": "ДозвоĐģĐ¸Ņ‚Đĩ Ņ˜Đ°Đ˛ĐŊĐžĐŧ ĐēĐžŅ€Đ¸ŅĐŊиĐē҃ да ĐŋŅ€ĐĩŅƒĐˇĐŧĐĩ (Đ´ĐžwĐŊĐģОад-҃ҘĐĩ)", "allow_public_user_to_upload": "ДозвоĐģи Ņ˜Đ°Đ˛ĐŊĐžĐŧ ĐēĐžŅ€Đ¸ŅĐŊиĐē҃ да ĐžŅ‚ĐŋŅ€ĐĩĐŧи (҃ĐŋĐģОад-҃ҘĐĩ)", - "alt_text_qr_code": "ĐĄĐģиĐēа QR ĐēОда", + "alt_text_qr_code": "ĐĄĐģиĐēа QĐ  ĐēОда", "anti_clockwise": "ĐŖ ҁĐŧĐĩŅ€Ņƒ ҁ҃ĐŋŅ€ĐžŅ‚ĐŊĐžĐŧ Од ĐēĐ°ĐˇĐ°Ņ™ĐēĐĩ ĐŊа ŅĐ°Ņ‚Ņƒ", - "api_key": "АПИ ĐēŅ™ŅƒŅ‡ (key)", - "api_key_description": "Ова Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐŋŅ€Đ¸ĐēаСаĐŊа ŅĐ°ĐŧĐž ҘĐĩĐ´ĐŊĐžĐŧ. ОбавĐĩСĐŊĐž ĐēĐžĐŋĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đĩ ĐŊĐĩĐŗĐž ŅˆŅ‚Đž ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐˇĐžŅ€.", + "api_key": "АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", + "api_key_description": "Ова Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐŋŅ€Đ¸ĐēаСаĐŊа ŅĐ°ĐŧĐž ҘĐĩĐ´ĐŊĐžĐŧ. ОбавĐĩСĐŊĐž ĐēĐžĐŋĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€Đĩ ĐŊĐĩĐŗĐž ŅˆŅ‚Đž ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐˇĐžŅ€.", "api_key_empty": "ИĐŧĐĩ Đ˛Đ°ŅˆĐĩĐŗ АПИ ĐēŅ™ŅƒŅ‡Đ° ĐŊĐĩ йи ҂ҀĐĩйаĐģĐž да ĐąŅƒĐ´Đĩ ĐŋŅ€Đ°ĐˇĐŊĐž", - "api_keys": "АПИ ĐēŅ™ŅƒŅ‡Đĩви (keys)", - "app_bar_signout_dialog_content": "Are you sure you want to sign out?", - "app_bar_signout_dialog_ok": "Yes", - "app_bar_signout_dialog_title": "Sign out", + "api_keys": "АПИ ĐēŅ™ŅƒŅ‡Đĩви (ĐēĐĩyҁ)", + "app_bar_signout_dialog_content": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҁĐĩ ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ?", + "app_bar_signout_dialog_ok": "Да", + "app_bar_signout_dialog_title": "ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ", "app_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", "appears_in": "ĐŸĐžŅ˜Đ°Đ˛Ņ™ŅƒŅ˜Đĩ ҁĐĩ ҃", "archive": "ĐŅ€Ņ…Đ¸Đ˛Đ°", "archive_or_unarchive_photo": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ иĐģи ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ŅšĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", + "archive_page_no_archived_assets": "ĐĐ¸ŅŅƒ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊа Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°", + "archive_page_title": "ĐŅ€Ņ…Đ¸Đ˛Đ° ({count})", "archive_size": "ВĐĩĐģĐ¸Ņ‡Đ¸ĐŊа Đ°Ņ€Ņ…Đ¸Đ˛Đĩ", "archive_size_description": "ПодĐĩŅĐ¸ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊ҃ Đ°Ņ€Ņ…Đ¸Đ˛Đĩ Са ĐŋŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ (҃ ГиБ)", - "archived": "Archived", + "archived": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž", "archived_count": "{count, plural, other {ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž #}}", "are_these_the_same_person": "Да Đģи ҁ҃ ОвО Đ¸ŅŅ‚Đ° ĐžŅĐžĐąĐ°?", "are_you_sure_to_do_this": "ЈĐĩҁ҂Đĩ Đģи ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ ОвО да ŅƒŅ€Đ°Đ´Đ¸Ņ‚Đĩ?", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", + "asset_action_delete_err_read_only": "НĐĩ ĐŧĐžĐŗŅƒ да ĐžĐąŅ€Đ¸ŅˆĐĩĐŧ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(Đĩ) ŅĐ°ĐŧĐž Са Ņ‡Đ¸Ņ‚Đ°ŅšĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "asset_action_share_err_offline": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐĩŅƒĐˇĐĩŅ‚Đ¸ ĐžŅ„ĐģĐ°Ņ˜ĐŊ Ņ€ĐĩŅŅƒŅ€Ņ(Đĩ), ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", "asset_added_to_album": "Đ”ĐžĐ´Đ°Ņ‚Đž ҃ аĐģĐąŅƒĐŧ", "asset_adding_to_album": "Đ”ĐžĐ´Đ°Ņ˜Đĩ ҁĐĩ ҃ аĐģĐąŅƒĐŧâ€Ļ", "asset_description_updated": "ОĐŋĐ¸Ņ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ", "asset_filename_is_offline": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа {filename} ҘĐĩ ваĐŊ ĐŧŅ€ĐĩĐļĐĩ (offline)", "asset_has_unassigned_faces": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа иĐŧа ĐŊĐĩдОдĐĩŅ™ĐĩĐŊа ĐģĐ¸Ņ†Đ°", "asset_hashing": "ĐĨĐĩŅˆĐ¸Ņ€Đ°ŅšĐĩâ€Ļ", - "asset_list_group_by_sub_title": "Group by", - "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout", - "asset_list_layout_settings_group_automatically": "Automatic", - "asset_list_layout_settings_group_by": "Group assets by", - "asset_list_layout_settings_group_by_month_day": "Month + day", - "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Photo grid layout settings", - "asset_list_settings_title": "Photo Grid", - "asset_offline": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐžĐ´ŅŅƒŅ‚ĐŊа (offline)", - "asset_offline_description": "Ова Đ˛Đ°ŅšŅĐēа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҁĐĩ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐŊаĐģаСи ĐŊа Đ´Đ¸ŅĐē҃. МоĐģиĐŧĐž ĐēĐžĐŊŅ‚Đ°ĐēŅ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžĐŗ ИĐŧĐ¸Ņ‡ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Са ĐŋĐžĐŧĐžŅ›.", - "asset_restored_successfully": "Asset restored successfully", + "asset_list_group_by_sub_title": "Đ“Ņ€ŅƒĐŋĐ¸ŅˆĐ¸ ĐŋĐž", + "asset_list_layout_settings_dynamic_layout_title": "ДиĐŊаĐŧĐ¸Ņ‡ĐŊи Ņ€Đ°ŅĐŋĐžŅ€ĐĩĐ´", + "asset_list_layout_settings_group_automatically": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи", + "asset_list_layout_settings_group_by": "Đ“Ņ€ŅƒĐŋĐ¸ŅˆĐ¸ СаĐŋĐ¸ŅĐĩ ĐŋĐž", + "asset_list_layout_settings_group_by_month_day": "МĐĩҁĐĩ҆ + ДаĐŊ", + "asset_list_layout_sub_title": "ЛаyĐžŅƒŅ‚", + "asset_list_settings_subtitle": "ОĐŋŅ†Đ¸Ņ˜Đĩ Са ĐŧŅ€ĐĩĐļĐŊи ĐŋŅ€Đ¸ĐēаС Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "asset_list_settings_title": "ĐœŅ€ĐĩĐļĐŊи ĐŋŅ€Đ¸ĐēаС Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "asset_offline": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐžĐ´ŅŅƒŅ‚ĐŊа", + "asset_offline_description": "Ова Đ˛Đ°ŅšŅĐēа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҁĐĩ Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐŊаĐģаСи ĐŊа Đ´Đ¸ŅĐē҃. МоĐģиĐŧĐž ĐēĐžĐŊŅ‚Đ°ĐēŅ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžĐŗ Immich адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Са ĐŋĐžĐŧĐžŅ†Ė.", + "asset_restored_successfully": "ИĐŧОвиĐŊа ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛Ņ€Đ°Ņ†ĖĐĩĐŊа", "asset_skipped": "ĐŸŅ€ĐĩҁĐēĐžŅ‡ĐĩĐŊĐž", "asset_skipped_in_trash": "ĐŖ ĐžŅ‚Đŋад", "asset_uploaded": "ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐž (ĐŖĐŋĐģОадĐĩĐ´)", "asset_uploading": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", + "asset_viewer_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ° ĐŗĐ°ĐģĐĩŅ€Đ¸Ņ˜Đĩ", + "asset_viewer_settings_title": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡ иĐŧОвиĐŊĐĩ", "assets": "ЗаĐŋĐ¸ŅĐ¸", "assets_added_count": "Đ”ĐžĐ´Đ°Ņ‚Đž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", "assets_added_to_album_count": "Đ”ĐžĐ´Đ°Ņ‚Đž ҘĐĩ {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}} ҃ аĐģĐąŅƒĐŧ", - "assets_added_to_name_count": "Đ”ĐžĐ´Đ°Ņ‚Đž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēa}} ҃ {hasName, select, true {{name}} other {ĐŊОви аĐģĐąŅƒĐŧ}}", + "assets_added_to_name_count": "Đ”ĐžĐ´Đ°Ņ‚Đž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ҃ {hasName, select, true {{name}} other {ĐŊОви аĐģĐąŅƒĐŧ}}", "assets_count": "{count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°ĐŊĐž", + "assets_deleted_permanently_from_server": "{count} Ņ€ĐĩŅŅƒŅ€Ņ(а) Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°ĐŊ(а) ŅĐ° Immich ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "assets_moved_to_trash_count": "ĐŸŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}} ҃ ĐžŅ‚Đŋад", "assets_permanently_deleted_count": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", "assets_removed_count": "ĐŖĐēĐģĐžŅšĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{count} ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžŅšĐĩĐŊĐž ŅĐ° Đ˛Đ°ŅˆĐĩĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", "assets_restore_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ ŅĐ˛Đĩ ŅĐ˛ĐžŅ˜Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ҁ҃ ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ? НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ! ИĐŧĐ°Ņ˜Ņ‚Đĩ ĐŊа ҃Đŧ҃ да ҁĐĩ ваĐŊĐŧŅ€ĐĩĐļĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐŊĐĩ ĐŧĐžĐŗŅƒ Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đ¸ ĐŊа ĐžĐ˛Đ°Ņ˜ ĐŊĐ°Ņ‡Đ¸ĐŊ.", - "assets_restored_count": "Đ’Ņ€Đ°Ņ›ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_count": "Đ’Ņ€Đ°Ņ†ĖĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", + "assets_restored_successfully": "{count} ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛Ņ€Đ°Ņ†ĖĐĩĐŊĐž", + "assets_trashed": "{count} ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ҘĐĩ ĐŋŅ€ĐĩĐąĐ°Ņ‡ĐĩĐŊĐž ҃ ĐžŅ‚Đŋад", "assets_trashed_count": "Đ‘Đ°Ņ‡ĐĩĐŊĐž ҃ ĐžŅ‚Đŋад {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few{# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", - "assets_were_part_of_album_count": "{count, plural, one {Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҘĐĩ} other {Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁ҃}} вĐĩŅ› Đ´ĐĩĐž аĐģĐąŅƒĐŧа", - "authorized_devices": "ОвĐģĐ°ŅˆŅ›ĐĩĐŊи ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "assets_trashed_from_server": "{count} Ņ€ĐĩŅŅƒŅ€Ņ(а) ĐžĐąŅ€Đ¸ŅĐ°ĐŊĐ¸Ņ… ŅĐ° Immich ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "assets_were_part_of_album_count": "{count, plural, one {Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҘĐĩ} other {Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҁ҃}} вĐĩ҆ˁ Đ´ĐĩĐž аĐģĐąŅƒĐŧа", + "authorized_devices": "ОвĐģĐ°ŅˆŅ†ĖĐĩĐŊи ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸", + "automatic_endpoint_switching_subtitle": "ПовĐĩĐļĐ¸Ņ‚Đĩ ҁĐĩ ĐģĐžĐēаĐģĐŊĐž ĐŋŅ€ĐĩĐēĐž ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐžĐŗ Wi-Fi-Ņ˜Đ° Đēада ҘĐĩ Đ´ĐžŅŅ‚ŅƒĐŋаĐŊ и ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊĐĩ вĐĩСĐĩ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Đŧ ĐŧĐĩŅŅ‚Đ¸Đŧа", + "automatic_endpoint_switching_title": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēа ĐŋŅ€ĐžĐŧĐĩĐŊа URL-Ова", "back": "Назад", "back_close_deselect": "Назад, ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ иĐģи ĐžĐŋĐžĐˇĐžĐ˛Đ¸Ņ‚Đĩ Đ¸ĐˇĐąĐžŅ€", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albums on device ({})", - "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", - "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", - "backup_album_selection_page_select_albums": "Select albums", - "backup_album_selection_page_selection_info": "Selection Info", - "backup_album_selection_page_total_assets": "Total unique assets", - "backup_all": "All", - "backup_background_service_backup_failed_message": "Failed to backup assets. Retryingâ€Ļ", - "backup_background_service_connection_failed_message": "Failed to connect to the server. Retryingâ€Ļ", - "backup_background_service_current_upload_notification": "Uploading {}", - "backup_background_service_default_notification": "Checking for new assetsâ€Ļ", - "backup_background_service_error_title": "Backup error", - "backup_background_service_in_progress_notification": "Backing up your assetsâ€Ļ", - "backup_background_service_upload_failure_notification": "Failed to upload {}", - "backup_controller_page_albums": "Backup Albums", - "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.", - "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled", - "backup_controller_page_background_app_refresh_enable_button_text": "Go to settings", - "backup_controller_page_background_battery_info_link": "Show me how", - "backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.", - "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Battery optimizations", - "backup_controller_page_background_charging": "Only while charging", - "backup_controller_page_background_configure_error": "Failed to configure the background service", - "backup_controller_page_background_delay": "Delay new assets backup: {}", - "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app", - "backup_controller_page_background_is_off": "Automatic background backup is off", - "backup_controller_page_background_is_on": "Automatic background backup is on", - "backup_controller_page_background_turn_off": "Turn off background service", - "backup_controller_page_background_turn_on": "Turn on background service", - "backup_controller_page_background_wifi": "Only on WiFi", - "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selected: ", - "backup_controller_page_backup_sub": "Backed up photos and videos", - "backup_controller_page_created": "Created on: {}", - "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.", - "backup_controller_page_excluded": "Excluded: ", - "backup_controller_page_failed": "Failed ({})", - "backup_controller_page_filename": "File name: {} [{}]", - "backup_controller_page_id": "ID: {}", - "backup_controller_page_info": "Backup Information", - "backup_controller_page_none_selected": "None selected", - "backup_controller_page_remainder": "Remainder", - "backup_controller_page_remainder_sub": "Remaining photos and videos to back up from selection", - "backup_controller_page_server_storage": "Server Storage", - "backup_controller_page_start_backup": "Start Backup", - "backup_controller_page_status_off": "Automatic foreground backup is off", - "backup_controller_page_status_on": "Automatic foreground backup is on", - "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backed up", - "backup_controller_page_total_sub": "All unique photos and videos from selected albums", - "backup_controller_page_turn_off": "Turn off foreground backup", - "backup_controller_page_turn_on": "Turn on foreground backup", - "backup_controller_page_uploading_file_info": "Uploading file info", - "backup_err_only_album": "Cannot remove the only album", - "backup_info_card_assets": "assets", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "background_location_permission": "ДозвоĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ҃ ĐŋОСадиĐŊи", + "background_location_permission_content": "Да йи ҁĐĩ ĐŧĐĩŅšĐ°ĐģĐĩ ĐŧŅ€ĐĩĐļĐĩ Đ´ĐžĐē ҁĐĩ Ņ€Đ°Đ´Đ¸ ҃ ĐŋОСадиĐŊи, ИĐŧĐ¸Ņ… ĐŧĐžŅ€Đ° *ŅƒĐ˛ĐĩĐē* иĐŧĐ°Ņ‚Đ¸ ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐ°ĐŊ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ¸ ĐēаĐēĐž йи аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŧĐžĐŗĐģа да ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ° иĐŧĐĩ Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", + "backup_album_selection_page_albums_device": "АĐģĐąŅƒĐŧа ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ ({count})", + "backup_album_selection_page_albums_tap": "Đ”ĐžĐ´Đ¸Ņ€ĐŊи да ҃ĐēŅ™ŅƒŅ‡Đ¸Ņˆ, Đ´ĐžĐ´Đ¸Ņ€ĐŊи дваĐŋŅƒŅ‚ да Đ¸ŅĐēŅ™ŅƒŅ‡Đ¸Ņˆ", + "backup_album_selection_page_assets_scatter": "ЗаĐŋĐ¸ŅĐ¸ ҁĐĩ ĐŧĐžĐŗŅƒ ĐŊĐ°Ņ›Đ¸ ҃ Đ˛Đ¸ŅˆĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Ņ‚Đ¸Ņ… аĐģĐąŅƒĐŧа. ĐžĐ´Đ°Ņ‚ĐģĐĩ аĐģĐąŅƒĐŧи ҁĐĩ ĐŧĐžĐŗŅƒ ҃ĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đ¸ иĐģи Đ¸ŅĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đ¸ Ņ‚ĐžĐēĐžĐŧ ĐŋŅ€ĐžŅ†ĐĩŅĐ° ĐŋŅ€Đ°Đ˛Ņ™ĐĩŅšĐ° ĐŋОСадиĐŊҁĐēĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ°.", + "backup_album_selection_page_select_albums": "ОдабĐĩŅ€Đ¸ аĐģĐąŅƒĐŧĐĩ", + "backup_album_selection_page_selection_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž ҁĐĩĐģĐĩĐēŅ†Đ¸Ņ˜Đ¸", + "backup_album_selection_page_total_assets": "ĐŖĐē҃ĐŋĐŊĐž ҘĐĩдиĐŊŅŅ‚Đ˛ĐĩĐŊĐ¸Ņ… ***", + "backup_all": "ХвĐĩ", + "backup_background_service_backup_failed_message": "ĐŸŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž. ПоĐēŅƒŅˆĐ°Đ˛Đ° ҁĐĩ ĐŋĐžĐŊОвОâ€Ļ", + "backup_background_service_connection_failed_message": "ПовĐĩĐˇĐ¸Đ˛Đ°ŅšĐĩ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž. ПоĐēŅƒŅˆĐ°Đ˛Đ°Đŧ ĐŋĐžĐŊОвОâ€Ļ", + "backup_background_service_current_upload_notification": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ {filename}", + "backup_background_service_default_notification": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ°Đ˛Đ°ŅšĐĩ ĐŊĐžĐ˛Đ¸Ņ… СаĐŋĐ¸ŅĐ°â€Ļ", + "backup_background_service_error_title": "Đ“Ņ€Đĩ҈Đēа ҃ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚ҃ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ°", + "backup_background_service_in_progress_notification": "ĐŸŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° СаĐŋĐ¸ŅĐ°â€Ļ", + "backup_background_service_upload_failure_notification": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐž: {filename}", + "backup_controller_page_albums": "НаĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋĐ¸Ņ˜Ņƒ аĐģĐąŅƒĐŧа", + "backup_controller_page_background_app_refresh_disabled_content": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ĐŋОСадиĐŊҁĐēĐž ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ҃ ОĐŋŅ†Đ¸Ņ˜Đĩ > ГĐĩĐŊĐĩŅ€Đ°ĐģĐŊĐĩ > ПозадиĐŊҁĐēĐž ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ĐēаĐēĐž йи ĐŊаĐŋŅ€Đ°Đ˛Đ¸Đģи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ ҃ ĐŋОСадиĐŊи.", + "backup_controller_page_background_app_refresh_disabled_title": "ПозадиĐŊҁĐēĐž ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐž", + "backup_controller_page_background_app_refresh_enable_button_text": "Иди ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", + "backup_controller_page_background_battery_info_link": "ПоĐēаĐļи Đŧи ĐēаĐēĐž", + "backup_controller_page_background_battery_info_message": "За ĐŊĐ°Ņ˜ĐŋĐžŅƒĐˇĐ´Đ°ĐŊĐ¸Ņ˜Đĩ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ°, ŅƒĐŗĐ°ŅĐ¸Ņ‚Đĩ йиĐģĐž ĐēĐžŅ˜Ņƒ ĐžĐŋŅ†Đ¸Ņ˜Ņƒ ҃ ĐžĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°Đŧа ĐēĐžŅ˜Đĩ йи ҁĐŋŅ€ĐĩŅ‡Đ°Đ˛Đ°ĐģĐĩ Immich ŅĐ° ĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊиĐŧ Ņ€Đ°Đ´ĐžĐŧ.\n\nĐžĐ˛Đ°Ņ˜ ĐŋĐžŅŅ‚ŅƒĐŋаĐē Đ˛Đ°Ņ€Đ¸Ņ€Đ° Од ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ° Đ´Đž ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐĩ ĐēĐžŅ€Đ°ĐēĐĩ Са Đ’Đ°Ņˆ ŅƒŅ€ĐĩŅ’Đ°Ņ˜.", + "backup_controller_page_background_battery_info_ok": "ОК", + "backup_controller_page_background_battery_info_title": "ОĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° Đ‘Đ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đĩ", + "backup_controller_page_background_charging": "ХаĐŧĐž Ņ‚ĐžĐēĐžĐŧ Đŋ҃ҚĐĩŅšĐ°", + "backup_controller_page_background_configure_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ¸ŅĐ°ŅšĐĩ ĐŋОСадиĐŊҁĐēĐžĐŗ ҁĐĩŅ€Đ˛Đ¸ŅĐ°", + "backup_controller_page_background_delay": "Đ’Ņ€ĐĩĐŧĐĩ иСĐŧĐĩŅ’Ņƒ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҘĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° СаĐŋĐ¸ŅĐ°: {duration}", + "backup_controller_page_background_description": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐŋОСадиĐŊҁĐēи ҁĐĩŅ€Đ˛Đ¸Ņ да Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋŅ€Đ°Đ˛Đ¸Ņˆ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ, ĐąĐĩС да ĐžŅ‚Đ˛Đ°Ņ€Đ°Ņˆ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "backup_controller_page_background_is_off": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋОСадиĐŊи ҘĐĩ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐž", + "backup_controller_page_background_is_on": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋОСадиĐŊи ҘĐĩ ҃ĐēŅ™ŅƒŅ‡ĐĩĐŊĐž", + "backup_controller_page_background_turn_off": "Đ˜ŅĐēŅ™ŅƒŅ‡Đ¸ ĐŋОСадиĐŊҁĐēи ҁĐĩŅ€Đ˛Đ¸Ņ", + "backup_controller_page_background_turn_on": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐŋОСадиĐŊҁĐēи ҁĐĩŅ€Đ˛Đ¸Ņ", + "backup_controller_page_background_wifi": "ХаĐŧĐž ĐŊа Wi-Fi", + "backup_controller_page_backup": "НаĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋĐ¸Ņ˜Ņƒ", + "backup_controller_page_backup_selected": "ĐžĐ´Đ°ĐąŅ€Đ°ĐŊĐž: ", + "backup_controller_page_backup_sub": "Đ—Đ°Đ˛Ņ€ŅˆĐĩĐŊĐž ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° и видĐĩа", + "backup_controller_page_created": "НаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊĐž:{date}", + "backup_controller_page_desc_backup": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋŅ€Đ˛ĐžĐŧ ĐŋĐģаĐŊ҃ да Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ Đēада ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ.", + "backup_controller_page_excluded": "Đ˜ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐž: ", + "backup_controller_page_failed": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ({count})", + "backup_controller_page_filename": "ИĐŧĐĩ Ņ„Đ°Ņ˜Đģа: {filename} [{size}]", + "backup_controller_page_id": "ИД:{id}", + "backup_controller_page_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ", + "backup_controller_page_none_selected": "ĐĐ¸ŅˆŅ‚Đ° ĐžĐ´Đ°ĐąŅ€Đ°ĐŊĐž", + "backup_controller_page_remainder": "ĐŸĐžĐ´ŅĐĩŅ‚ĐŊиĐē", + "backup_controller_page_remainder_sub": "ĐžŅŅ‚Đ°ĐģĐž Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° и видĐĩа да ҁĐĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧи Од ҁĐĩĐģĐĩĐēŅ†Đ¸Ņ˜Đĩ", + "backup_controller_page_server_storage": "ĐŸŅ€ĐžŅŅ‚ĐžŅ€ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", + "backup_controller_page_start_backup": "ПоĐēŅ€ĐĩĐŊи ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ", + "backup_controller_page_status_off": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋŅ€Đ˛ĐžĐŧ ĐŋĐģаĐŊ҃ ҘĐĩ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐž", + "backup_controller_page_status_on": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋŅ€Đ˛ĐžĐŧ ĐŋĐģаĐŊ҃ ҘĐĩ ҃ĐēŅ™ŅƒŅ‡ĐĩĐŊĐž", + "backup_controller_page_storage_format": "{used} Од {total} Đ¸ŅĐēĐžŅ€Đ¸ŅˆŅ›ĐĩĐŊĐž", + "backup_controller_page_to_backup": "АĐģĐąŅƒĐŧи ĐēĐžŅ˜Đ¸ Ņ›Đĩ ҁĐĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đ¸", + "backup_controller_page_total_sub": "ХвĐĩ ҘĐĩдиĐŊŅŅ‚Đ˛ĐĩĐŊĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩи иС ĐžĐ´Đ°ĐąŅ€Đ°ĐŊĐ¸Ņ… аĐģĐąŅƒĐŧа", + "backup_controller_page_turn_off": "Đ˜ŅĐēŅ™ŅƒŅ‡Đ¸ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋŅ€Đ˛ĐžĐŧ ĐŋĐģаĐŊ҃", + "backup_controller_page_turn_on": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋŅ€Đ˛ĐžĐŧ ĐŋĐģаĐŊ҃", + "backup_controller_page_uploading_file_info": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ ŅĐ˛ĐžŅ˜ŅŅ‚Đ°Đ˛Đ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "backup_err_only_album": "НĐĩĐŧĐžĐŗŅƒŅ›Đĩ ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ҘĐĩдиĐŊĐžĐŗ аĐģĐąŅƒĐŧа", + "backup_info_card_assets": "СаĐŋĐ¸ŅĐ¸", + "backup_manual_cancelled": "ĐžŅ‚ĐēаСаĐŊĐž", + "backup_manual_in_progress": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ ҘĐĩ вĐĩ҆ˁ ҃ Ņ‚ĐžĐē҃. ПоĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ĐēĐ°ŅĐŊĐ¸Ņ˜Đĩ", + "backup_manual_success": "ĐŖŅĐŋĐĩŅ…", + "backup_manual_title": "ĐŖĐŋĐģОад ŅŅ‚Đ°Ņ‚ŅƒŅ", + "backup_options_page_title": "Đ‘Đ°Ņ†Đē҃Đŋ ĐžĐŋŅ‚Đ¸ĐžĐŊҁ", + "backup_setting_subtitle": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° ҃ ĐŋОСадиĐŊи и ĐŋŅ€ĐĩĐ´ŅšĐĩĐŧ ĐŋĐģаĐŊ҃", "backward": "ĐŖĐŊаСад", "birthdate_saved": "Đ”Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ° ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ", "birthdate_set_description": "Đ”Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ° ҁĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ да йи ҁĐĩ Đ¸ĐˇŅ€Đ°Ņ‡ŅƒĐŊаĐģĐĩ ĐŗĐžĐ´Đ¸ĐŊĐĩ ОвĐĩ ĐžŅĐžĐąĐĩ ҃ Đ´ĐžĐąŅƒ ĐžĐ´Ņ€ĐĩŅ’ĐĩĐŊĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ.", - "blurred_background": "ЗаĐŧŅƒŅ›ĐĩĐŊа ĐŋОСадиĐŊа", - "bugs_and_feature_requests": "Đ“Ņ€Đĩ҈ĐēĐĩ и ĐˇĐ°Ņ…Ņ‚Đĩви Са Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ", - "build": "Под-вĐĩŅ€ĐˇĐ¸Ņ˜Đ° (Build)", - "build_image": "ĐĄĐ°ĐŗŅ€Đ°Đ´Đ¸ (Đ‘ŅƒĐ¸ĐģĐ´) иĐŧĐ°ĐŗĐĩ", - "bulk_delete_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ ĐŗŅ€ŅƒĐŋĐŊĐž да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ {count, plural, one {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ°}}? Ово Ņ›Đĩ ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸ ĐŊĐ°Ņ˜Đ˛ĐĩŅ›Đĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đž ŅĐ˛Đ°ĐēĐĩ ĐŗŅ€ŅƒĐŋĐĩ и Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ŅĐ˛Đĩ Đ´Ņ€ŅƒĐŗĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ. НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ!", - "bulk_keep_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐˇĐ°Đ´Ņ€ĐļĐ¸Ņ‚Đĩ {count, plural, one {1 Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}? Ово Ņ›Đĩ Ņ€ĐĩŅˆĐ¸Ņ‚Đ¸ ŅĐ˛Đĩ Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŗŅ€ŅƒĐŋĐĩ ĐąĐĩС ĐąŅ€Đ¸ŅĐ°ŅšĐ° йиĐģĐž ҇ĐĩĐŗĐ°.", - "bulk_trash_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ ĐŗŅ€ŅƒĐŋĐŊĐž да ĐžĐ´ĐąĐ°Ņ†Đ¸Ņ‚Đĩ {count, plural, one {1 Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}? Ово Ņ›Đĩ ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸ ĐŊĐ°Ņ˜Đ˛ĐĩŅ›Ņƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃ ŅĐ˛Đ°ĐēĐĩ ĐŗŅ€ŅƒĐŋĐĩ и ĐžĐ´ĐąĐ°Ņ†Đ¸Ņ‚Đ¸ ŅĐ˛Đĩ ĐžŅŅ‚Đ°ĐģĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ.", - "buy": "ĐšŅƒĐŋĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†ĐĩĐŊŅ†Ņƒ ИĐŧĐ¸Ņ‡-а", - "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", - "cache_settings_clear_cache_button": "Clear cache", - "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Image cache size ({} assets)", - "cache_settings_statistics_album": "Library thumbnails", - "cache_settings_statistics_assets": "{} assets ({})", - "cache_settings_statistics_full": "Full images", - "cache_settings_statistics_shared": "Shared album thumbnails", - "cache_settings_statistics_thumbnail": "Thumbnails", - "cache_settings_statistics_title": "Cache usage", - "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application", - "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", - "cache_settings_title": "Caching Settings", + "blurred_background": "ЗаĐŧŅƒŅ†ĖĐĩĐŊа ĐŋОСадиĐŊа", + "bugs_and_feature_requests": "Đ“Ņ€Đĩ҈ĐēĐĩ (ĐąŅƒĐŗŅ) и ĐˇĐ°Ņ…Ņ‚Đĩви Са Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ", + "build": "Под-вĐĩŅ€ĐˇĐ¸Ņ˜Đ° (Đ‘ŅƒĐ¸ĐģĐ´)", + "build_image": "ĐĄĐ°ĐŗŅ€Đ°Đ´Đ¸ (Đ‘ŅƒĐ¸ĐģĐ´) image", + "bulk_delete_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ ĐŗŅ€ŅƒĐŋĐŊĐž да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ {count, plural, one {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ°}}? Ово ҆ˁĐĩ ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸ ĐŊĐ°Ņ˜Đ˛Đĩ҆ˁĐĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đž ŅĐ˛Đ°ĐēĐĩ ĐŗŅ€ŅƒĐŋĐĩ и Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ŅĐ˛Đĩ Đ´Ņ€ŅƒĐŗĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ. НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ!", + "bulk_keep_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐˇĐ°Đ´Ņ€ĐļĐ¸Ņ‚Đĩ {count, plural, one {1 Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}? Ово ҆ˁĐĩ Ņ€ĐĩŅˆĐ¸Ņ‚Đ¸ ŅĐ˛Đĩ Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŗŅ€ŅƒĐŋĐĩ ĐąĐĩС ĐąŅ€Đ¸ŅĐ°ŅšĐ° йиĐģĐž ҇ĐĩĐŗĐ°.", + "bulk_trash_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ ĐŗŅ€ŅƒĐŋĐŊĐž да ĐžĐ´ĐąĐ°Ņ†Đ¸Ņ‚Đĩ {count, plural, one {1 Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} few {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}? Ово ҆ˁĐĩ ĐˇĐ°Đ´Ņ€ĐļĐ°Ņ‚Đ¸ ĐŊĐ°Ņ˜Đ˛ĐĩŅ†ĖŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃ ŅĐ˛Đ°ĐēĐĩ ĐŗŅ€ŅƒĐŋĐĩ и ĐžĐ´ĐąĐ°Ņ†Đ¸Ņ‚Đ¸ ŅĐ˛Đĩ ĐžŅŅ‚Đ°ĐģĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ.", + "buy": "ĐšŅƒĐŋĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†ĐĩĐŊŅ†Ņƒ Immich-a", + "cache_settings_album_thumbnails": "ĐĄĐģĐ¸Ņ‡Đ¸Ņ†Đĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ ({count} assets)", + "cache_settings_clear_cache_button": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Ņƒ", + "cache_settings_clear_cache_button_title": "Ова ĐžĐŋŅ†Đ¸Ņ˜Đ° ĐąŅ€Đ¸ŅˆĐĩ ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Ņƒ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ. Ово Ņ›Đĩ ĐąĐ¸Ņ‚ĐŊĐž ŅƒŅ‚Đ¸Ņ†Đ°Ņ‚Đ¸ ĐŊа ĐŋĐĩŅ€Ņ„ĐžŅ€ĐŧаĐŊҁĐĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ Đ´ĐžĐē ҁĐĩ ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đ° ĐŊĐĩ ŅƒŅ‡Đ¸Ņ‚Đ° ĐŋĐžĐŊОвО.", + "cache_settings_duplicated_assets_clear_button": "ĐĻЛЕАР", + "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ ĐēĐžŅ˜Đĩ ҘĐĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ŅŅ‚Đ°Đ˛Đ¸Đģа ĐŊа ҆ҀĐŊ҃ ĐģĐ¸ŅŅ‚Ņƒ", + "cache_settings_duplicated_assets_title": "Đ”ŅƒĐŋĐģĐ¸Ņ€Đ°ĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({count})", + "cache_settings_image_cache_size": "ВĐĩĐģĐ¸Ņ‡Đ¸ĐŊа ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ ҁĐģиĐēа ({count} assets)", + "cache_settings_statistics_album": "МиĐŊĐ¸Ņ˜Đ°Ņ‚ŅƒŅ€Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēа", + "cache_settings_statistics_assets": "{count} ŅŅ‚Đ°Đ˛Đēи ({size})", + "cache_settings_statistics_full": "ĐŸŅƒĐŊĐĩ ҁĐģиĐēĐĩ", + "cache_settings_statistics_shared": "МиĐŊĐ¸Ņ˜Đ°Ņ‚ŅƒŅ€Đĩ Đ´ĐĩŅ™ĐĩĐŊĐ¸Ņ… аĐģĐąŅƒĐŧа", + "cache_settings_statistics_thumbnail": "МиĐŊĐ¸Ņ˜Đ°Ņ‚ŅƒŅ€Đĩ", + "cache_settings_statistics_title": "Đ˜ŅĐēĐžŅ€Đ¸ŅˆŅ›ĐĩĐŊа ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đ°", + "cache_settings_subtitle": "КоĐŊŅ‚role Са ĐēĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Ņƒ ĐŧОйиĐģĐŊĐĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ Immich", + "cache_settings_thumbnail_size": "КĐĩ҈ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đ° ĐēĐžŅ˜Ņƒ ĐˇĐ°ŅƒĐˇĐ¸ĐŧĐ°Ņ˜Ņƒ ĐŧиĐŊĐ¸Ņ˜Đ°Ņ‚ŅƒŅ€Đĩ ({count} ŅŅ‚Đ°Đ˛Đēи)", + "cache_settings_tile_subtitle": "КоĐŊŅ‚Ņ€ĐžĐģĐ¸ŅˆĐ¸Ņ‚Đĩ ĐŋĐžĐŊĐ°ŅˆĐ°ŅšĐĩ ĐģĐžĐēаĐģĐŊĐžĐŗ ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩŅšĐ°", + "cache_settings_tile_title": "ЛоĐēаĐģĐŊа ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đ°", + "cache_settings_title": "ОĐŋŅ†Đ¸Ņ˜Đĩ Са ĐēĐĩŅˆĐ¸Ņ€Đ°ŅšĐĩ", "camera": "КаĐŧĐĩŅ€Đ°", "camera_brand": "Đ‘Ņ€ĐĩĐŊĐ´ ĐēаĐŧĐĩŅ€Đĩ", "camera_model": "МодĐĩĐģ ĐēаĐŧĐĩŅ€Đĩ", "cancel": "ĐžĐ´ŅƒŅŅ‚Đ°ĐŊи", "cancel_search": "ĐžŅ‚ĐēаĐļи ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", - "canceled": "Canceled", + "canceled": "ĐžŅ‚ĐēаСаĐŊĐž", "cannot_merge_people": "НĐĩ ĐŧĐžĐļĐĩ ҁĐŋĐžŅ˜Đ¸Ņ‚Đ¸ ĐžŅĐžĐąĐĩ", "cannot_undo_this_action": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ!", "cannot_update_the_description": "НĐĩ ĐŧĐžĐļĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", "change_date": "ĐŸŅ€ĐžĐŧĐĩĐŊи Đ´Đ°Ņ‚ŅƒĐŧ", - "change_display_order": "Change display order", + "change_display_order": "ĐŸŅ€ĐžĐŧĐĩĐŊи Ņ€ĐĩĐ´ĐžŅĐģĐĩĐ´ ĐŋŅ€Đ¸ĐēаСа", "change_expiration_time": "ĐŸŅ€ĐžĐŧĐĩĐŊи Đ˛Ņ€ĐĩĐŧĐĩ Đ¸ŅŅ‚ĐĩĐēа", "change_location": "ĐŸŅ€ĐžĐŧĐĩĐŊи ĐŧĐĩŅŅ‚Đž", "change_name": "ĐŸŅ€ĐžĐŧĐĩĐŊи иĐŧĐĩ", "change_name_successfully": "ĐŸŅ€ĐžĐŧĐĩĐŊи иĐŧĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž", "change_password": "ĐŸŅ€ĐžĐŧĐĩĐŊи ЛозиĐŊĐē҃", "change_password_description": "Ово ҘĐĩ иĐģи ĐŋŅ€Đ˛Đ¸ ĐŋŅƒŅ‚ да ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™ŅƒŅ˜ĐĩŅ‚Đĩ ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧ иĐģи ҘĐĩ ĐŋОдĐŊĐĩŅ‚ ĐˇĐ°Ņ…Ņ‚Đĩв Са ĐŋŅ€ĐžĐŧĐĩĐŊ҃ ĐģОСиĐŊĐēĐĩ. ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ĐģОСиĐŊĐē҃ Đ¸ŅĐŋОд.", - "change_password_form_confirm_password": "Confirm Password", - "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.", - "change_password_form_new_password": "New Password", - "change_password_form_password_mismatch": "Passwords do not match", - "change_password_form_reenter_new_password": "Re-enter New Password", + "change_password_form_confirm_password": "ПоĐŊОвО ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ", + "change_password_form_description": "Ћао, {name}\n\nОво ҘĐĩ вĐĩŅ€ĐžĐ˛Đ°Ņ‚ĐŊĐž Đ’Đ°ŅˆĐĩ ĐŋŅ€Đ˛Đž ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°ŅšĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧ҃, иĐģи ҘĐĩ ĐŋОдĐŊĐĩ҈ĐĩĐŊ ĐˇĐ°Ņ…Ņ‚Đĩв Са ĐŋŅ€ĐžĐŧĐĩĐŊ҃ ŅˆĐ¸Ņ„Ņ€Đĩ. МоĐģиĐŧĐž Đ’Đ°Ņ, ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ŅˆĐ¸Ņ„Ņ€Ņƒ Đ¸ŅĐŋОд.", + "change_password_form_new_password": "Нова ŅˆĐ¸Ņ„Ņ€Đ°", + "change_password_form_password_mismatch": "Đ¨Đ¸Ņ„Ņ€Đĩ ҁĐĩ ĐŊĐĩ ĐŋĐžĐ´ŅƒĐ´Đ°Ņ€Đ°Ņ˜Ņƒ", + "change_password_form_reenter_new_password": "ПоĐŊОвО ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ŅˆĐ¸Ņ„Ņ€Ņƒ", + "change_pin_code": "ĐŸŅ€ĐžĐŧĐĩĐŊа ПИН ĐēОда", "change_your_password": "ĐŸŅ€ĐžĐŧĐĩĐŊи ŅĐ˛ĐžŅ˜Ņƒ ŅˆĐ¸Ņ„Ņ€Ņƒ", "changed_visibility_successfully": "Đ’Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊа", "check_all": "Đ¨Ņ‚Đ¸ĐēĐģĐ¸Ņ€Đ°Ņ‚Đ¸ ŅĐ˛Đĩ", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đģи ĐŋĐžŅŅ‚ĐžŅ˜Đĩ ĐžŅˆŅ‚Đĩ҆ˁĐĩĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ иĐŧОвиĐŊĐĩ", + "check_corrupt_asset_backup_button": "Đ˜ĐˇĐ˛Ņ€ŅˆĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ", + "check_corrupt_asset_backup_description": "ПоĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ ŅĐ°ĐŧĐž ĐŋŅ€ĐĩĐēĐž Wi-Fi ĐŧŅ€ĐĩĐļĐĩ и ĐŊаĐēĐžĐŊ ŅˆŅ‚Đž ҁĐĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° ŅĐ˛Đ¸Ņ… ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа. ĐŸĐžŅŅ‚ŅƒĐŋаĐē ĐŧĐžĐļĐĩ ĐŋĐžŅ‚Ņ€Đ°Ņ˜Đ°Ņ‚Đ¸ ĐŊĐĩĐēĐžĐģиĐēĐž ĐŧиĐŊŅƒŅ‚Đ°.", "check_logs": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ Đ´ĐŊĐĩвĐŊиĐēĐĩ (ĐģĐžĐŗŅ)", - "choose_matching_people_to_merge": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ›Đĩ ĐžŅĐžĐąĐĩ Са ҁĐŋĐ°Ņ˜Đ°ŅšĐĩ", + "choose_matching_people_to_merge": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ†ĖĐĩ ĐžŅĐžĐąĐĩ Са ҁĐŋĐ°Ņ˜Đ°ŅšĐĩ", "city": "Đ“Ņ€Đ°Đ´", "clear": "ĐˆĐ°ŅĐŊĐž", "clear_all": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ ŅĐ˛Đĩ", "clear_all_recent_searches": "ĐžĐąŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ ŅĐ˛Đĩ ĐŊĐĩдавĐŊĐĩ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", "clear_message": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ĐŋĐžŅ€ŅƒĐē҃", "clear_value": "ĐˆĐ°ŅĐŊа Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_dialog_msg_confirm": "ОК", + "client_cert_enter_password": "ЕĐŊŅ‚ĐĩŅ€ Password", + "client_cert_import": "ИĐŧĐŋĐžŅ€Ņ‚", + "client_cert_import_success_msg": "ĐĄĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ ĐēĐģĐ¸Ņ˜ĐĩĐŊŅ‚Đ° ҘĐĩ ŅƒĐ˛ĐĩСĐĩĐŊ", + "client_cert_invalid_msg": "НĐĩваĐļĐĩŅ†ĖĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° иĐģи ĐŋĐžĐŗŅ€Đĩ҈ĐŊа ĐģОСиĐŊĐēа", + "client_cert_remove_msg": "ĐĄĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ ĐēĐģĐ¸Ņ˜ĐĩĐŊŅ‚Đ° ҘĐĩ ҃ĐēĐģĐžŅšĐĩĐŊ", + "client_cert_subtitle": "ĐŸĐžĐ´Ņ€Đļава ŅĐ°ĐŧĐž ПКĐĻĐĄ12 (.Đŋ12, .ĐŋŅ„x) Ņ„ĐžŅ€ĐŧĐ°Ņ‚. ĐŖĐ˛ĐžĐˇ/҃ĐēĐģĐ°ŅšĐ°ŅšĐĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° ҘĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž ŅĐ°ĐŧĐž ĐŋŅ€Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đĩ", + "client_cert_title": "SSL ĐēĐģĐ¸Ņ˜ĐĩĐŊ҂ҁĐēи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚", "clockwise": "ĐŖ ҁĐŧĐĩŅ€Ņƒ ĐēĐ°ĐˇĐ°Ņ™ĐēĐĩ", "close": "Đ—Đ°Ņ‚Đ˛ĐžŅ€Đ¸", "collapse": "ĐĄĐē҃Đŋи", @@ -641,28 +644,29 @@ "comment_deleted": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€ ĐžĐąŅ€Đ¸ŅĐ°ĐŊ", "comment_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ°", "comments_and_likes": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ и ĐģĐ°Ņ˜ĐēОви", - "comments_are_disabled": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ ҁ҃ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊи", - "common_create_new_album": "Create new album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", - "confirm": "ĐŸĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ", + "comments_are_disabled": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ ҁ҃ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊи", + "common_create_new_album": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜ ĐŊОви аĐģĐąŅƒĐŧ", + "common_server_error": "МоĐģиĐŧĐž Đ˛Đ°Ņ да ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ĐŧŅ€ĐĩĐļĐŊ҃ вĐĩĐˇŅƒ, ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ҁĐĩ да ҘĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ Đ´ĐžŅŅ‚ŅƒĐŋаĐŊ и да ҁ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ°/ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐĩ.", + "completed": "Đ—Đ°Đ˛Ņ€ŅˆĐĩĐŊĐž", + "confirm": "ĐŸĐžŅ‚Đ˛Ņ€Đ´Đ¸", "confirm_admin_password": "ĐŸĐžŅ‚Đ˛Ņ€Đ´Đ¸ АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊ҃ ЛозиĐŊĐē҃", "confirm_delete_face": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžŅĐžĐąŅƒ {name} иС Đ´ĐĩĐģа?", "confirm_delete_shared_link": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Đ°Ņ˜ Đ´ĐĩŅ™ĐĩĐŊи link?", - "confirm_keep_this_delete_others": "Хвe ĐžŅŅ‚Đ°Đģe Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēe ҃ ĐŗŅ€ŅƒĐŋи Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊe ĐžŅĐ¸Đŧ Овe Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēe. Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ?", + "confirm_keep_this_delete_others": "ХвĐĩ ĐžŅŅ‚Đ°ĐģĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ ĐŗŅ€ŅƒĐŋи ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ĐžŅĐ¸Đŧ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ?", + "confirm_new_pin_code": "ĐŸĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ ĐŊОви ПИН ĐēОд", "confirm_password": "ПоĐŊОвО ҃ĐŊĐĩŅĐ¸ ŅˆĐ¸Ņ„Ņ€Ņƒ", "contain": "ĐžĐąŅƒŅ…Đ˛Đ°Ņ‚Đ¸", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐĐ°ŅŅ‚Đ°Đ˛Đ¸", - "control_bottom_app_bar_album_info_shared": "{} items ¡ Shared", - "control_bottom_app_bar_create_new_album": "Create new album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", + "control_bottom_app_bar_album_info_shared": "{count} ŅŅ‚Đ˛Đ°Ņ€Đ¸ ĐŋОдĐĩŅ™ĐĩĐŊĐž", + "control_bottom_app_bar_create_new_album": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜ ĐŊОви аĐģĐąŅƒĐŧ", + "control_bottom_app_bar_delete_from_immich": "ĐžĐąŅ€Đ¸ŅˆĐ¸ иС Immich-a", + "control_bottom_app_bar_delete_from_local": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ŅĐ° ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", + "control_bottom_app_bar_edit_location": "ИСĐŧĐĩĐŊи ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "control_bottom_app_bar_edit_time": "ИСĐŧĐĩĐŊи Đ´Đ°Ņ‚ŅƒĐŧ и Đ˛Ņ€ĐĩĐŧĐĩ", + "control_bottom_app_bar_share_link": "ДĐĩĐģи link", + "control_bottom_app_bar_share_to": "ПодĐĩĐģи ŅĐ°", + "control_bottom_app_bar_trash_from_immich": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ ҃ ĐžŅ‚Đŋад", "copied_image_to_clipboard": "КоĐŋĐ¸Ņ€Đ°ĐŊа ҁĐģиĐēа ҃ ĐŧĐĩŅ’ŅƒŅĐŋŅ€ĐĩĐŧĐŊиĐē (҆ĐģиĐŋĐąĐžĐ°Ņ€Đ´).", "copied_to_clipboard": "КоĐŋĐ¸Ņ€Đ°ĐŊĐž ҃ ĐŧĐĩŅ’ŅƒŅĐŋŅ€ĐĩĐŧĐŊиĐē (҆ĐģиĐŋĐąĐžĐ°Ņ€Đ´)!", "copy_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐēĐžĐŋĐ¸Ņ€Đ°ŅšŅƒ", @@ -677,34 +681,36 @@ "covers": "ОĐŧĐžŅ‚Đ¸", "create": "НаĐŋŅ€Đ°Đ˛Đ¸", "create_album": "НаĐŋŅ€Đ°Đ˛Đ¸ аĐģĐąŅƒĐŧ", - "create_album_page_untitled": "Untitled", + "create_album_page_untitled": "БĐĩС ĐŊĐ°ŅĐģОва", "create_library": "НаĐŋŅ€Đ°Đ˛Đ¸ БибĐģĐ¸ĐžŅ‚ĐĩĐē҃", "create_link": "НаĐŋŅ€Đ°Đ˛Đ¸ вĐĩĐˇŅƒ", "create_link_to_share": "НаĐŋŅ€Đ°Đ˛Đ¸ вĐĩĐˇŅƒ Са Đ´ĐĩŅ™ĐĩҚĐĩ", "create_link_to_share_description": "НĐĩĐēа ŅĐ˛Đ°ĐēĐž ŅĐ° вĐĩСОĐŧ види Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", - "create_new": "CREATE NEW", + "create_new": "ĐĻРЕАĐĸЕ НЕW", "create_new_person": "НаĐŋŅ€Đ°Đ˛Đ¸ ĐŊĐžĐ˛Ņƒ ĐžŅĐžĐąŅƒ", "create_new_person_hint": "ДодĐĩĐģĐ¸Ņ‚Đĩ Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊĐžĐ˛ĐžŅ˜ ĐžŅĐžĐąĐ¸", "create_new_user": "НаĐŋŅ€Đ°Đ˛Đ¸ ĐŊĐžĐ˛ĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "create_shared_album_page_share_add_assets": "ADD ASSETS", - "create_shared_album_page_share_select_photos": "Select Photos", + "create_shared_album_page_share_add_assets": "ДОДАЈ СРЕДСĐĸВА", + "create_shared_album_page_share_select_photos": "ОдабĐĩŅ€Đ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", "create_tag": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ОСĐŊаĐē҃ (tag)", - "create_tag_description": "НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ОСĐŊаĐē҃ (tag). За ŅƒĐŗĐŊĐĩĐļŅ’ĐĩĐŊĐĩ ОСĐŊаĐēĐĩ, ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ Đŋ҃ĐŊ҃ ĐŋŅƒŅ‚Đ°ŅšŅƒ ОСĐŊаĐēĐĩ ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ›Đ¸ ĐēĐžŅĐĩ ҆Ҁ҂Đĩ.", + "create_tag_description": "НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ОСĐŊаĐē҃ (tag). За ŅƒĐŗĐŊĐĩĐļŅ’ĐĩĐŊĐĩ ОСĐŊаĐēĐĩ, ҃ĐŊĐĩŅĐ¸Ņ‚Đĩ Đŋ҃ĐŊ҃ ĐŋŅƒŅ‚Đ°ŅšŅƒ ОСĐŊаĐēĐĩ ҃ĐēŅ™ŅƒŅ‡ŅƒŅ˜ŅƒŅ†ĖĐ¸ ĐēĐžŅĐĩ ҆Ҁ҂Đĩ.", "create_user": "НаĐŋŅ€Đ°Đ˛Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "created": "НаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊ", - "crop": "Crop", - "curated_object_page_title": "Things", + "created_at": "ĐšŅ€ĐĩĐ¸Ņ€Đ°ĐŊĐž", + "crop": "ĐžĐąŅ€ĐĩĐˇĐ¸Đ˛Đ°ŅšĐĩ", + "curated_object_page_title": "ĐĄŅ‚Đ˛Đ°Ņ€Đ¸", "current_device": "ĐĸŅ€ĐĩĐŊŅƒŅ‚ĐŊи ŅƒŅ€ĐĩŅ’Đ°Ņ˜", - "current_server_address": "Current server address", - "custom_locale": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊа ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ° (locale)", + "current_pin_code": "ĐĸŅ€ĐĩĐŊŅƒŅ‚ĐŊи ПИН ĐēОд", + "current_server_address": "ĐĸŅ€ĐĩĐŊŅƒŅ‚ĐŊа Đ°Đ´Ņ€ĐĩŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "custom_locale": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊа ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ° (ĐģĐžŅ†Đ°ĐģĐĩ)", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ Đ´Đ°Ņ‚ŅƒĐŧĐĩ и ĐąŅ€ĐžŅ˜ĐĩвĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ ҘĐĩСиĐēа и Ņ€ĐĩĐŗĐ¸ĐžĐŊа", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "Е Đ´Đ´ МММ", + "daily_title_text_date_year": "Е Đ´Đ´ МММ yyyy", "dark": "ĐĸаĐŧĐŊĐž", "date_after": "Đ”Đ°Ņ‚ŅƒĐŧ ĐŋĐžŅĐģĐĩ", "date_and_time": "Đ”Đ°Ņ‚ŅƒĐŧ и Đ’Ņ€ĐĩĐŧĐĩ", "date_before": "Đ”Đ°Ņ‚ŅƒĐŧ ĐŋŅ€Đĩ", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "Е Đ´ ЛЛЛ y â€ĸ ĐĨ:ĐŧĐŧ", "date_of_birth_saved": "Đ”Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ° ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ", "date_range": "Đ Đ°ŅĐŋĐžĐŊ Đ´Đ°Ņ‚ŅƒĐŧа", "day": "ДаĐŊ", @@ -713,38 +719,38 @@ "deduplication_criteria_2": "Đ‘Ņ€ĐžŅ˜ EXIF ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", "deduplication_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž Đ´ĐĩĐ´ŅƒĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ¸", "deduplication_info_description": "Да ĐąĐ¸ŅĐŧĐž Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ҃ĐŊаĐŋŅ€ĐĩĐ´ ĐžĐ´Đ°ĐąŅ€Đ°Đģи Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ҃ĐēĐģĐžĐŊиĐģи Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ ĐŗŅ€ŅƒĐŋĐŊĐž, ĐŗĐģĐĩдаĐŧĐž:", - "default_locale": "ĐŸĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ° (locale)", + "default_locale": "ĐŸĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊа ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ° (ĐģĐžŅ†Đ°ĐģĐĩ)", "default_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ Đ´Đ°Ņ‚ŅƒĐŧĐĩ и ĐąŅ€ĐžŅ˜ĐĩвĐĩ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ ĐģĐžĐēаĐģĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ Đ˛Đ°ŅˆĐĩĐŗ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡Đ°", "delete": "ĐžĐąŅ€Đ¸ŅˆĐ¸", "delete_album": "ĐžĐąŅ€Đ¸ŅˆĐ¸ аĐģĐąŅƒĐŧ", - "delete_api_key_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Đ°Ņ˜ АПИ ĐēŅ™ŅƒŅ‡ (key)?", - "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", - "delete_dialog_title": "Delete Permanently", + "delete_api_key_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Đ°Ņ˜ АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)?", + "delete_dialog_alert": "ОвĐĩ ŅŅ‚Đ˛Đ°Ņ€Đ¸ Ņ›Đĩ ĐŋĐĩŅ€ĐŧаĐŊĐĩĐŊŅ‚ĐŊĐž ĐąĐ¸Ņ‚Đ¸ ĐžĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ŅĐ° Immich-a и Đ’Đ°ŅˆĐĩĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", + "delete_dialog_alert_local": "ОвĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžŅšĐĩĐŊĐĩ ŅĐ° Đ˛Đ°ŅˆĐĩĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°, аĐģи ҆ˁĐĩ и Đ´Đ°Ņ™Đĩ ĐąĐ¸Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ ĐŊа Immich ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", + "delete_dialog_alert_local_non_backed_up": "НĐĩĐēĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ĐŊĐ¸ŅŅƒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐž ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Immich-u и ĐąĐ¸Ņ†ĖĐĩ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžŅšĐĩĐŊĐĩ ŅĐ° Đ˛Đ°ŅˆĐĩĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", + "delete_dialog_alert_remote": "ОвĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ŅĐ° Immich ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "delete_dialog_ok_force": "ИĐŋаĐē ĐžĐąŅ€Đ¸ŅˆĐ¸", + "delete_dialog_title": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ĐŋĐĩŅ€ĐŧаĐŊĐĩĐŊŅ‚ĐŊĐž", "delete_duplicates_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ОвĐĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ?", "delete_face": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ ĐžŅĐžĐąŅƒ", "delete_key": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ ĐēŅ™ŅƒŅ‡", "delete_library": "ĐžĐąŅ€Đ¸ŅˆĐ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "delete_link": "ĐžĐąŅ€Đ¸ŅˆĐ¸ вĐĩĐˇŅƒ", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", + "delete_local_dialog_ok_backed_up_only": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ŅĐ°ĐŧĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ", + "delete_local_dialog_ok_force": "ИĐŋаĐē ĐžĐąŅ€Đ¸ŅˆĐ¸", "delete_others": "Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ Đ´Ņ€ŅƒĐŗĐĩ", "delete_shared_link": "ĐžĐąŅ€Đ¸ŅˆĐ¸ Đ´ĐĩŅ™ĐĩĐŊ҃ вĐĩĐˇŅƒ", - "delete_shared_link_dialog_title": "Delete Shared Link", + "delete_shared_link_dialog_title": "ĐžĐąŅ€Đ¸ŅˆĐ¸ Đ´ĐĩŅ™ĐĩĐŊи link", "delete_tag": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ОСĐŊаĐē҃ (tag)", - "delete_tag_confirmation_prompt": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ОСĐŊаĐē҃ (tag) {tagName}?", + "delete_tag_confirmation_prompt": "Да Đģи ŅŅ‚Đ˛Đ°Ņ€ĐŊĐž ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ОСĐŊаĐē҃ {tagName}?", "delete_user": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "deleted_shared_link": "ĐžĐąŅ€Đ¸ŅˆĐĩĐŊа Đ´ĐĩŅ™ĐĩĐŊа вĐĩСа", - "deletes_missing_assets": "Đ‘Ņ€Đ¸ŅˆĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒ ŅĐ° Đ´Đ¸ŅĐēа", + "deletes_missing_assets": "Đ‘Ņ€Đ¸ŅˆĐĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐēĐžŅ˜Đ° ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒ ŅĐ° Đ´Đ¸ŅĐēа", "description": "ОĐŋĐ¸Ņ", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", + "description_input_hint_text": "Адд Đ´ĐĩŅŅ†Ņ€Đ¸ĐŋŅ‚Đ¸ĐžĐŊ...", + "description_input_submit_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšŅƒ ĐžĐŋĐ¸ŅĐ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ Đ´ĐŊĐĩвĐŊиĐē Са Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°", "details": "ДĐĩŅ‚Đ°Ņ™Đ¸", "direction": "ĐĄĐŧĐĩŅ€", - "disabled": "oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž", + "disabled": "ОĐŊĐĩĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąŅ€Đ°ĐŊи иСĐŧĐĩĐŊĐĩ", "discord": "Đ”Đ¸ŅĐēĐžŅ€Đ´", "discover": "ĐžŅ‚ĐēŅ€Đ¸Ņ˜Ņ‚Đĩ", @@ -753,34 +759,34 @@ "display_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸ĐēаСа", "display_order": "Đ ĐĩĐ´ĐžŅĐģĐĩĐ´ ĐŋŅ€Đ¸ĐēаСа", "display_original_photos": "ĐŸŅ€Đ¸ĐēаĐļĐ¸Ņ‚Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", - "display_original_photos_setting_description": "Đ Đ°Đ´Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸ĐēĐ°ĐˇŅƒŅ˜ĐĩŅ‚Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ Đēада ĐŗĐģĐĩdate ĐŧĐ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đ°Đģ ĐŊĐĩĐŗĐž ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Đēада ҘĐĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ´ĐĩĐģĐž ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐž ŅĐ° webom. Ово ĐŧĐžĐļĐĩ дОвĐĩŅŅ‚Đ¸ Đ´Đž ҁĐŋĐžŅ€Đ¸Ņ˜ĐĩĐŗ ĐŋŅ€Đ¸ĐēаСа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°.", + "display_original_photos_setting_description": "Đ Đ°Đ´Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸ĐēĐ°ĐˇŅƒŅ˜ĐĩŅ‚Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ Đēада ĐŗĐģĐĩdate ĐŧĐ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đ°Đģ ĐŊĐĩĐŗĐž ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ Đēада ҘĐĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ´ĐĩĐģĐž ĐēĐžĐŧĐŋĐ°Ņ‚Đ¸ĐąĐ¸ĐģĐŊĐž ŅĐ° wĐĩйОĐŧ. Ово ĐŧĐžĐļĐĩ дОвĐĩŅŅ‚Đ¸ Đ´Đž ҁĐŋĐžŅ€Đ¸Ņ˜ĐĩĐŗ ĐŋŅ€Đ¸ĐēаСа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°.", "do_not_show_again": "НĐĩ ĐŋŅ€Đ¸ĐēаĐļи ĐŋĐžĐŊОвО ĐžĐ˛Ņƒ ĐŋĐžŅ€ŅƒĐē҃", "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Đ°", "done": "ĐŖŅ€Đ°Ņ’ĐĩĐŊĐž", "download": "ĐŸŅ€ĐĩŅƒĐˇĐŧи", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", + "download_canceled": "ĐŸŅ€ĐĩŅƒĐˇĐŧи ĐžŅ‚ĐēаСаĐŊĐž", + "download_complete": "ĐŸŅ€ĐĩŅƒĐˇĐŧи ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž", + "download_enqueue": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ ŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊĐž ҃ Ņ€ĐĩĐ´", + "download_error": "ДоwĐŊĐģОад Đ•Ņ€Ņ€ĐžŅ€", + "download_failed": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", + "download_filename": "Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа: {filename}", + "download_finished": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž", "download_include_embedded_motion_videos": "ĐŖĐŗŅ€Đ°Ņ’ĐĩĐŊи видĐĩĐž ҁĐŊиĐŧŅ†Đ¸", "download_include_embedded_motion_videos_description": "ĐŖĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ ŅƒĐŗŅ€Đ°Ņ’ĐĩĐŊĐĩ ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ҃ ĐŋĐžĐēŅ€ĐĩŅ‚Ņƒ ĐēаО ĐˇĐ°ŅĐĩĐąĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", - "download_notfound": "Download not found", - "download_paused": "Download paused", + "download_notfound": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐŊĐ¸Ņ˜Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊĐž", + "download_paused": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ ĐŋĐ°ŅƒĐˇĐ¸Ņ€Đ°ĐŊĐž", "download_settings": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ", "download_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа вĐĩСаĐŊиĐŧ Са ĐŋŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", + "download_started": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ СаĐŋĐžŅ‡ĐĩŅ‚Đž", + "download_sucess": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž", + "download_sucess_android": "МĐĩĐ´Đ¸Ņ˜Đ¸ ҁ҃ ĐŋŅ€ĐĩŅƒĐˇĐĩŅ‚Đ¸ ĐŊа ДĐĻИМ/Immich", + "download_waiting_to_retry": "ЧĐĩĐēĐ°ŅšĐĩ ĐŊа ĐŋĐžĐŊОвĐŊи ĐŋĐžĐēŅƒŅˆĐ°Ņ˜", "downloading": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҃ Ņ‚ĐžĐē҃", "downloading_asset_filename": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ {filename}", - "downloading_media": "Downloading media", + "downloading_media": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐŧĐĩĐ´Đ¸Ņ˜Đ°", "drop_files_to_upload": "ĐŖĐąĐ°Ņ†Đ¸Ņ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ йиĐģĐž ĐŗĐ´Đĩ да Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đĩ (҃ĐŋĐģОад-҃ҘĐĩŅ‚Đĩ)", "duplicates": "Đ”ŅƒĐŋĐģиĐēĐ°Ņ‚Đ¸", - "duplicates_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đĩ ŅĐ˛Đ°Đē҃ ĐŗŅ€ŅƒĐŋ҃ Ņ‚Đ°ĐēĐž ŅˆŅ‚Đž Ņ›ĐĩŅ‚Đĩ ĐŊавĐĩŅŅ‚Đ¸ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ, аĐēĐž Đ¸Ņ… иĐŧа", + "duplicates_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đĩ ŅĐ˛Đ°Đē҃ ĐŗŅ€ŅƒĐŋ҃ Ņ‚Đ°ĐēĐž ŅˆŅ‚Đž ҆ˁĐĩŅ‚Đĩ ĐŊавĐĩŅŅ‚Đ¸ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ, аĐēĐž Đ¸Ņ… иĐŧа", "duration": "ĐĸŅ€Đ°Ņ˜Đ°ŅšĐĩ", "edit": "ĐŖŅ€Đĩди", "edit_album": "ĐŖŅ€Đĩди аĐģĐąŅƒĐŧ", @@ -794,45 +800,46 @@ "edit_key": "ИСĐŧĐĩĐŊи ĐēŅ™ŅƒŅ‡", "edit_link": "ĐŖŅ€Đĩди вĐĩĐˇŅƒ", "edit_location": "ĐŖŅ€Đĩди ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", - "edit_location_dialog_title": "Location", + "edit_location_dialog_title": "ЛоĐēĐ°Ņ†Đ¸Ņ˜Đ°", "edit_name": "ĐŖŅ€Đĩди иĐŧĐĩ", "edit_people": "ĐŖŅ€Đĩди ĐžŅĐžĐąĐĩ", "edit_tag": "ĐŖŅ€Đĩди ОСĐŊаĐē҃ (tag)", "edit_title": "ĐŖŅ€Đĩди Ņ‚Đ¸Ņ‚ŅƒĐģ҃", "edit_user": "ĐŖŅ€Đĩди ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "edited": "ĐŖŅ€ĐĩŅ’ĐĩĐŊĐž", - "editor": "Urednik", - "editor_close_without_save_prompt": "ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ĐŊĐĩŅ›Đĩ ĐąĐ¸Ņ‚Đ¸ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊĐĩ", + "editor": "ĐŖŅ€ĐĩĐ´ĐŊиĐē", + "editor_close_without_save_prompt": "ĐŸŅ€ĐžĐŧĐĩĐŊĐĩ ĐŊĐĩ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊĐĩ", "editor_close_without_save_title": "Đ—Đ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ŅƒŅ€ĐĩŅ’Đ¸Đ˛Đ°Ņ‡?", - "editor_crop_tool_h2_aspect_ratios": "ĐŸŅ€ĐžĐŋĐžŅ€Ņ†Đ¸Ņ˜Đĩ (aspect ratios)", + "editor_crop_tool_h2_aspect_ratios": "ĐŸŅ€ĐžĐŋĐžŅ€Ņ†Đ¸Ņ˜Đĩ (Đ°ŅĐŋĐĩ҆҂ Ņ€Đ°Ņ‚Đ¸ĐžŅ)", "editor_crop_tool_h2_rotation": "Đ ĐžŅ‚Đ°Ņ†Đ¸Ņ˜Đ°", "email": "Е-ĐŋĐžŅˆŅ‚Đ°", - "empty_folder": "This folder is empty", - "empty_trash": "Đ˜ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ҁĐŧĐĩŅ›Đĩ", - "empty_trash_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ҁĐŧĐĩŅ›Đĩ? Ово Ņ›Đĩ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ ҁĐŧĐĩŅ›Ņƒ иС Immich-a.\nNe ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ!", - "enable": "ОĐŧĐžĐŗŅƒŅ›Đ¸ (ЕĐŊайĐģĐĩ)", - "enabled": "ОĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž (enabled)", + "email_notifications": "ОбавĐĩŅˆŅ‚ĐĩŅšĐ° Đĩ-ĐŋĐžŅˆŅ‚ĐžĐŧ", + "empty_folder": "Ова ĐŧаĐŋа ҘĐĩ ĐŋŅ€Đ°ĐˇĐŊа", + "empty_trash": "Đ˜ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ҁĐŧĐĩ҆ˁĐĩ", + "empty_trash_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ҁĐŧĐĩ҆ˁĐĩ? Ово ҆ˁĐĩ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ŅĐ˛Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ ҁĐŧĐĩŅ†ĖŅƒ иС Immich-a.\nНĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸Ņ‚Đ¸ ĐžĐ˛Ņƒ Ņ€Đ°Đ´ŅšŅƒ!", + "enable": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ (ЕĐŊайĐģĐĩ)", + "enabled": "ОĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊĐž (ЕĐŊайĐģĐĩĐ´)", "end_date": "ĐšŅ€Đ°Ņ˜ŅšĐ¸ Đ´Đ°Ņ‚ŅƒĐŧ", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "ĐĄŅ‚Đ°Đ˛Ņ™ĐĩĐŊĐž ҃ Ņ€ĐĩĐ´", + "enter_wifi_name": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐŊаСив Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", "error": "Đ“Ņ€Đĩ҈Đēа", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "ĐŸŅ€ĐžĐŧĐĩĐŊа Ņ€ĐĩĐ´ĐžŅĐģĐĩда ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ŅšĐ° аĐģĐąŅƒĐŧа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģа", "error_delete_face": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐąŅ€Đ¸ŅĐ°ŅšŅƒ ĐžŅĐžĐąĐĩ иС Đ´ĐĩĐģа", "error_loading_image": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšŅƒ ҁĐģиĐēĐĩ", - "error_saving_image": "Error: {}", + "error_saving_image": "Đ“Ņ€Đĩ҈Đēа: {error}", "error_title": "Đ“Ņ€Đĩ҈Đēа – НĐĩŅˆŅ‚Đž ҘĐĩ ĐŋĐžŅˆĐģĐž ĐŊаОĐŋаĐēĐž", "errors": { - "cannot_navigate_next_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžŅ›Đ¸ Đ´Đž ҁĐģĐĩĐ´ĐĩŅ›Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "cannot_navigate_previous_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžŅ›Đ¸ Đ´Đž ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "cant_apply_changes": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ", - "cant_change_activity": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ {enabled, select, true {oneĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đ¸} other {ĐžĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đ¸}} аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚Đ¸", - "cant_change_asset_favorite": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚ Са Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", - "cant_change_metadata_assets_count": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ Са {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", + "cannot_navigate_next_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžŅ†ĖĐ¸ Đ´Đž ҁĐģĐĩĐ´Đĩ҆ˁĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "cannot_navigate_previous_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžŅ†ĖĐ¸ Đ´Đž ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "cant_apply_changes": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩ", + "cant_change_activity": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ {enabled, select, true {oneĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đ¸} other {ĐžĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đ¸}} аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚Đ¸", + "cant_change_asset_favorite": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚ Са Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", + "cant_change_metadata_assets_count": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ Са {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", "cant_get_faces": "НĐĩ ĐŧĐžĐŗŅƒ да ĐŊĐ°Ņ’ĐĩĐŧ ĐģĐ¸Ņ†Đ°", "cant_get_number_of_comments": "НĐĩ ĐŧĐžĐŗŅƒ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐąŅ€ĐžŅ˜ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ°", "cant_search_people": "НĐĩ ĐŧĐžĐŗŅƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‚Đ¸ ĐžŅĐžĐąĐĩ", "cant_search_places": "НĐĩ ĐŧĐžĐŗŅƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‚Đ¸ ĐŧĐĩŅŅ‚Đ°", - "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ›ĐĩĐŊи ĐŋĐžŅĐģОви Са: {job}", + "cleared_jobs": "ĐžŅ‡Đ¸ŅˆŅ†ĖĐĩĐŊи ĐŋĐžŅĐģОви Са: {job}", "error_adding_assets_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҃ аĐģĐąŅƒĐŧ", "error_adding_users_to_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐžĐ´Đ°Đ˛Đ°ŅšŅƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ҃ аĐģĐąŅƒĐŧ", "error_deleting_shared_user": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐąŅ€Đ¸ŅĐ°ŅšŅƒ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", @@ -840,7 +847,7 @@ "error_hiding_buy_button": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ҁĐēŅ€Đ¸Đ˛Đ°ŅšŅƒ Đ´ŅƒĐŗĐŧĐĩŅ‚Đ° Са Đē҃ĐŋОвиĐŊ҃", "error_removing_assets_from_album": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ҃ĐēĐģĐ°ŅšĐ°ŅšŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иС аĐģĐąŅƒĐŧа, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ĐēĐžĐŊСОĐģ҃ Са Đ˛Đ¸ŅˆĐĩ Đ´ĐĩŅ‚Đ°Ņ™Đ°", "error_selecting_all_assets": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ¸ĐˇĐąĐžŅ€Ņƒ ŅĐ˛Đ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", - "exclusion_pattern_already_exists": "ĐžĐ˛Đ°Ņ˜ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩŅšĐ° вĐĩŅ› ĐŋĐžŅŅ‚ĐžŅ˜Đ¸.", + "exclusion_pattern_already_exists": "ĐžĐ˛Đ°Ņ˜ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩŅšĐ° вĐĩ҆ˁ ĐŋĐžŅŅ‚ĐžŅ˜Đ¸.", "failed_job_command": "КоĐŧаĐŊда {command} ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģа Са ĐˇĐ°Đ´Đ°Ņ‚Đ°Đē: {job}", "failed_to_create_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", "failed_to_create_shared_link": "ĐŸŅ€Đ°Đ˛Ņ™ĐĩҚĐĩ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", @@ -849,179 +856,182 @@ "failed_to_keep_this_delete_others": "ĐĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž ĐˇĐ°Đ´Ņ€ĐļĐ°Đ˛Đ°ŅšĐĩ ĐžĐ˛ĐžĐŗ Đ´ĐĩĐģа и ĐąŅ€Đ¸ŅĐ°ŅšĐĩ ĐžŅŅ‚Đ°ĐģĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "failed_to_load_asset": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_load_assets": "ĐĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", + "failed_to_load_notifications": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_load_people": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ĐžŅĐžĐąĐ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_remove_product_key": "ĐŖĐēĐģĐ°ŅšĐ°ŅšĐĩ ĐēŅ™ŅƒŅ‡Đ° ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_stack_assets": "ĐĄĐģĐ°ĐŗĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "failed_to_unstack_assets": "Đ Đ°ŅĐēĐģаĐŋĐ°ŅšĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", - "import_path_already_exists": "Ова ĐŋŅƒŅ‚Đ°ŅšĐ° ŅƒĐ˛ĐžĐˇĐ° вĐĩŅ› ĐŋĐžŅŅ‚ĐžŅ˜Đ¸.", + "failed_to_update_notification_status": "АĐļŅƒŅ€Đ¸Ņ€Đ°ŅšĐĩ ŅŅ‚Đ°Ņ‚ŅƒŅĐ° ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", + "import_path_already_exists": "Ова ĐŋŅƒŅ‚Đ°ŅšĐ° ŅƒĐ˛ĐžĐˇĐ° вĐĩ҆ˁ ĐŋĐžŅŅ‚ĐžŅ˜Đ¸.", "incorrect_email_or_password": "НĐĩĐ¸ŅĐŋŅ€Đ°Đ˛Đ°ĐŊ e-mail иĐģи ĐģОСиĐŊĐēа", - "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚Đ°ŅšĐ° ĐŊĐ¸Ņ˜Đĩ ĐŋŅ€ĐžŅˆĐģа} few {# ĐŋŅƒŅ‚Đ°ŅšĐĩ ĐŊĐ¸ŅŅƒ ĐŋŅ€ĐžŅˆĐģĐĩ} other {# ĐŋŅƒŅ‚Đ°ŅšĐ° ĐŊĐ¸ŅŅƒ ĐŋŅ€ĐžŅˆĐģĐĩ}} ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ Đ˛Đ°Ņ™Đ°ĐŊĐžŅŅ‚Đ¸", - "profile_picture_transparent_pixels": "ĐĄĐģиĐēĐĩ ĐŋŅ€ĐžŅ„Đ¸Đģа ĐŊĐĩ ĐŧĐžĐŗŅƒ иĐŧĐ°Ņ‚Đ¸ ĐŋŅ€ĐžĐˇĐ¸Ņ€ĐŊĐĩ ĐŋиĐēҁĐĩĐģĐĩ. МоĐģиĐŧĐž ŅƒĐ˛ĐĩŅ›Đ°Ņ˜Ņ‚Đĩ и/иĐģи ĐŋĐžĐŧĐĩŅ€Đ¸Ņ‚Đĩ ҁĐģиĐē҃.", - "quota_higher_than_disk_size": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Đģи ҁ҂Đĩ ĐēĐ˛ĐžŅ‚Ņƒ вĐĩŅ›Ņƒ Од вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ¸ŅĐēа", - "repair_unable_to_check_items": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đ¸ {count, select, one {ŅŅ‚Đ°Đ˛Đē҃} other {ŅŅ‚Đ°Đ˛ĐēĐĩ}}", - "unable_to_add_album_users": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ҃ аĐģĐąŅƒĐŧ", - "unable_to_add_assets_to_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đĩ Đ´ĐĩŅ™ĐĩĐŊĐžŅ˜ вĐĩСи", - "unable_to_add_comment": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", - "unable_to_add_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", - "unable_to_add_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ Са ŅƒĐ˛ĐžĐˇ", - "unable_to_add_partners": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đĩ", + "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚Đ°ŅšĐ° ĐŊĐ¸Ņ˜Đĩ ĐŋŅ€ĐžŅˆĐģа} other {# ĐŋŅƒŅ‚Đ°Ņše ĐŊĐ¸ŅŅƒ ĐŋŅ€ĐžŅˆĐģĐĩ}} ĐŋŅ€ĐžĐ˛ĐĩŅ€Ņƒ Đ˛Đ°Ņ™Đ°ĐŊĐžŅŅ‚Đ¸", + "profile_picture_transparent_pixels": "ĐĄĐģиĐēĐĩ ĐŋŅ€ĐžŅ„Đ¸Đģа ĐŊĐĩ ĐŧĐžĐŗŅƒ иĐŧĐ°Ņ‚Đ¸ ĐŋŅ€ĐžĐˇĐ¸Ņ€ĐŊĐĩ ĐŋиĐēҁĐĩĐģĐĩ. МоĐģиĐŧĐž ŅƒĐ˛ĐĩŅ†ĖĐ°Ņ˜Ņ‚Đĩ и/иĐģи ĐŋĐžĐŧĐĩŅ€Đ¸Ņ‚Đĩ ҁĐģиĐē҃.", + "quota_higher_than_disk_size": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Đģи ҁ҂Đĩ ĐēĐ˛ĐžŅ‚Ņƒ вĐĩŅ†ĖŅƒ Од вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ Đ´Đ¸ŅĐēа", + "repair_unable_to_check_items": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đ¸ {count, select, one {ŅŅ‚Đ°Đ˛Đē҃} other {ŅŅ‚Đ°Đ˛ĐēĐĩ}}", + "unable_to_add_album_users": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ҃ аĐģĐąŅƒĐŧ", + "unable_to_add_assets_to_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đĩ Đ´ĐĩŅ™ĐĩĐŊĐžŅ˜ вĐĩСи", + "unable_to_add_comment": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", + "unable_to_add_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", + "unable_to_add_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ Са ŅƒĐ˛ĐžĐˇ", + "unable_to_add_partners": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đĩ", "unable_to_add_remove_archive": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ {archived, select, true {҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иС} other {Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃}} Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ", - "unable_to_add_remove_favorites": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ {favorite, select, true {Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃} other {҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иС}} Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚a", + "unable_to_add_remove_favorites": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ {favorite, select, true {Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃} other {҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иС}} Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ", "unable_to_archive_unarchive": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ {archived, select, true {Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đ¸} other {Đ´Đĩ-Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đ¸}}", - "unable_to_change_album_user_role": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ҃ĐģĐžĐŗŅƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа аĐģĐąŅƒĐŧа", - "unable_to_change_date": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ŅƒĐŧ", - "unable_to_change_favorite": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚ Са Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃/Đĩ", - "unable_to_change_location": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", - "unable_to_change_password": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐģОСиĐŊĐē҃", - "unable_to_change_visibility": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Đ˛Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ Са {count, plural, one {# ĐžŅĐžĐąŅƒ} other {# ĐžŅĐžĐąĐĩ}}", - "unable_to_complete_oauth_login": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ˛Ņ€ŅˆĐ¸Ņ‚Đ¸ OAuth ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", - "unable_to_connect": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ҁĐĩ", - "unable_to_connect_to_server": "НĐĩĐŧĐžĐŗŅƒŅ›Đĩ ҘĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ҁĐĩ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", - "unable_to_copy_to_clipboard": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đ¸ ҃ ĐŧĐĩŅ’ŅƒŅĐŋŅ€ĐĩĐŧĐŊиĐē (҆ĐģиĐŋĐąĐžĐ°Ņ€Đ´), ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đģи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ¸ ĐŋŅ€ĐĩĐēĐž https-a", - "unable_to_create_admin_account": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēи ĐŊаĐģĐžĐŗ", - "unable_to_create_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ĐŊОви АПИ ĐēŅ™ŅƒŅ‡ (key)", - "unable_to_create_library": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", - "unable_to_create_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "unable_to_delete_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", - "unable_to_delete_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "unable_to_change_album_user_role": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ҃ĐģĐžĐŗŅƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа аĐģĐąŅƒĐŧа", + "unable_to_change_date": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ŅƒĐŧ", + "unable_to_change_favorite": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚ Са Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃/Đĩ", + "unable_to_change_location": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "unable_to_change_password": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐģОСиĐŊĐē҃", + "unable_to_change_visibility": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ Đ˛Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ Са {count, plural, one {# ĐžŅĐžĐąŅƒ} other {# ĐžŅĐžĐąĐĩ}}", + "unable_to_complete_oauth_login": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ˛Ņ€ŅˆĐ¸Ņ‚Đ¸ OAuth ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ", + "unable_to_connect": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ҁĐĩ", + "unable_to_connect_to_server": "НĐĩĐŧĐžĐŗŅƒŅ†ĖĐĩ ҘĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ҁĐĩ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", + "unable_to_copy_to_clipboard": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đ¸ ҃ ĐŧĐĩŅ’ŅƒŅĐŋŅ€ĐĩĐŧĐŊиĐē (҆ĐģиĐŋĐąĐžĐ°Ņ€Đ´), ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đģи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ¸ ĐŋŅ€ĐĩĐēĐž ҅҂҂Đŋҁ-а", + "unable_to_create_admin_account": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēи ĐŊаĐģĐžĐŗ", + "unable_to_create_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ĐŊОви АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", + "unable_to_create_library": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", + "unable_to_create_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", + "unable_to_delete_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", + "unable_to_delete_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", "unable_to_delete_assets": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐąŅ€Đ¸ŅĐ°ŅšŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", - "unable_to_delete_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", - "unable_to_delete_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ Са ŅƒĐ˛ĐžĐˇ", - "unable_to_delete_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ Đ´ĐĩŅ™ĐĩĐŊи link", - "unable_to_delete_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "unable_to_download_files": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐĩŅƒĐˇĐĩŅ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "unable_to_edit_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ иСĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", - "unable_to_edit_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ иСĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ ŅƒĐ˛ĐžĐˇĐ°", - "unable_to_empty_trash": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đ¸ ĐžŅ‚Đŋад", - "unable_to_enter_fullscreen": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŋŅ€ĐĩĐēĐž ҆ĐĩĐģĐžĐŗ ĐĩĐēŅ€Đ°ĐŊа", - "unable_to_exit_fullscreen": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐ°Ņ›Đ¸ иС ҆ĐĩĐģĐžĐŗ ĐĩĐēŅ€Đ°ĐŊа", - "unable_to_get_comments_number": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐąŅ€ĐžŅ˜ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ°", + "unable_to_delete_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", + "unable_to_delete_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ Са ŅƒĐ˛ĐžĐˇ", + "unable_to_delete_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ Đ´ĐĩŅ™ĐĩĐŊи link", + "unable_to_delete_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", + "unable_to_download_files": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐĩŅƒĐˇĐĩŅ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "unable_to_edit_exclusion_pattern": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ иСĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐˇĐ°Ņ† Đ¸ĐˇŅƒĐˇĐ¸ĐŧĐ°ŅšĐ°", + "unable_to_edit_import_path": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ иСĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŋŅƒŅ‚Đ°ŅšŅƒ ŅƒĐ˛ĐžĐˇĐ°", + "unable_to_empty_trash": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đ¸ ĐžŅ‚Đŋад", + "unable_to_enter_fullscreen": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŋŅ€ĐĩĐēĐž ҆ĐĩĐģĐžĐŗ ĐĩĐēŅ€Đ°ĐŊа", + "unable_to_exit_fullscreen": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐ°Ņ†ĖĐ¸ иС ҆ĐĩĐģĐžĐŗ ĐĩĐēŅ€Đ°ĐŊа", + "unable_to_get_comments_number": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐąŅ€ĐžŅ˜ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ°", "unable_to_get_shared_link": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ Đ´ĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "unable_to_hide_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°ĐēŅ€Đ¸Ņ‚Đ¸ ĐžŅĐžĐąŅƒ", - "unable_to_link_motion_video": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ (link) видĐĩĐž ҁĐŊиĐŧаĐē", - "unable_to_link_oauth_account": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ OAuth ĐŊаĐģĐžĐŗ", - "unable_to_load_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", - "unable_to_load_asset_activity": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ ҁҀĐĩĐ´ŅŅ‚Đ°Đ˛Đ°", - "unable_to_load_items": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ ŅŅ‚Đ°Đ˛ĐēĐĩ", - "unable_to_load_liked_status": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ ŅŅ‚Đ°Ņ‚ŅƒŅ ŅĐ˛Đ¸Ņ’Đ°ŅšĐ°", - "unable_to_log_out_all_devices": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐžĐ´â€ŒŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ŅĐ˛Đĩ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đĩ", - "unable_to_log_out_device": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐžĐ´â€ŒŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ŅƒŅ€ĐĩŅ’Đ°Ņ˜", - "unable_to_login_with_oauth": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ҁĐĩ ĐŋĐžĐŧĐžŅ›Ņƒ OAuth-a", + "unable_to_link_motion_video": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ видĐĩĐž ŅĐ° ҁĐģиĐēĐžĐŧ", + "unable_to_link_oauth_account": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ OAuth ĐŊаĐģĐžĐŗ", + "unable_to_load_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", + "unable_to_load_asset_activity": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ ҁҀĐĩĐ´ŅŅ‚Đ°Đ˛Đ°", + "unable_to_load_items": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ ŅŅ‚Đ°Đ˛ĐēĐĩ", + "unable_to_load_liked_status": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ŅƒŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ ŅŅ‚Đ°Ņ‚ŅƒŅ ŅĐ˛Đ¸Ņ’Đ°ŅšĐ°", + "unable_to_log_out_all_devices": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ŅĐ˛Đĩ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đĩ", + "unable_to_log_out_device": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ŅƒŅ€ĐĩŅ’Đ°Ņ˜", + "unable_to_login_with_oauth": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ҁĐĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ OAuth-а", "unable_to_play_video": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ видĐĩĐž", - "unable_to_reassign_assets_existing_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐĩŅ€Đ°ŅĐŋОдĐĩĐģĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊа {name, select, null {ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›Ņƒ ĐžŅĐžĐąŅƒ} other {{name}}}", - "unable_to_reassign_assets_new_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐĩĐŊĐĩŅ‚Đ¸ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐŊĐžĐ˛ĐžŅ˜ ĐžŅĐžĐąĐ¸", + "unable_to_reassign_assets_existing_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐĩŅ€Đ°ŅĐŋОдĐĩĐģĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊа {name, select, null {ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ†ĖŅƒ ĐžŅĐžĐąŅƒ} other {{name}}}", + "unable_to_reassign_assets_new_person": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐĩĐŊĐĩŅ‚Đ¸ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐŊĐžĐ˛ĐžŅ˜ ĐžŅĐžĐąĐ¸", "unable_to_refresh_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "unable_to_remove_album_users": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ иС аĐģĐąŅƒĐŧа", - "unable_to_remove_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ АПИ ĐēŅ™ŅƒŅ‡ (key)", - "unable_to_remove_assets_from_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đĩ ŅĐ° Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа", + "unable_to_remove_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", + "unable_to_remove_assets_from_shared_link": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đĩ ŅĐ° Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа", "unable_to_remove_deleted_assets": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ваĐŊĐŧŅ€ĐĩĐļĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", "unable_to_remove_library": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "unable_to_remove_partner": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "unable_to_remove_reaction": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ Ņ€ĐĩаĐēŅ†Đ¸Ņ˜Ņƒ", "unable_to_repair_items": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋĐžĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ŅŅ‚Đ°Đ˛ĐēĐĩ", "unable_to_reset_password": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Ņ€ĐĩҁĐĩŅ‚ĐžĐ˛Đ°Ņ‚Đ¸ ĐģОСиĐŊĐē҃", + "unable_to_reset_pin_code": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Ņ€ĐĩҁĐĩŅ‚ĐžĐ˛Đ°Ņ‚Đ¸ ПИН ĐēОд", "unable_to_resolve_duplicate": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đ¸ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚", - "unable_to_restore_assets": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "unable_to_restore_assets": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", "unable_to_restore_trash": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋĐžĐ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đ¸ ĐžŅ‚Đŋад", "unable_to_restore_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋĐžĐ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "unable_to_save_album": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ аĐģĐąŅƒĐŧ", - "unable_to_save_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ АПИ ĐēŅ™ŅƒŅ‡ (key)", - "unable_to_save_date_of_birth": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ°", + "unable_to_save_api_key": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", + "unable_to_save_date_of_birth": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ°", "unable_to_save_name": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ иĐŧĐĩ", "unable_to_save_profile": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋŅ€ĐžŅ„Đ¸Đģ", "unable_to_save_settings": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "unable_to_scan_libraries": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "unable_to_scan_library": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", - "unable_to_set_feature_photo": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ Đ¸ŅŅ‚Đ°ĐēĐŊŅƒŅ‚Ņƒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ", + "unable_to_set_feature_photo": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ Đ¸ŅŅ‚Đ°ĐēĐŊŅƒŅ‚Ņƒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ", "unable_to_set_profile_picture": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ ĐŋŅ€ĐžŅ„Đ¸ĐģĐŊ҃ ҁĐģиĐē҃", "unable_to_submit_job": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐĩĐ´Đ°Ņ‚Đ¸ ĐˇĐ°Đ´Đ°Ņ‚Đ°Đē", - "unable_to_trash_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Đ¸ĐˇĐąĐ°Ņ†Đ¸Ņ‚Đ¸ ĐŧĐ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đ°Đģ ҃ ĐžŅ‚Đŋад", + "unable_to_trash_asset": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ¸ĐˇĐąĐ°Ņ†Đ¸Ņ‚Đ¸ ĐŧĐ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đ°Đģ ҃ ĐžŅ‚Đŋад", "unable_to_unlink_account": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ Ņ€Đ°ŅĐēиĐŊŅƒŅ‚Đ¸ ĐŋŅ€ĐžŅ„Đ¸Đģ", - "unable_to_unlink_motion_video": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸ вĐĩĐˇŅƒ ŅĐ° видĐĩĐž ҁĐŊиĐŧĐēĐžĐŧ", - "unable_to_update_album_cover": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŊĐ°ŅĐģОвĐŊĐ¸Ņ†Ņƒ аĐģĐąŅƒĐŧа", - "unable_to_update_album_info": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž аĐģĐąŅƒĐŧ҃", + "unable_to_unlink_motion_video": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ОдвĐĩĐˇĐ°Ņ‚Đ¸ видĐĩĐž Од ҁĐģиĐēĐĩ", + "unable_to_update_album_cover": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŊĐ°ŅĐģОвĐŊĐ¸Ņ†Ņƒ аĐģĐąŅƒĐŧа", + "unable_to_update_album_info": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž аĐģĐąŅƒĐŧ҃", "unable_to_update_library": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "unable_to_update_location": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", "unable_to_update_settings": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "unable_to_update_timeline_display_status": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ŅŅ‚Đ°Ņ‚ŅƒŅ ĐŋŅ€Đ¸ĐēаСа Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐĩ ĐģиĐŊĐ¸Ņ˜Đĩ", "unable_to_update_user": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "unable_to_upload_file": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃" + "unable_to_upload_file": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃" }, - "exif": "EXIF", - "exif_bottom_sheet_description": "Add Description...", - "exif_bottom_sheet_details": "DETAILS", - "exif_bottom_sheet_location": "LOCATION", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_description": "Đ”ĐžĐ´Đ°Ņ˜ ĐžĐŋĐ¸Ņ...", + "exif_bottom_sheet_details": "ДЕĐĸАЛЈИ", + "exif_bottom_sheet_location": "ЛОКАĐĻИЈА", + "exif_bottom_sheet_people": "ПЕОПЛЕ", + "exif_bottom_sheet_person_add_person": "Адд name", + "exif_bottom_sheet_person_age": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ {age}", + "exif_bottom_sheet_person_age_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ {months} ĐŧĐĩҁĐĩŅ†Đ¸", + "exif_bottom_sheet_person_age_year_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ 1 ĐŗĐžĐ´Đ¸ĐŊа, {months} ĐŧĐĩҁĐĩŅ†Đ¸", + "exif_bottom_sheet_person_age_years": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ {years}", "exit_slideshow": "Đ˜ĐˇĐ°Ņ’Đ¸ иС ĐŋŅ€ĐžŅ˜ĐĩĐēŅ†Đ¸Ņ˜Đĩ ҁĐģĐ°Ņ˜Đ´ĐžĐ˛Đ°", "expand_all": "ĐŸŅ€ĐžŅˆĐ¸Ņ€Đ¸ ŅĐ˛Đĩ", - "experimental_settings_new_asset_list_subtitle": "Work in progress", - "experimental_settings_new_asset_list_title": "Enable experimental photo grid", - "experimental_settings_subtitle": "Use at your own risk!", - "experimental_settings_title": "Experimental", + "experimental_settings_new_asset_list_subtitle": "ĐŖ Đ¸ĐˇŅ€Đ°Đ´Đ¸", + "experimental_settings_new_asset_list_title": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ĐĩĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊи ĐŧŅ€ĐĩĐļĐŊи ĐŋŅ€Đ¸ĐēаС Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "experimental_settings_subtitle": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐŊа ŅĐžĐŋŅŅ‚Đ˛ĐĩĐŊ҃ ĐžĐ´ĐŗĐžĐ˛ĐžŅ€ĐŊĐžŅŅ‚!", + "experimental_settings_title": "ЕĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž", "expire_after": "Да Đ¸ŅŅ‚ĐĩĐēĐŊĐĩ ĐŊаĐēĐžĐŊ", "expired": "Đ˜ŅŅ‚ĐĩĐēĐģĐž", "expires_date": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ {date}", "explore": "Đ˜ŅŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ", - "explorer": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡ (Explorer)", + "explorer": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡ (ЕxĐŋĐģĐžŅ€ĐĩŅ€)", "export": "ИСвĐĩСи", "export_as_json": "ИСвĐĩСи ЈСОН", - "extension": "ЕĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đ° (Extension)", + "extension": "ЕĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đ° (ЕxŅ‚ĐĩĐŊŅĐ¸ĐžĐŊ)", "external": "ĐĄĐŋĐžŅ™Đ°ŅˆŅšĐ¸", "external_libraries": "ĐĄĐŋĐžŅ™Đ°ŅˆŅšĐĩ БибĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "ĐĄĐŋĐžŅ™ĐŊа ĐŧŅ€ĐĩĐļа", + "external_network_sheet_info": "Када ĐŊĐ¸Ņ˜Đĩ ĐŊа ĐļĐĩŅ™ĐĩĐŊĐžŅ˜ Wi-Fi ĐŧŅ€ĐĩĐļи, аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ҆ˁĐĩ ҁĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ ĐŋŅ€ĐĩĐēĐž ĐŋŅ€Đ˛Đĩ Од Đ´ĐžĐģĐĩ ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… URL Đ°Đ´Ņ€ĐĩŅĐ° Đ´Đž ĐēĐžŅ˜Đ¸Ņ… ĐŧĐžĐļĐĩ да Đ´ĐžŅ’Đĩ, ĐŋĐžŅ‡ĐĩĐ˛ŅˆĐ¸ Од Đ˛Ņ€Ņ…Đ° Đ´Đž Đ´ĐŊа", "face_unassigned": "НĐĩŅ€Đ°ŅĐŋĐžŅ€ĐĩŅ’ĐĩĐŊи", - "failed": "Failed", - "failed_to_load_assets": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ҁҀĐĩĐ´ŅŅ‚Đ°Đ˛Đ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", - "failed_to_load_folder": "Failed to load folder", + "failed": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", + "failed_to_load_assets": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐŊĐ¸ŅŅƒ ҃ҁĐŋĐĩ҈ĐŊĐž ŅƒŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ", + "failed_to_load_folder": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", "favorite": "Đ¤Đ°Đ˛ĐžŅ€Đ¸Ņ‚", "favorite_or_unfavorite_photo": "ОĐŧĐ¸Ņ™ĐĩĐŊа иĐģи ĐŊĐĩĐžĐŧĐ¸Ņ™ĐĩĐŊа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", "favorites": "Đ¤Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đ¸", - "favorites_page_no_favorites": "No favorite assets found", + "favorites_page_no_favorites": "ĐĐ¸Ņ˜Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊ ĐŊĐ¸Ņ˜ĐĩдаĐŊ ĐžĐŧĐ¸Ņ™ĐĩĐŊи ĐŧĐ°Ņ‚ĐĩŅ€Đ¸Ņ˜Đ°Đģ", "feature_photo_updated": "ГĐģавĐŊа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа", - "features": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ", + "features": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ (Ņ„ĐĩĐ°Ņ‚ŅƒŅ€Đĩҁ)", "features_setting_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ°Đŧа аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", "file_name": "Назив Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°", "file_name_or_extension": "ИĐŧĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иĐģи ĐĩĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đ°", "filename": "ИĐŧĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", "filetype": "Đ’Ņ€ŅŅ‚Đ° Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°", - "filter": "Filter", + "filter": "ФиĐģŅ‚ĐĩŅ€", "filter_people": "ФиĐģŅ‚Ņ€Đ¸Ņ€Đ°ŅšĐĩ ĐžŅĐžĐąĐ°", - "find_them_fast": "Đ‘Ņ€ĐˇĐž Đ¸Ņ… ĐŋŅ€ĐžĐŊĐ°Ņ’Đ¸Ņ‚Đĩ ĐŋĐž иĐŧĐĩĐŊ҃ ĐŋĐžĐŧĐžŅ›Ņƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", + "filter_places": "ФиĐģŅ‚Ņ€Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŧĐĩŅŅ‚Đ°", + "find_them_fast": "Đ‘Ņ€ĐˇĐž Đ¸Ņ… ĐŋŅ€ĐžĐŊĐ°Ņ’Đ¸Ņ‚Đĩ ĐŋĐž иĐŧĐĩĐŊ҃ ĐŋĐžĐŧĐžŅ†ĖŅƒ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", "fix_incorrect_match": "Đ˜ŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ ĐŊĐĩŅ‚Đ°Ņ‡ĐŊĐž ĐŋĐžĐ´ŅƒĐ´Đ°Ņ€Đ°ŅšĐĩ", - "folder": "Folder", - "folder_not_found": "Folder not found", - "folders": "Đ¤Đ°ŅŅ†Đ¸ĐēĐģĐĩ (Folders)", - "folders_feature_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Đ˛Đ°ŅšĐĩ ĐŋŅ€Đ¸ĐēаСа Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ Са Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ҃ ŅĐ¸ŅŅ‚ĐĩĐŧ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", + "folder": "Đ¤Đ°ŅŅ†Đ¸ĐēĐģа", + "folder_not_found": "Đ¤Đ°ŅŅ†Đ¸ĐēĐģа ĐŊĐ¸Ņ˜Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊа", + "folders": "Đ¤Đ°ŅŅ†Đ¸ĐēĐģĐĩ (ФОĐģĐ´ĐĩҀҁ)", + "folders_feature_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Đ˛Đ°ŅšĐĩ ĐŋŅ€Đ¸ĐēаСа Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ Са Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐ° ҃ ŅĐ¸ŅŅ‚ĐĩĐŧ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "forward": "НаĐŋŅ€ĐĩĐ´", "general": "ГĐĩĐŊĐĩŅ€Đ°ĐģĐŊĐž", - "get_help": "ĐĐ°Ņ’Đ¸ ĐŋĐžĐŧĐžŅ›", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_help": "ĐĐ°Ņ’Đ¸ ĐŋĐžĐŧĐžŅ†Ė", + "get_wifiname_error": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐąĐ¸Ņ‚Đ¸ иĐŧĐĩ Wi-Fi ĐŧŅ€ĐĩĐļĐĩ. ĐŖĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ҁĐĩ да ҁ҂Đĩ даĐģи ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊĐĩ дОСвОĐģĐĩ и да ҁ҂Đĩ ĐŋОвĐĩСаĐŊи ĐŊа Wi-Fi ĐŧŅ€ĐĩĐļ҃", "getting_started": "ĐŸĐžŅ‡Đ¸ŅšĐĩĐŧ", "go_back": "Đ’Ņ€Đ°Ņ‚Đ¸ ҁĐĩ", "go_to_folder": "Иди ҃ Ņ„Đ°ŅŅ†Đ¸ĐēĐģ҃", "go_to_search": "Иди ĐŊа ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", - "grant_permission": "Grant permission", + "grant_permission": "Đ”Đ°Ņ˜ дОСвОĐģ҃", "group_albums_by": "Đ“Ņ€ŅƒĐŋĐŊи аĐģĐąŅƒĐŧи ĐŋĐž...", "group_country": "Đ“Ņ€ŅƒĐŋа ĐŋĐž Đ´Ņ€Đļава", "group_no": "БĐĩС ĐŗŅ€ŅƒĐŋĐ¸ŅĐ°ŅšĐ°", "group_owner": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŋĐž вĐģĐ°ŅĐŊиĐē҃", "group_places_by": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŧĐĩŅŅ‚Đ° ĐŋĐž...", "group_year": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐŋĐž ĐŗĐžĐ´Đ¸ĐŊи", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", + "haptic_feedback_switch": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸ Ņ…Đ°ĐŋŅ‚Đ¸Ņ‡Đē҃ ĐŋĐžĐ˛Ņ€Đ°Ņ‚ĐŊ҃ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Ņƒ", + "haptic_feedback_title": "ĐĨаĐŋŅ‚Đ¸Ņ‡ĐēĐĩ ĐŋĐžĐ˛Ņ€Đ°Ņ‚ĐŊĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ", "has_quota": "ИĐŧа ĐēĐ˛ĐžŅ‚Ņƒ", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Đ”ĐžĐ´Đ°Ņ˜ ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ™Đĩ", + "header_settings_field_validator_msg": "Đ’Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐąĐ¸Ņ‚Đ¸ ĐŋŅ€Đ°ĐˇĐŊа", + "header_settings_header_name_input": "Назив ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ™Đ°", + "header_settings_header_value_input": "Đ’Ņ€ĐĩĐ´ĐŊĐžŅŅ‚ ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ™Đ°", + "headers_settings_tile_subtitle": "ДĐĩŅ„Đ¸ĐŊĐ¸ŅˆĐ¸Ņ‚Đĩ ĐŋŅ€ĐžĐēŅĐ¸ ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ™Đ° ĐēĐžŅ˜Đ° аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ҂ҀĐĩйа да ŅˆĐ°Ņ™Đĩ ŅĐ° ŅĐ˛Đ°ĐēиĐŧ ĐŧŅ€ĐĩĐļĐŊиĐŧ ĐˇĐ°Ņ…Ņ‚ĐĩвОĐŧ", + "headers_settings_tile_title": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи ĐŋŅ€ĐžĐēŅĐ¸ ĐˇĐ°ĐŗĐģĐ°Đ˛Ņ†Đ¸", "hi_user": "Đ—Đ´Ņ€Đ°Đ˛Đž {name} ({email})", "hide_all_people": "ХаĐēŅ€Đ¸Ņ˜ ŅĐ˛Đĩ ĐžŅĐžĐąĐĩ", "hide_gallery": "ХаĐēŅ€Đ¸Ņ˜ ĐŗĐ°ĐģĐĩŅ€Đ¸Ņ˜Ņƒ", @@ -1029,41 +1039,42 @@ "hide_password": "ХаĐēŅ€Đ¸Ņ˜ ĐģОСиĐŊĐē҃", "hide_person": "ХаĐēŅ€Đ¸Ņ˜ ĐžŅĐžĐąŅƒ", "hide_unnamed_people": "ХаĐēŅ€Đ¸Ņ˜ ĐŊĐĩиĐŧĐĩĐŊОваĐŊĐĩ ĐžŅĐžĐąĐĩ", - "home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.", - "home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping", - "home_page_add_to_album_success": "Added {added} assets to album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", - "home_page_building_timeline": "Building the timeline", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", - "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", - "host": "ДоĐŧĐ°Ņ›Đ¸ĐŊ (ĐĨĐžŅŅ‚)", + "home_page_add_to_album_conflicts": "Đ”ĐžĐ´Đ°Ņ‚ {added} СаĐŋĐ¸Ņ ҃ аĐģĐąŅƒĐŧ {album}. {failed} СаĐŋĐ¸ŅĐ¸ ҁ҃ вĐĩŅ› ҃ аĐģĐąŅƒĐŧ҃.", + "home_page_add_to_album_err_local": "ĐĸŅ€ĐĩĐŊŅƒŅ‚ĐŊĐž ĐŊĐĩĐŧĐžĐŗŅƒŅ›Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐģĐžĐēаĐģĐŊĐĩ СаĐŋĐ¸ŅĐĩ ҃ аĐģĐąŅƒĐŧĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ†Ņƒ ҁĐĩ", + "home_page_add_to_album_success": "Доdate {added} ŅŅ‚Đ°Đ˛ĐēĐĩ ҃ аĐģĐąŅƒĐŧ {album}.", + "home_page_album_err_partner": "ĐˆĐžŅˆ ŅƒĐ˛ĐĩĐē ĐŊĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩҀҁĐēа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ҃ аĐģĐąŅƒĐŧ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_archive_err_local": "ĐˆĐžŅˆ ŅƒĐ˛ĐĩĐē ĐŊĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đ¸ ĐģĐžĐēаĐģĐŊĐĩ Ņ€ĐĩŅŅƒŅ€ŅĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_archive_err_partner": "НĐĩ ĐŧĐžĐŗŅƒ да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Đŧ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩҀҁĐē҃ иĐŧОвиĐŊ҃, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_building_timeline": "ĐšŅ€ĐĩĐ¸Ņ€Đ°ŅšĐĩ Ņ…Ņ€ĐžĐŊĐžĐģĐžŅˆĐēĐĩ ĐģиĐŊĐ¸Ņ˜Đĩ", + "home_page_delete_err_partner": "НĐĩ ĐŧĐžĐŗŅƒ да ĐžĐąŅ€Đ¸ŅˆĐĩĐŧ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩҀҁĐē҃ иĐŧОвиĐŊ҃, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_delete_remote_err_local": "ЛоĐēаĐģĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ҃ ĐžĐąŅ€Đ¸ŅĐ°Đ˛Đ°ŅšŅƒ ŅƒĐ´Đ°Ņ™ĐĩĐŊĐžĐŗ Đ¸ĐˇĐąĐžŅ€Đ°, ĐŋŅ€ĐĩҁĐēаĐēĐ°ŅšĐĩ", + "home_page_favorite_err_local": "ĐĸŅ€ĐĩĐŊŅƒŅ‚ĐŊĐž ĐŊĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†Đĩ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐģĐžĐēаĐģĐŊĐĩ СаĐŋĐ¸ŅĐĩ ҃ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ†Ņƒ ҁĐĩ", + "home_page_favorite_err_partner": "ĐˆĐžŅˆ ŅƒĐ˛ĐĩĐē ĐŊĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ОСĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩҀҁĐēĐĩ Ņ€ĐĩŅŅƒŅ€ŅĐĩ ĐēаО ĐžĐŧĐ¸Ņ™ĐĩĐŊĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_first_time_notice": "АĐēĐž ҘĐĩ ОвО ĐŋŅ€Đ˛Đ¸ ĐŋŅƒŅ‚ да ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ, ĐŧĐžĐģиĐŧĐž Đ’Đ°Ņ да ОдайĐĩŅ€ĐĩŅ‚Đĩ аĐģĐąŅƒĐŧĐĩ ĐēĐžŅ˜Đĩ ĐļĐĩĐģĐ¸Ņ‚Đĩ да ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đĩ", + "home_page_share_err_local": "НĐĩ ĐŧĐžĐŗŅƒ да Đ´ĐĩĐģиĐŧ ĐģĐžĐēаĐģĐŊĐĩ Ņ€ĐĩŅŅƒŅ€ŅĐĩ ĐŋŅ€ĐĩĐēĐž linkа, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "home_page_upload_err_limit": "МоĐļĐĩŅ‚Đĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đ¸ ĐŊĐ°Ņ˜Đ˛Đ¸ŅˆĐĩ 30 ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° Đ¸ŅŅ‚ĐžĐ˛Ņ€ĐĩĐŧĐĩĐŊĐž, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ŅƒŅ†ĖĐ¸", + "host": "ДоĐŧĐ°Ņ†ĖĐ¸ĐŊ (ĐĨĐžŅŅ‚)", "hour": "ĐĄĐ°Ņ‚", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", + "id": "ИД", + "ignore_icloud_photos": "Đ˜ĐŗĐŊĐžŅ€Đ¸ŅˆĐ¸Ņ‚Đĩ иĐĻĐģĐžŅƒĐ´ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", + "ignore_icloud_photos_description": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ĐēĐžŅ˜Đĩ ҁ҃ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊĐĩ ĐŊа иĐĻĐģĐžŅƒĐ´-҃ ĐŊĐĩ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐĩ ĐŊа Immich ҁĐĩŅ€Đ˛ĐĩŅ€", "image": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", - "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž {date}", - "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž {person1} {date}", - "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧиĐģи {person1} и {person2} {date}", - "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧиĐģи {person1}, {person2}, и {person3} {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧиĐģи {person1}, {person2}, и {additionalCount, number} ĐžŅŅ‚Đ°ĐģĐ¸Ņ… {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1} {date}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1} и {person2} {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1}, {person2}, и {person3} {date}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1}, {person2}, и {additionalCount, number} Đ´Ņ€ŅƒĐŗĐ¸Ņ… {date}", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", - "image_viewer_page_state_provider_download_success": "Download Success", - "image_viewer_page_state_provider_share_error": "Share Error", + "image_alt_text_date": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ŅĐ° {person1} {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ŅĐ° {person1} и {person2} {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ŅĐ° {person1}, {person2} и {person3} {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ŅĐ° {person1}, {person2} и Ņ˜ĐžŅˆ {additionalCount, number} ĐžŅŅ‚Đ°ĐģĐ¸Ņ… {date}", + "image_alt_text_date_place": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1} {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1} и {person2} {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐžŅƒ {city}, {country} ŅĐ° {person1}, {person2} и {person3} {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {ВидĐĩĐž} other {Image}} ҁĐŊиĐŧŅ™ĐĩĐŊĐž ҃ {city}, {country} ŅĐ° {person1}, {person2} и Ņ˜ĐžŅˆ {additionalCount, number} Đ´Ņ€ŅƒĐŗĐ¸Ņ… {date}", + "image_saved_successfully": "ĐĄĐģиĐēа ҘĐĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊа", + "image_viewer_page_state_provider_download_started": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ҘĐĩ СаĐŋĐžŅ‡ĐĩŅ‚Đž", + "image_viewer_page_state_provider_download_success": "ĐŸŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐĩ ĐŖŅĐŋĐĩ҈ĐŊĐž", + "image_viewer_page_state_provider_share_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐĩŅ™ĐĩҚ҃", "immich_logo": "Đ›ĐžĐŗĐž Immich-a", - "immich_web_interface": "Web иĐŊŅ‚ĐĩҀ҄ĐĩҘҁ Immich-a", + "immich_web_interface": "WĐĩĐą иĐŊŅ‚ĐĩҀ҄ĐĩҘҁ Immich-a", "import_from_json": "ĐŖĐ˛ĐĩСи иС ЈСОН-а", "import_path": "ĐŸŅƒŅ‚Đ°ŅšĐ° ŅƒĐ˛ĐžĐˇĐ°", "in_albums": "ĐŖ {count, plural, one {# аĐģĐąŅƒĐŧ҃} few {# аĐģĐąŅƒĐŧа} other {# аĐģĐąŅƒĐŧа}}", @@ -1080,8 +1091,8 @@ "night_at_midnight": "ХваĐēа ĐŊĐžŅ› ҃ ĐŋĐžĐŊĐžŅ›", "night_at_twoam": "ХваĐēа ĐŊĐžŅ› ҃ 2аĐŧ" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "НĐĩваĐļĐĩŅ†ĖĐ¸ Đ´Đ°Ņ‚ŅƒĐŧ", + "invalid_date_format": "НĐĩваĐļĐĩŅ†ĖĐ¸ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚ŅƒĐŧа", "invite_people": "ĐŸĐžĐˇĐžĐ˛Đ¸Ņ‚Đĩ Ņ™ŅƒĐ´Đĩ", "invite_to_album": "Позови ĐŊа аĐģĐąŅƒĐŧ", "items_count": "{count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", @@ -1102,110 +1113,111 @@ "level": "Ниво", "library": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа", "library_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", - "library_page_device_albums": "Albums on Device", - "library_page_new_album": "New album", - "library_page_sort_asset_count": "Number of assets", - "library_page_sort_created": "Created date", - "library_page_sort_last_modified": "Last modified", - "library_page_sort_title": "Album title", + "library_page_device_albums": "АĐģĐąŅƒĐŧи ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ", + "library_page_new_album": "Нови аĐģĐąŅƒĐŧ", + "library_page_sort_asset_count": "Đ‘Ņ€ĐžŅ˜ ҁҀĐĩĐ´ŅŅ‚Đ°Đ˛Đ°", + "library_page_sort_created": "ĐĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Đĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°ĐŊĐž", + "library_page_sort_last_modified": "ĐŸĐžŅĐģĐĩĐ´ŅšĐ° иСĐŧĐĩĐŊа", + "library_page_sort_title": "Назив аĐģĐąŅƒĐŧа", "light": "ХвĐĩŅ‚ĐģĐž", "like_deleted": "Đ›Đ°Ņ˜Đē҃Ҙ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐž", "link_motion_video": "НаĐŋŅ€Đ°Đ˛Đ¸ вĐĩĐˇŅƒ Са видĐĩĐž СаĐŋĐ¸Ņ", "link_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ вĐĩСĐĩ", - "link_to_oauth": "ВĐĩСа Đ´Đž OAuth-a", + "link_to_oauth": "ВĐĩСа Đ´Đž OAuth-а", "linked_oauth_account": "ПовĐĩСаĐŊи OAuth ĐŊаĐģĐžĐŗ", "list": "ИСĐģĐ¸ŅŅ‚Đ°Ņ˜", "loading": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ", "loading_search_results_failed": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ° ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", + "local_network": "Đ›ĐžŅ†Đ°Đģ ĐŊĐĩŅ‚wĐžŅ€Đē", + "local_network_sheet_info": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ҆ˁĐĩ ҁĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ ĐŋŅ€ĐĩĐēĐž ОвĐĩ URL Đ°Đ´Ņ€ĐĩҁĐĩ Đēада ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŊавĐĩĐ´ĐĩĐŊ҃ Ви-Фи ĐŧŅ€ĐĩĐļ҃", + "location_permission": "ДозвоĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "location_permission_content": "Да йи ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Đž Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Ņƒ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐžĐŗ ĐŋŅ€ĐĩĐąĐ°Ņ†Đ¸Đ˛Đ°ŅšĐ°, Immich-u ҘĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊа ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐŊа дОСвОĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ĐēаĐēĐž йи ĐŧĐžĐŗĐ°Đž да ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ° ĐŊаСив ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", + "location_picker_choose_on_map": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐŊа ĐŧаĐŋи", + "location_picker_latitude_error": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ваĐļĐĩŅ†ĖŅƒ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐē҃ ŅˆĐ¸Ņ€Đ¸ĐŊ҃", + "location_picker_latitude_hint": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐē҃ ŅˆĐ¸Ņ€Đ¸ĐŊ҃ ОвдĐĩ", + "location_picker_longitude_error": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ваĐļĐĩŅ†ĖŅƒ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐē҃ Đ´ŅƒĐļиĐŊ҃", + "location_picker_longitude_hint": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐē҃ Đ´ŅƒĐļиĐŊ҃ ОвдĐĩ", "log_out": "ĐžĐ´Ņ˜Đ°Đ˛Đ¸ ҁĐĩ", "log_out_all_devices": "ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ŅĐ° ŅĐ˛Đ¸Ņ… ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°", "logged_out_all_devices": "ĐžĐ´Ņ˜Đ°Đ˛Ņ™ĐĩĐŊи ҁ҃ ŅĐ˛Đ¸ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸", "logged_out_device": "ĐžĐ´Ņ˜Đ°Đ˛Ņ™ĐĩĐŊ ŅƒŅ€ĐĩŅ’Đ°Ņ˜", "login": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ°", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", - "login_form_err_http": "Please specify http:// or https://", - "login_form_err_invalid_email": "Invalid Email", - "login_form_err_invalid_url": "Invalid URL", - "login_form_err_leading_whitespace": "Leading whitespace", - "login_form_err_trailing_whitespace": "Trailing whitespace", - "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", - "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", - "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", - "login_form_password_hint": "password", - "login_form_save_login": "Stay logged in", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", - "login_has_been_disabled": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊа.", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", - "logout_all_device_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҁĐĩ ĐžĐ´â€ŒŅ˜Đ°Đ˛Đ¸Ņ‚Đĩ ŅĐ° ŅĐ˛Đ¸Ņ… ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°?", - "logout_this_device_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҁĐĩ ĐžĐ´â€ŒŅ˜Đ°Đ˛Đ¸Ņ‚Đĩ ŅĐ° ĐžĐ˛ĐžĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°?", + "login_disabled": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа", + "login_form_api_exception": "Đ˜ĐˇŅƒĐˇĐĩŅ‚Đ°Đē АПИ-Ņ˜Đ°. МоĐģиĐŧĐž Đ˛Đ°Ņ да ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ URL Đ°Đ´Ņ€Đĩҁ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° и ĐŋĐžĐēŅƒŅˆĐ°Ņ‚Đĩ ĐŋĐžĐŊОвО.", + "login_form_back_button_text": "Назад", + "login_form_email_hint": "Đ˛Đ°Ņˆemail@email.Ņ†ĐžĐŧ", + "login_form_endpoint_hint": "҅҂҂Đŋ://иĐŋ-Đ˛Đ°ŅˆĐĩĐŗ-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°:ĐŋĐžŅ€Ņ‚", + "login_form_endpoint_url": "URL ĐĄĐĩŅ€Đ˛ĐĩŅ€Đ°", + "login_form_err_http": "ДоĐŋĐ¸ŅˆĐ¸ ҅҂҂Đŋ:// иĐģи ҅҂҂Đŋҁ://", + "login_form_err_invalid_email": "НĐĩваĐļĐĩŅ›Đ¸ ЕĐŧаиĐģ", + "login_form_err_invalid_url": "НĐĩ ваĐļĐĩŅ›Đ¸ link (URL)", + "login_form_err_leading_whitespace": "РаСĐŧаĐē Đ¸ŅĐŋŅ€ĐĩĐ´", + "login_form_err_trailing_whitespace": "РаСĐŧаĐē иСа", + "login_form_failed_get_oauth_server_config": "ЕвидĐĩĐŊŅ†Đ¸Ņ˜Đ° ĐŗŅ€ĐĩŅˆĐ°Đēа ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ›Đ¸ OAuth, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đ¸ ҁĐĩŅ€Đ˛ĐĩҀҁĐēи link (URL)", + "login_form_failed_get_oauth_server_disable": "OAuth ĐžĐŋŅ†Đ¸Ņ˜Đ° ĐŊĐ¸Ņ˜Đĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊа ОвОĐŧ ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", + "login_form_failed_login": "НĐĩ҃ҁĐŋĐĩ҈ĐŊа ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸ URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, email и ŅˆĐ¸Ņ„Ņ€Ņƒ", + "login_form_handshake_exception": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž Đ¸ĐˇŅƒĐˇĐĩŅ‚Đēа Ņ€ŅƒĐēĐžŅŅ‚Đ¸ŅĐēĐ°ŅšĐ° ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ. ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ĐŋĐžĐ´Ņ€ŅˆĐē҃ Са ŅĐ°ĐŧĐžĐŋĐžŅ‚ĐŋĐ¸ŅĐ°ĐŊĐĩ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đĩ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа аĐēĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ŅĐ°ĐŧĐžĐŋĐžŅ‚ĐŋĐ¸ŅĐ°ĐŊи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚.", + "login_form_password_hint": "ŅˆĐ¸Ņ„Ņ€Đ°", + "login_form_save_login": "ĐžŅŅ‚Đ°ĐŊи ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™ĐĩĐŊ", + "login_form_server_empty": "ЕĐŊŅ‚ĐĩŅ€ а ҁĐĩŅ€Đ˛ĐĩŅ€ URL.", + "login_form_server_error": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋОвĐĩĐˇĐ°Ņ‚Đ¸ ҁĐĩ ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ.", + "login_has_been_disabled": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа.", + "login_password_changed_error": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž ĐŗŅ€Đĩ҈ĐēĐĩ ĐŋŅ€Đ¸ĐģиĐēĐžĐŧ аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšĐ° ĐģОСиĐŊĐēĐĩ", + "login_password_changed_success": "ЛозиĐŊĐēа ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа", + "logout_all_device_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҁĐĩ ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ŅĐ° ŅĐ˛Đ¸Ņ… ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°?", + "logout_this_device_confirmation": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҁĐĩ ĐžĐ´Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ŅĐ° ĐžĐ˛ĐžĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°?", "longitude": "ГĐĩĐžĐŗŅ€Đ°Ņ„ŅĐēа Đ´ŅƒĐļиĐŊа", "look": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜", "loop_videos": "ПоĐŊĐ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ", - "loop_videos_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ Са Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋĐžĐŊĐ°Đ˛Ņ™Đ°ŅšĐĩ видĐĩĐž СаĐŋĐ¸ŅĐ° ҃ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ĐŊиĐē҃ Đ´ĐĩŅ‚Đ°Ņ™Đ°.", - "main_branch_warning": "ĐŖĐŋĐžŅ‚Ņ€ĐĩĐąŅ™Đ°Đ˛Đ°Ņ‚Đĩ Ņ€Đ°ĐˇĐ˛ĐžŅ˜ĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ; ŅŅ‚Ņ€ĐžĐŗĐž ĐŋŅ€ĐĩĐŋĐžŅ€ŅƒŅ‡ŅƒŅ˜ĐĩĐŧĐž ҃ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒ Đ¸ĐˇĐ´Đ°Ņ‚Đĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ!", + "loop_videos_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ Са Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēĐž ĐŋĐžĐŊĐ°Đ˛Ņ™Đ°ŅšĐĩ видĐĩĐž СаĐŋĐ¸ŅĐ° ҃ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ĐŊиĐē҃ Đ´ĐĩŅ‚Đ°Ņ™Đ°.", + "main_branch_warning": "ĐŖĐŋĐžŅ‚Ņ€ĐĩĐąŅ™Đ°Đ˛Đ°Ņ‚Đĩ Ņ€Đ°ĐˇĐ˛ĐžŅ˜ĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ; ŅŅ‚Ņ€ĐžĐŗĐž ĐŋŅ€ĐĩĐŋĐžŅ€ŅƒŅ‡ŅƒŅ˜ĐĩĐŧĐž ҃ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒ иСdate вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ!", "main_menu": "ГĐģавĐŊи ĐŧĐĩĐŊи", "make": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜", "manage_shared_links": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đ´ĐĩŅ™ĐĩĐŊиĐŧ вĐĩСаĐŧа", "manage_sharing_with_partners": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đ´ĐĩŅ™ĐĩҚĐĩĐŧ ŅĐ° ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ¸Đŧа", "manage_the_app_settings": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", "manage_your_account": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đ˛Đ°ŅˆĐ¸Đŧ ĐŋŅ€ĐžŅ„Đ¸ĐģĐžĐŧ", - "manage_your_api_keys": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ АПИ ĐēŅ™ŅƒŅ‡ĐĩвиĐŧа (keys)", + "manage_your_api_keys": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ АПИ ĐēŅ™ŅƒŅ‡ĐĩвиĐŧа (ĐēĐĩyҁ)", "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đ¸Đŧ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™ĐĩĐŊиĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа", "manage_your_oauth_connection": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜ĐžĐŧ OAuth вĐĩСОĐŧ", "map": "МаĐŋа", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", + "map_assets_in_bound": "{count} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "map_cannot_get_user_location": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", + "map_location_dialog_yes": "Да", + "map_location_picker_page_use_location": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "map_location_service_disabled_content": "ĐŖŅĐģŅƒĐŗĐ° ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ ĐŧĐžŅ€Đ° ĐąĐ¸Ņ‚Đ¸ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа да йи ҁĐĩ ĐŋŅ€Đ¸ĐēаСиваĐģа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ŅĐ° Đ˛Đ°ŅˆĐĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ. Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҘĐĩ ŅĐ°Đ´Đ° ĐžĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ?", + "map_location_service_disabled_title": "ĐŖŅĐģŅƒĐŗĐ° ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа", "map_marker_for_images": "ОзĐŊĐ°Ņ‡Đ¸Đ˛Đ°Ņ‡ ĐŊа ĐŧаĐŋи Са ҁĐģиĐēĐĩ ҁĐŊиĐŧŅ™ĐĩĐŊĐĩ ҃ {city}, {country}", "map_marker_with_image": "ĐœĐ°Ņ€ĐēĐĩŅ€ ĐŊа ĐŧаĐŋи ŅĐ° ҁĐģиĐēĐžĐŧ", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", + "map_no_assets_in_bounds": "НĐĩĐŧа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° ҃ ĐžĐ˛ĐžŅ˜ ОйĐģĐ°ŅŅ‚Đ¸", + "map_no_location_permission_content": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐŊа ҘĐĩ дОСвОĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ да йи ҁĐĩ ĐŋŅ€Đ¸ĐēаСаĐģи Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ŅĐ° Đ˛Đ°ŅˆĐĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ. Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҘĐĩ ŅĐ°Đ´Đ° дОСвОĐģĐ¸Ņ‚Đĩ?", + "map_no_location_permission_title": "ДозвоĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ҘĐĩ ĐžĐ´ĐąĐ¸Ņ˜ĐĩĐŊа", "map_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŧаĐŋĐĩ", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", + "map_settings_dark_mode": "ĐĸаĐŧĐŊи Ņ€ĐĩĐļиĐŧ", + "map_settings_date_range_option_day": "ĐŸĐžŅĐģĐĩĐ´ŅšĐ° 24 ŅĐ°Ņ‚Đ°", + "map_settings_date_range_option_days": "ĐŸŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐ¸Ņ… {days} даĐŊа", + "map_settings_date_range_option_year": "ĐŸŅ€ĐžŅˆĐģа ĐŗĐžĐ´Đ¸ĐŊа", + "map_settings_date_range_option_years": "ĐŸŅ€ĐžŅ‚ĐĩĐēĐģĐ¸Ņ… {years} ĐŗĐžĐ´Đ¸ĐŊа", + "map_settings_dialog_title": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° МаĐŋĐĩ", + "map_settings_include_show_archived": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž", + "map_settings_include_show_partners": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đĩ", + "map_settings_only_show_favorites": "ĐŸŅ€Đ¸ĐēаĐļи ŅĐ°ĐŧĐž ĐžĐŧĐ¸Ņ™ĐĩĐŊĐĩ", + "map_settings_theme_settings": "ĐĸĐĩĐŧа ĐŧаĐŋĐĩ", + "map_zoom_to_see_photos": "ĐŖĐŧĐ°ŅšĐ¸Ņ‚Đĩ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", + "mark_all_as_read": "ОзĐŊĐ°Ņ‡Đ¸ ŅĐ˛Đĩ ĐēаО ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐž", + "mark_as_read": "ОзĐŊĐ°Ņ‡Đ¸ ĐēаО ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐž", + "marked_all_as_read": "ХвĐĩ ҘĐĩ ОСĐŊĐ°Ņ‡ĐĩĐŊĐž ĐēаО ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐž", "matches": "ĐŸĐžĐ´ŅƒĐ´Đ°Ņ€Đ°ŅšĐ°", "media_type": "Đ’Ņ€ŅŅ‚Đ° ĐŧĐĩĐ´Đ¸Ņ˜Đ°", - "memories": "ĐĄĐĩŅ›Đ°ŅšĐ°", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", - "memories_setting_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐžĐŊиĐŧ ŅˆŅ‚Đž Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ҃ ŅĐ˛ĐžŅ˜Đ¸Đŧ ҁĐĩŅ›Đ°ŅšĐ¸Đŧа", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", + "memories": "ĐĄĐĩŅ†ĖĐ°ŅšĐ°", + "memories_all_caught_up": "ХвĐĩ ҘĐĩ ŅƒŅ…Đ˛Đ°Ņ†ĖĐĩĐŊĐž", + "memories_check_back_tomorrow": "Đ’Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ ҁĐĩ ŅŅƒŅ‚Ņ€Đ° Са Ņ˜ĐžŅˆ ҃ҁĐŋĐžĐŧĐĩĐŊа", + "memories_setting_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐžĐŊиĐŧ ŅˆŅ‚Đž Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ҃ ŅĐ˛ĐžŅ˜Đ¸Đŧ ҁĐĩŅ†ĖĐ°ŅšĐ¸Đŧа", + "memories_start_over": "ĐŸĐžŅ‡ĐŊи Đ¸ŅĐŋĐžŅ‡ĐĩŅ‚Đēа", + "memories_swipe_to_close": "ĐŸŅ€ĐĩĐ˛ŅƒŅ†Đ¸Ņ‚Đĩ ĐŊĐ°ĐŗĐžŅ€Đĩ да ĐąĐ¸ŅŅ‚Đĩ ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Đģи", "memory": "МĐĩĐŧĐžŅ€Đ¸Ņ˜Đ°", - "memory_lane_title": "ĐĸŅ€Đ°Đēа ҁĐĩŅ›Đ°ŅšĐ° {title}", + "memory_lane_title": "ĐĸŅ€Đ°Đēа ҁĐĩŅ†ĖĐ°ŅšĐ° {title}", "menu": "МĐĩĐŊи", "merge": "ĐĄĐŋĐžŅ˜Đ¸", "merge_people": "ĐĄĐŋĐžŅ˜Đ¸ ĐžŅĐžĐąĐĩ", @@ -1218,71 +1230,76 @@ "missing": "НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ", "model": "МодĐĩĐģ", "month": "МĐĩҁĐĩ҆", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "ММММ y", "more": "Đ’Đ¸ŅˆĐĩ", - "moved_to_trash": "ĐŸŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž ҃ ҁĐŧĐĩŅ›Đĩ", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", - "mute_memories": "ĐŸŅ€Đ¸ĐŗŅƒŅˆĐ¸ ҁĐĩŅ›Đ°ŅšĐ°", + "moved_to_archive": "ĐŸŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ҃ Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ", + "moved_to_library": "ĐŸŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ҃ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", + "moved_to_trash": "ĐŸŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊĐž ҃ ҁĐŧĐĩ҆ˁĐĩ", + "multiselect_grid_edit_date_time_err_read_only": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да иСĐŧĐĩĐŊĐ¸Ņ‚Đĩ Đ´Đ°Ņ‚ŅƒĐŧ ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ŅĐ°ĐŧĐž Са Ņ‡Đ¸Ņ‚Đ°ŅšĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "multiselect_grid_edit_gps_err_read_only": "НĐĩ ĐŧĐžĐŗŅƒ да иСĐŧĐĩĐŊиĐŧ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ŅĐ°ĐŧĐž Са Ņ‡Đ¸Ņ‚Đ°ŅšĐĩ, ĐŋŅ€ĐĩҁĐēĐ°Ņ‡ĐĩĐŧ", + "mute_memories": "ĐŸŅ€Đ¸ĐŗŅƒŅˆĐ¸ ҁĐĩŅ†ĖĐ°ŅšĐ°", "my_albums": "ĐœĐžŅ˜Đ¸ аĐģĐąŅƒĐŧи", "name": "ИĐŧĐĩ", "name_or_nickname": "ИĐŧĐĩ иĐģи ĐŊадиĐŧаĐē", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "ĐŖĐŧŅ€ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ", + "networking_subtitle": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа ĐēŅ€Đ°Ņ˜ŅšĐĩ Ņ‚Đ°Ņ‡ĐēĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "never": "НиĐēада", - "new_album": "Нови аĐģĐąŅƒĐŧ", - "new_api_key": "Нови АПИ ĐēŅ™ŅƒŅ‡ (key)", + "new_album": "Нови АĐģĐąŅƒĐŧ", + "new_api_key": "Нови АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", "new_password": "Нова ŅˆĐ¸Ņ„Ņ€Đ°", "new_person": "Нова ĐžŅĐžĐąĐ°", + "new_pin_code": "Нови ПИН ĐēОд", "new_user_created": "Нови ĐēĐžŅ€Đ¸ŅĐŊиĐē ҘĐĩ ĐēŅ€ĐĩĐ¸Ņ€Đ°ĐŊ", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ НОВА ВЕРЗИЈА", "newest_first": "ĐĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Đĩ ĐŋŅ€Đ˛Đž", "next": "ĐĄĐģĐĩĐ´ĐĩŅ›Đĩ", - "next_memory": "ĐĄĐģĐĩĐ´ĐĩŅ›Đĩ ҁĐĩŅ›Đ°ŅšĐĩ", + "next_memory": "ĐĄĐģĐĩĐ´Đĩ҆ˁĐĩ ҁĐĩŅ†ĖĐ°ŅšĐĩ", "no": "НĐĩ", "no_albums_message": "НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ аĐģĐąŅƒĐŧ да ĐąĐ¸ŅŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊиСОваĐģи ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ", "no_albums_with_name_yet": "Đ˜ĐˇĐŗĐģĐĩда да Ņ˜ĐžŅˆ ŅƒĐ˛ĐĩĐē ĐŊĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩдаĐŊ аĐģĐąŅƒĐŧ ŅĐ° ОвиĐŧ иĐŧĐĩĐŊĐžĐŧ.", "no_albums_yet": "Đ˜ĐˇĐŗĐģĐĩда да Ņ˜ĐžŅˆ ĐŊĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩдаĐŊ аĐģĐąŅƒĐŧ.", "no_archived_assets_message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ да ĐąĐ¸ŅŅ‚Đĩ Đ¸Ņ… ŅĐ°ĐēŅ€Đ¸Đģи иС ĐŋŅ€Đ¸ĐēаСа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", "no_assets_message": "КЛИКНИĐĸЕ ДА ĐŖĐŸĐ›ĐžĐĐ”Đ˜Đ ĐĐĸЕ ĐĄĐ’ĐžĐˆĐŖ ĐŸĐ Đ’ĐŖ ФОĐĸĐžĐ“Đ ĐĐ¤Đ˜ĐˆĐŖ", - "no_assets_to_show": "No assets to show", + "no_assets_to_show": "НĐĩĐŧа ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° Са ĐŋŅ€Đ¸ĐēаС", "no_duplicates_found": "ĐĐ¸Ņ˜Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊ ĐŊĐ¸Ņ˜ĐĩдаĐŊ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚.", - "no_exif_info_available": "НĐĩĐŧа Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐ¸Ņ… exif иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ°", + "no_exif_info_available": "НĐĩĐŧа Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐ¸Ņ… ĐĩxĐ¸Ņ„ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ°", "no_explore_results_message": "ĐŖĐŋĐģĐžĐ°Đ´ŅƒŅ˜Ņ‚Đĩ Ņ˜ĐžŅˆ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° да ĐąĐ¸ŅŅ‚Đĩ Đ¸ŅŅ‚Ņ€Đ°ĐļиĐģи ŅĐ˛ĐžŅ˜Ņƒ ĐēĐžĐģĐĩĐēŅ†Đ¸Ņ˜Ņƒ.", "no_favorites_message": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ да ĐąĐ¸ŅŅ‚Đĩ ĐąŅ€ĐˇĐž ĐŊĐ°ŅˆĐģи Đ˛Đ°ŅˆĐĩ ĐŊĐ°Ņ˜ĐąĐžŅ™Đĩ ҁĐģиĐēĐĩ и видĐĩĐž ҁĐŊиĐŧĐēĐĩ", "no_libraries_message": "НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐŋĐžŅ™ĐŊ҃ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ", "no_name": "НĐĩĐŧа иĐŧĐĩĐŊа", + "no_notifications": "НĐĩĐŧа ОйавĐĩŅˆŅ‚ĐĩŅšĐ°", + "no_people_found": "ĐĐ¸ŅŅƒ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊи ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ†ĖĐ¸ Ņ™ŅƒĐ´Đ¸", "no_places": "НĐĩĐŧа ĐŧĐĩŅŅ‚Đ°", "no_results": "НĐĩĐŧа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°", "no_results_description": "ПоĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ŅĐ° ŅĐ¸ĐŊĐžĐŊиĐŧĐžĐŧ иĐģи ĐžĐŋŅˆŅ‚Đ¸Ņ˜ĐžĐŧ ĐēŅ™ŅƒŅ‡ĐŊĐžĐŧ Ņ€ĐĩŅ‡Đ¸", "no_shared_albums_message": "НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ аĐģĐąŅƒĐŧ да ĐąĐ¸ŅŅ‚Đĩ Đ´ĐĩĐģиĐģи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ŅĐ° Ņ™ŅƒĐ´Đ¸Đŧа ҃ Đ˛Đ°ŅˆĐžŅ˜ ĐŧŅ€ĐĩĐļи", "not_in_any_album": "НĐĩĐŧа ĐŊи ҃ ҘĐĩĐ´ĐŊĐžĐŧ аĐģĐąŅƒĐŧ҃", - "not_selected": "Not selected", - "note_apply_storage_label_to_previously_uploaded assets": "НаĐŋĐžĐŧĐĩĐŊа: Да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊиĐģи ОСĐŊаĐē҃ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°, ĐŋĐžĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ", + "not_selected": "ĐĐ¸Ņ˜Đĩ Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊĐž", + "note_apply_storage_label_to_previously_uploaded assets": "НаĐŋĐžĐŧĐĩĐŊа: Да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€Đ¸ĐŧĐĩĐŊиĐģи ОСĐŊаĐē҃ Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ҃ĐŋĐģĐžĐ°Đ´Đ¸Ņ€Đ°ĐŊĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ, ĐŋĐžĐēŅ€ĐĩĐŊĐ¸Ņ‚Đĩ", "notes": "НаĐŋĐžĐŧĐĩĐŊĐĩ", - "notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.", - "notification_permission_list_tile_content": "Grant permission to enable notifications.", - "notification_permission_list_tile_enable_button": "Enable Notifications", - "notification_permission_list_tile_title": "Notification Permission", - "notification_toggle_setting_description": "ОĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", + "notification_permission_dialog_content": "Да йи ҃ĐēŅ™ŅƒŅ†Đ¸Đģи ĐŊĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ, Đ¸Đ´Đ¸Ņ‚Đĩ ҃ ОĐŋŅ†Đ¸Ņ˜Đĩ и ОдайĐĩŅ€Đ¸Ņ‚Đĩ ДозвоĐģи.", + "notification_permission_list_tile_content": "Đ”Đ°Ņ˜Ņ‚Đĩ дОСвОĐģ҃ Са ĐžĐŧĐžĐŗŅƒŅ†ĖĐ°Đ˛Đ°ŅšĐĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ°.", + "notification_permission_list_tile_enable_button": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ĐĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "notification_permission_list_tile_title": "ДозвоĐģĐĩ Са ĐŊĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "notification_toggle_setting_description": "ОĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ° ĐŋŅƒŅ‚ĐĩĐŧ Đĩ-ĐŋĐžŅˆŅ‚Đĩ", "notifications": "ĐĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", "notifications_setting_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ОйавĐĩŅˆŅ‚ĐĩŅšĐ¸Đŧа", - "oauth": "OAuth", - "official_immich_resources": "ЗваĐŊĐ¸Ņ‡ĐŊи ИĐŧĐ¸Ņ‡ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", - "offline": "ĐžĐ´ŅŅƒŅ‚Đ°ĐŊ (Offline)", - "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ (Offline) ĐŋŅƒŅ‚Đ°ŅšĐĩ", + "official_immich_resources": "ЗваĐŊĐ¸Ņ‡ĐŊи Immich Ņ€ĐĩŅŅƒŅ€ŅĐ¸", + "offline": "ĐžĐ´ŅŅƒŅ‚Đ°ĐŊ (ĐžŅ„Ņ„ĐģиĐŊĐĩ)", + "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ (ĐžŅ„Ņ„ĐģиĐŊĐĩ) ĐŋŅƒŅ‚Đ°ŅšĐĩ", "offline_paths_description": "Ови Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ĐŋĐžŅĐģĐĩĐ´Đ¸Ņ†Đ° Ņ€ŅƒŅ‡ĐŊĐžĐŗ ĐąŅ€Đ¸ŅĐ°ŅšĐ° Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ Đ´ĐĩĐž ҁĐŋĐžŅ™ĐŊĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", "ok": "ОĐē", "oldest_first": "ĐĐ°Ņ˜ŅŅ‚Đ°Ņ€Đ¸Ņ˜Đĩ ĐŋŅ€Đ˛Đž", - "on_this_device": "On this device", + "on_this_device": "На ОвОĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ", "onboarding": "ĐŸŅ€Đ¸ŅŅ‚ŅƒĐŋĐ°ŅšĐĩ (ОĐŊĐąĐžĐ°Ņ€Đ´Đ¸ĐŊĐŗ)", - "onboarding_privacy_description": "ĐĄĐģĐĩĐ´ĐĩŅ›Đĩ (ĐžĐŋŅ†Đ¸ĐžĐŊĐĩ) Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ°Ņ˜Ņƒ ĐŊа ҁĐŋĐžŅ™ĐŊĐĩ ҃ҁĐģŅƒĐŗĐĩ и ĐŧĐžĐŗŅƒ ҁĐĩ ĐžĐŊĐĩĐŧĐžĐŗŅƒŅ›Đ¸Ņ‚Đ¸ ҃ йиĐģĐž ĐēĐžĐŧ ҂ҀĐĩĐŊŅƒŅ‚Đē҃ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đĩ.", + "onboarding_privacy_description": "ĐĄĐģĐĩĐ´Đĩ҆ˁĐĩ (ĐžĐŋŅ†Đ¸one) Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ҁĐĩ ĐžŅĐģĐ°ŅšĐ°Ņ˜Ņƒ ĐŊа ҁĐŋĐžŅ™ĐŊĐĩ ҃ҁĐģŅƒĐŗĐĩ и ĐŧĐžĐŗŅƒ ҁĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐ¸Ņ‚Đ¸ ҃ йиĐģĐž ĐēĐžĐŧ ҂ҀĐĩĐŊŅƒŅ‚Đē҃ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ˜Đĩ.", "onboarding_theme_description": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚ĐĩĐŧ҃ ĐąĐžŅ˜Đ° Са ŅĐ˛ĐžŅ˜ ĐŊаĐģĐžĐŗ. Ово ĐŧĐžĐļĐĩŅ‚Đĩ ĐēĐ°ŅĐŊĐ¸Ņ˜Đĩ да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ҃ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", "onboarding_welcome_description": "ĐĨĐ°Ņ˜Đ´Đĩ да ĐŋОдĐĩŅĐ¸ĐŧĐž Đ˛Đ°ŅˆŅƒ иĐŊŅŅ‚Đ°ĐŊŅ†Ņƒ ŅĐ° ĐŊĐĩĐēиĐŧ ŅƒĐžĐąĐ¸Ņ‡Đ°Ņ˜ĐĩĐŊиĐŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", "onboarding_welcome_user": "Đ”ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģи, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋаĐŊ (ОĐŊĐģиĐŊĐĩ)", "only_favorites": "ХаĐŧĐž Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đ¸", - "open_in_map_view": "ĐžŅ‚Đ˛ĐžŅ€Đ¸ ҃ ĐŋŅ€Đ¸ĐēĐ°ĐˇŅƒ ĐŧаĐŋĐĩ", + "open": "ĐžŅ‚Đ˛ĐžŅ€Đ¸", + "open_in_map_view": "ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ҃ ĐŋŅ€Đ¸ĐēаС ĐēĐ°Ņ€Ņ‚Đĩ", "open_in_openstreetmap": "ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ҃ ОĐŋĐĩĐŊĐĄŅ‚Ņ€ĐĩĐĩŅ‚ĐœĐ°Đŋ-҃", "open_the_search_filters": "ĐžŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ Ņ„Đ¸ĐģŅ‚ĐĩŅ€Đĩ Са ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", "options": "ОĐŋŅ†Đ¸Ņ˜Đĩ", @@ -1298,14 +1315,14 @@ "partner_can_access": "{partner} ĐŧĐžĐļĐĩ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи", "partner_can_access_assets": "ХвĐĩ Đ˛Đ°ŅˆĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž ҁĐŊиĐŧŅ†Đ¸ ĐžŅĐ¸Đŧ ĐžĐŊĐ¸Ņ… ҃ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊиĐŧ и Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊиĐŧ", "partner_can_access_location": "ЛоĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊа ĐēĐžŅ˜ĐžŅ˜ ҁ҃ Đ˛Đ°ŅˆĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ҁĐŊиĐŧŅ™ĐĩĐŊĐĩ", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", + "partner_list_user_photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа {user}", + "partner_list_view_all": "ĐŸŅ€Đ¸ĐēаĐļи ŅĐ˛Đĩ", + "partner_page_empty_message": "Đ’Đ°ŅˆĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ Ņ˜ĐžŅˆ ŅƒĐ˛ĐĩĐē ĐŊĐ¸ŅŅƒ Đ´ĐĩŅ™ĐĩĐŊĐĩ ĐŊи ŅĐ° ҘĐĩĐ´ĐŊиĐŧ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€ĐžĐŧ.", + "partner_page_no_more_users": "НĐĩĐŧа Đ˛Đ¸ŅˆĐĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēа Са Đ´ĐžĐ´Đ°Đ˛Đ°ŅšĐĩ", + "partner_page_partner_add_failed": "Đ”ĐžĐ´Đ°Đ˛Đ°ŅšĐĩ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ° ĐŊĐ¸Ņ˜Đĩ ҃ҁĐŋĐĩĐģĐž", + "partner_page_select_partner": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", + "partner_page_shared_to_title": "ДĐĩŅ™ĐĩĐŊĐž ŅĐ°", + "partner_page_stop_sharing_content": "{partner} Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ҆ˁĐĩ ĐŧĐžŅ†ĖĐ¸ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°Đŧа.", "partner_sharing": "ĐŸĐ°Ņ€Ņ‚ĐŊĐĩҀҁĐēĐž Đ´ĐĩŅ™ĐĩҚĐĩ", "partners": "ĐŸĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ¸", "password": "Đ¨Đ¸Ņ„Ņ€Đ°", @@ -1320,7 +1337,7 @@ "path": "ĐŸŅƒŅ‚Đ°ŅšĐ°", "pattern": "ШайĐģĐžĐŊ", "pause": "ĐŸĐ°ŅƒĐˇĐ°", - "pause_memories": "ĐŸĐ°ŅƒĐˇĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ҁĐĩŅ›Đ°ŅšĐ°", + "pause_memories": "ĐŸĐ°ŅƒĐˇĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ҁĐĩŅ†ĖĐ°ŅšĐ°", "paused": "ĐŸĐ°ŅƒĐˇĐ¸Ņ€Đ°ĐŊĐž", "pending": "На ҇ĐĩĐēĐ°ŅšŅƒ", "people": "ĐžŅĐžĐąĐĩ", @@ -1331,61 +1348,65 @@ "permanent_deletion_warning_setting_description": "ĐŸŅ€Đ¸ĐēаĐļи ҃ĐŋĐžĐˇĐžŅ€ĐĩҚĐĩ Đēада Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", "permanently_delete": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸", "permanently_delete_assets_count": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅˆĐ¸ {count, plural, one {Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} other {Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", - "permanently_delete_assets_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ {count, plural, one {ĐžĐ˛Ņƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃?} other {ОвĐĩ # Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ?}}Ово Ņ›Đĩ Đ¸Ņ… Ņ‚Đ°ĐēĐžŅ’Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ {count, plural, one {иС ŅšĐ¸Ņ…ĐžĐ˛ĐžĐŗ} other {иС ŅšĐ¸Ņ…ĐžĐ˛Đ¸Ņ…}} аĐģĐąŅƒĐŧа.", + "permanently_delete_assets_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ {count, plural, one {ĐžĐ˛Ņƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃?} other {ОвĐĩ # Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ?}}Ово ҆ˁĐĩ Đ¸Ņ… Ņ‚Đ°ĐēĐžŅ’Đĩ ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đ¸ {count, plural, one {иС ŅšĐ¸Ņ…ĐžĐ˛ĐžĐŗ} other {иС ŅšĐ¸Ņ…ĐžĐ˛Đ¸Ņ…}} аĐģĐąŅƒĐŧа.", "permanently_deleted_asset": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "permanently_deleted_assets_count": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", + "permission_onboarding_back": "Назад", + "permission_onboarding_continue_anyway": "ИĐŋаĐē ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸", + "permission_onboarding_get_started": "ЗаĐŋĐžŅ‡ĐŊĐ¸Ņ‚Đĩ", + "permission_onboarding_go_to_settings": "Иди ĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", + "permission_onboarding_permission_denied": "ДозвоĐģа ĐžĐ´ĐąĐ¸Ņ˜ĐĩĐŊа. Да ĐąĐ¸ŅŅ‚Đĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Đģи Immich, дОдĐĩĐģĐ¸Ņ‚Đĩ дОСвОĐģĐĩ Са Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ҃ ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", + "permission_onboarding_permission_granted": "ДозвоĐģа ĐžĐ´ĐžĐąŅ€ĐĩĐŊа! ĐĄĐŋŅ€ĐĩĐŧĐŊи ҁ҂Đĩ.", + "permission_onboarding_permission_limited": "ДозвоĐģа ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊа. Да ĐąĐ¸ŅŅ‚Đĩ ĐžĐŧĐžĐŗŅƒŅ†ĖĐ¸Đģи Immich-u да ĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋĐ¸Ņ˜Đĩ и ҃ĐŋŅ€Đ°Đ˛Ņ™Đ° ҆ĐĩĐģĐžĐŧ Đ˛Đ°ŅˆĐžĐŧ ĐēĐžĐģĐĩĐēŅ†Đ¸Ņ˜ĐžĐŧ ĐŗĐ°ĐģĐĩŅ€Đ¸Ņ˜Đĩ, дОдĐĩĐģĐ¸Ņ‚Đĩ дОСвОĐģĐĩ Са Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ҃ ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа.", + "permission_onboarding_request": "Immich ĐˇĐ°Ņ…Ņ‚Đĩва дОСвОĐģ҃ да види Đ˛Đ°ŅˆĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ.", "person": "ĐžŅĐžĐąĐ°", - "person_birthdate": "Đ ĐžŅ’ĐĩĐŊ(a) {date}", + "person_birthdate": "Đ ĐžŅ’ĐĩĐŊ(а) {date}", "person_hidden": "{name}{hidden, select, true { (ҁĐēŅ€Đ¸Đ˛ĐĩĐŊĐž)} other {}}", "photo_shared_all_users": "Đ˜ĐˇĐŗĐģĐĩда да ҁ҂Đĩ ĐŋОдĐĩĐģиĐģи ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ŅĐ° ŅĐ˛Đ¸Đŧ ĐēĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸Đŧа иĐģи да ĐŊĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩĐ´ĐŊĐžĐŗ ĐēĐžŅ€Đ¸ŅĐŊиĐēа ŅĐ° ĐēĐžŅ˜Đ¸Đŧ ĐąĐ¸ŅŅ‚Đĩ Đ´ĐĩĐģиĐģи.", - "photos": "ĐĄĐģиĐēĐĩ", + "photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", "photos_and_videos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ & ВидĐĩĐž СаĐŋĐ¸ŅĐ¸", "photos_count": "{count, plural, one {{count, number} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°} few {{count, number} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ} other {{count, number} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°}}", "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ иС ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐ¸Ņ… ĐŗĐžĐ´Đ¸ĐŊа", "pick_a_location": "ОдабĐĩŅ€Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "pin_code_changed_successfully": "ПИН ĐēОд ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊ", + "pin_code_reset_successfully": "ПИН ĐēОд ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž Ņ€ĐĩҁĐĩŅ‚ĐžĐ˛Đ°ĐŊ", + "pin_code_setup_successfully": "ĐŖŅĐŋĐĩ҈ĐŊĐž ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ПИН ĐēОда", "place": "МĐĩŅŅ‚Đž", "places": "МĐĩŅŅ‚Đ°", - "places_count": "{count, plural, one {{count, number} МĐĩŅŅ‚Đž} other {{count, number} МĐĩҁ҂a}}", + "places_count": "{count, plural, one {{count, number} МĐĩŅŅ‚Đž} other {{count, number} МĐĩŅŅ‚Đ°}}", "play": "ПоĐēŅ€ĐĩĐŊи", - "play_memories": "ПоĐēŅ€ĐĩĐŊи ҁĐĩŅ›Đ°ŅšĐ°", + "play_memories": "ПоĐēŅ€ĐĩĐŊи ҁĐĩŅ†ĖĐ°ŅšĐ°", "play_motion_photo": "ПоĐēŅ€ĐĩĐŊи ĐŋĐžĐēŅ€ĐĩŅ‚ĐŊ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ", "play_or_pause_video": "ПоĐēŅ€ĐĩĐŊи иĐģи ĐŋĐ°ŅƒĐˇĐ¸Ņ€Đ°Ņ˜ видĐĩĐž СаĐŋĐ¸Ņ", "port": "ĐŋĐžŅ€Ņ‚", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", + "preferences_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ¸Đŧа аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "preferences_settings_title": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "preset": "ĐŖĐŊаĐŋŅ€ĐĩĐ´ ĐŋОдĐĩ҈ĐĩĐŊĐž", "preview": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "previous": "ĐŸŅ€ĐžŅˆĐģĐž", - "previous_memory": "Prethodno ҁĐĩŅ›Đ°ŅšĐĩ", - "previous_or_next_photo": "Prethodna иĐģи ҁĐģĐĩĐ´ĐĩŅ›Đ° Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", - "primary": "ĐŸŅ€Đ¸ĐŧĐ°Ņ€ĐŊа (Primary)", + "previous_memory": "ĐŸŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊĐž ҁĐĩŅ†ĖĐ°ŅšĐĩ", + "previous_or_next_photo": "ĐŸŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊа иĐģи ҁĐģĐĩĐ´ĐĩŅ†ĖĐ° Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", + "primary": "ĐŸŅ€Đ¸ĐŧĐ°Ņ€ĐŊа (ĐŸŅ€Đ¸ĐŧĐ°Ņ€y)", "privacy": "ĐŸŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚", - "profile_drawer_app_logs": "Logs", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", - "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", + "profile": "ĐŸŅ€ĐžŅ„Đ¸Đģ", + "profile_drawer_app_logs": "ЕвидĐĩĐŊŅ†Đ¸Ņ˜Đ°", + "profile_drawer_client_out_of_date_major": "МобиĐģĐŊа аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐģа. МоĐģиĐŧĐž Đ˛Đ°Ņ да ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Ņƒ ĐŗĐģавĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ.", + "profile_drawer_client_out_of_date_minor": "МобиĐģĐŊа аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐģа. МоĐģиĐŧĐž Đ˛Đ°Ņ да ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Ņƒ ҁĐŋĐžŅ€ĐĩĐ´ĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ.", + "profile_drawer_client_server_up_to_date": "КĐģĐ¸Ņ˜ĐĩĐŊŅ‚ и ҁĐĩŅ€Đ˛ĐĩŅ€ ҁ҃ ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Đĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ", + "profile_drawer_github": "Đ“Đ¸Ņ‚ĐĨŅƒĐą", + "profile_drawer_server_out_of_date_major": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ҘĐĩ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐž. МоĐģиĐŧĐž Đ˛Đ°Ņ да аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Ņƒ ĐŗĐģавĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ.", + "profile_drawer_server_out_of_date_minor": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ҘĐĩ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐž. МоĐģиĐŧĐž Đ˛Đ°Ņ да аĐļŅƒŅ€Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Ņƒ ҁĐŋĐžŅ€ĐĩĐ´ĐŊ҃ вĐĩŅ€ĐˇĐ¸Ņ˜Ņƒ.", "profile_image_of_user": "ĐĄĐģиĐēа ĐŋŅ€ĐžŅ„Đ¸Đģа Од ĐēĐžŅ€Đ¸ŅĐŊиĐēа {user}", "profile_picture_set": "ĐŸŅ€ĐžŅ„Đ¸ĐģĐŊа ҁĐģиĐēа ĐŋĐžŅŅ‚Đ°Đ˛Ņ™ĐĩĐŊа.", "public_album": "ЈавĐŊи аĐģĐąŅƒĐŧ", "public_share": "ЈавĐŊĐž Đ´ĐĩŅ™ĐĩҚĐĩ", "purchase_account_info": "ĐŸĐžĐ´Ņ€ĐļаваĐŧ ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€", - "purchase_activated_subtitle": "ĐĨваĐģа ваĐŧ ŅˆŅ‚Đž ĐŋĐžĐ´Ņ€ĐļĐ°Đ˛Đ°Ņ‚Đĩ ИĐŧĐŧĐ¸Ņ†Ņ… и ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊĐžĐŗ ĐēОда", - "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž {date, date}", + "purchase_activated_subtitle": "ĐĨваĐģа ваĐŧ ŅˆŅ‚Đž ĐŋĐžĐ´Ņ€ĐļĐ°Đ˛Đ°Ņ‚Đĩ Immich и ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊĐžĐŗ ĐēОда", + "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž {date}", "purchase_activated_title": "Đ’Đ°Ņˆ ĐēŅ™ŅƒŅ‡ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ", - "purchase_button_activate": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°j", + "purchase_button_activate": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜", "purchase_button_buy": "ĐšŅƒĐŋи", - "purchase_button_buy_immich": "ĐšŅƒĐŋи ИĐŧĐ¸Ņ‡", + "purchase_button_buy_immich": "ĐšŅƒĐŋĐ¸Ņ‚Đĩ Immich", "purchase_button_never_show_again": "НиĐēада Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐŋŅ€Đ¸ĐēĐ°ĐˇŅƒŅ˜", "purchase_button_reminder": "ĐŸĐžĐ´ŅĐĩŅ‚Đ¸ ĐŧĐĩ Са 30 даĐŊа", "purchase_button_remove_key": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ĐēŅ™ŅƒŅ‡", @@ -1395,20 +1416,20 @@ "purchase_individual_description_2": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŋĐžĐ´Ņ€ŅˆĐēĐĩ", "purchase_individual_title": "ИĐŊĐ´Đ¸Đ˛Đ¸Đ´ŅƒĐ°ĐģĐŊа ĐģĐ¸Ņ†ĐĩĐŊŅ†Đ°", "purchase_input_suggestion": "ИĐŧĐ°Ņ‚Đĩ ĐēŅ™ŅƒŅ‡ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ°? ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐēŅ™ŅƒŅ‡ Đ¸ŅĐŋОд", - "purchase_license_subtitle": "ĐšŅƒĐŋĐ¸Ņ‚Đĩ ИĐŧĐ¸Ņ‡ да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐ´Ņ€ĐļаĐģи ĐēĐžĐŊŅ‚Đ¸ĐŊŅƒĐ¸Ņ€Đ°ĐŊи Ņ€Đ°ĐˇĐ˛ĐžŅ˜ ҃ҁĐģŅƒĐŗĐĩ", + "purchase_license_subtitle": "ĐšŅƒĐŋĐ¸Ņ‚Đĩ Immich да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐ´Ņ€ĐļаĐģи ĐēĐžĐŊŅ‚Đ¸ĐŊŅƒĐ¸Ņ€Đ°ĐŊи Ņ€Đ°ĐˇĐ˛ĐžŅ˜ ҃ҁĐģŅƒĐŗĐĩ", "purchase_lifetime_description": "ДоĐļĐ¸Đ˛ĐžŅ‚ĐŊа ĐģĐ¸Ņ†ĐĩĐŊŅ†Đ°", "purchase_option_title": "ОПĐĻИЈЕ ĐšĐŖĐŸĐžĐ’Đ˜ĐĐ•", - "purchase_panel_info_1": "Đ˜ĐˇĐŗŅ€Đ°Đ´ŅšĐ° ИĐŧĐ¸Ņ‡-а ĐˇĐ°Ņ…Ņ‚Đĩва ĐŧĐŊĐžĐŗĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊа и Ņ‚Ņ€ŅƒĐ´Đ°, а иĐŧаĐŧĐž иĐŊĐļĐĩҚĐĩŅ€Đĩ ĐēĐžŅ˜Đ¸ Ņ€Đ°Đ´Đĩ ĐŊа Ņ‚ĐžĐŧĐĩ ŅĐ° Đŋ҃ĐŊиĐŧ Ņ€Đ°Đ´ĐŊиĐŧ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐžĐŧ ĐēаĐēĐž ĐąĐ¸ŅĐŧĐž ҘĐĩ ŅƒŅ‡Đ¸ĐŊиĐģи ŅˆŅ‚Đž ҘĐĩ ĐŧĐžĐŗŅƒŅ›Đĩ ĐąĐžŅ™ĐžĐŧ. ĐĐ°ŅˆĐ° ĐŧĐ¸ŅĐ¸Ņ˜Đ° ҘĐĩ да ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊĐžĐŗ ĐēОда и ĐĩŅ‚Đ¸Ņ‡ĐēĐĩ ĐŋĐžŅĐģОвĐŊĐĩ ĐŋŅ€Đ°ĐēҁĐĩ ĐŋĐžŅŅ‚Đ°ĐŊ҃ ĐžĐ´Ņ€Đļив Đ¸ĐˇĐ˛ĐžŅ€ ĐŋŅ€Đ¸Ņ…ĐžĐ´Đ° Са ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐĩŅ€Đĩ и да ŅŅ‚Đ˛ĐžŅ€Đ¸ĐŧĐž ĐĩĐēĐžŅĐ¸ŅŅ‚ĐĩĐŧ ĐēĐžŅ˜Đ¸ ĐŋĐžŅˆŅ‚ŅƒŅ˜Đĩ ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚ ŅĐ° ŅŅ‚Đ˛Đ°Ņ€ĐŊиĐŧ аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛Đ°Đŧа ĐĩĐēҁĐŋĐģĐžĐ°Ņ‚Đ°Ņ‚Đ¸Đ˛ĐŊиĐŧ ҃ҁĐģŅƒĐŗĐ°Đŧа ҃ ОйĐģаĐē҃.", - "purchase_panel_info_2": "ĐŸĐžŅˆŅ‚Đž ҁĐŧĐž ҁĐĩ ОйавĐĩСаĐģи да ĐŊĐĩŅ›ĐĩĐŧĐž Đ´ĐžĐ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋĐģĐ°Ņ‚ĐŊĐĩ СидОвĐĩ, Ова Đē҃ĐŋОвиĐŊа ваĐŧ ĐŊĐĩŅ›Đĩ Đ´Đ°Ņ‚Đ¸ ĐŊиĐēаĐēвĐĩ Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ҃ ИĐŧĐ¸Ņ‡-҃. ĐžŅĐģĐ°ŅšĐ°ĐŧĐž ҁĐĩ ĐŊа ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ĐŋĐžĐŋŅƒŅ‚ Đ˛Đ°Ņ да ĐŋĐžĐ´Ņ€ĐļĐĩ ИĐŧĐ¸Ņ‡-Ов ŅŅ‚Đ°ĐģĐŊи Ņ€Đ°ĐˇĐ˛ĐžŅ˜.", + "purchase_panel_info_1": "Đ˜ĐˇĐŗŅ€Đ°Đ´ŅšĐ° Immich-a ĐˇĐ°Ņ…Ņ‚Đĩва ĐŧĐŊĐžĐŗĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊа и Ņ‚Ņ€ŅƒĐ´Đ°, а иĐŧаĐŧĐž иĐŊĐļĐĩҚĐĩŅ€Đĩ ĐēĐžŅ˜Đ¸ Ņ€Đ°Đ´Đĩ ĐŊа Ņ‚ĐžĐŧĐĩ ŅĐ° Đŋ҃ĐŊиĐŧ Ņ€Đ°Đ´ĐŊиĐŧ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐžĐŧ ĐēаĐēĐž ĐąĐ¸ŅĐŧĐž ҘĐĩ ŅƒŅ‡Đ¸ĐŊиĐģи ŅˆŅ‚Đž ҘĐĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐąĐžŅ™ĐžĐŧ. ĐĐ°ŅˆĐ° ĐŧĐ¸ŅĐ¸Ņ˜Đ° ҘĐĩ да ŅĐžŅ„Ņ‚Đ˛ĐĩŅ€ ĐžŅ‚Đ˛ĐžŅ€ĐĩĐŊĐžĐŗ ĐēОда и ĐĩŅ‚Đ¸Ņ‡ĐēĐĩ ĐŋĐžŅĐģОвĐŊĐĩ ĐŋŅ€Đ°ĐēҁĐĩ ĐŋĐžŅŅ‚Đ°ĐŊ҃ ĐžĐ´Ņ€Đļив Đ¸ĐˇĐ˛ĐžŅ€ ĐŋŅ€Đ¸Ņ…ĐžĐ´Đ° Са ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐĩŅ€Đĩ и да ŅŅ‚Đ˛ĐžŅ€Đ¸ĐŧĐž ĐĩĐēĐžŅĐ¸ŅŅ‚ĐĩĐŧ ĐēĐžŅ˜Đ¸ ĐŋĐžŅˆŅ‚ŅƒŅ˜Đĩ ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚ ŅĐ° ŅŅ‚Đ˛Đ°Ņ€ĐŊиĐŧ аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛Đ°Đŧа ĐĩĐēҁĐŋĐģĐžĐ°Ņ‚Đ°Ņ‚Đ¸Đ˛ĐŊиĐŧ ҃ҁĐģŅƒĐŗĐ°Đŧа ҃ ОйĐģаĐē҃.", + "purchase_panel_info_2": "ĐŸĐžŅˆŅ‚Đž ҁĐŧĐž ҁĐĩ ОйавĐĩСаĐģи да ĐŊĐĩ҆ˁĐĩĐŧĐž Đ´ĐžĐ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋĐģĐ°Ņ‚ĐŊĐĩ СидОвĐĩ, Ова Đē҃ĐŋОвиĐŊа ваĐŧ ĐŊĐĩ҆ˁĐĩ Đ´Đ°Ņ‚Đ¸ ĐŊиĐēаĐēвĐĩ Đ´ĐžĐ´Đ°Ņ‚ĐŊĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đĩ ҃ Immich-u. ĐžŅĐģĐ°ŅšĐ°ĐŧĐž ҁĐĩ ĐŊа ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ ĐŋĐžĐŋŅƒŅ‚ Đ˛Đ°Ņ да ĐŋĐžĐ´Ņ€ĐļĐĩ Immich-Ов ŅŅ‚Đ°ĐģĐŊи Ņ€Đ°ĐˇĐ˛ĐžŅ˜.", "purchase_panel_title": "ĐŸĐžĐ´Ņ€ĐļĐ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ˜ĐĩĐēĐ°Ņ‚", "purchase_per_server": "По ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", "purchase_per_user": "По ĐēĐžŅ€Đ¸ŅĐŊиĐē҃", "purchase_remove_product_key": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ĐēŅ™ŅƒŅ‡ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ°", "purchase_remove_product_key_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ°?", - "purchase_remove_server_product_key": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "purchase_remove_server_product_key_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°?", + "purchase_remove_server_product_key": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "purchase_remove_server_product_key_prompt": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҃ĐēĐģĐžĐŊĐ¸Ņ‚Đĩ ŅˆĐ¸Ņ„Ņ€Ņƒ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°?", "purchase_server_description_1": "За ҆ĐĩĐž ҁĐĩŅ€Đ˛ĐĩŅ€", - "purchase_server_description_2": "ЗĐŊĐ°Ņ‡Đēа ĐŋĐžĐ´Ņ€ŅˆĐēĐĩ", + "purchase_server_description_2": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŋĐžĐ´Ņ€ŅˆĐēĐĩ", "purchase_server_title": "ĐĄĐĩŅ€Đ˛ĐĩŅ€", "purchase_settings_server_activated": "ĐšŅ™ŅƒŅ‡ĐĩĐŧ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҃ĐŋŅ€Đ°Đ˛Ņ™Đ° адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", "rating": "ĐžŅ†ĐĩĐŊа СвĐĩĐˇĐ´Đ¸Ņ†Đ°", @@ -1418,23 +1439,25 @@ "reaction_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ Ņ€ĐĩаĐēŅ†Đ¸Ņ˜Đĩ", "read_changelog": "ĐŸŅ€ĐžŅ‡Đ¸Ņ‚Đ°Ņ˜Ņ‚Đĩ Đ´ĐŊĐĩвĐŊиĐē ĐŋŅ€ĐžĐŧĐĩĐŊа", "reassign": "ПоĐŊОвО Đ´ĐžĐ´Đ°Ņ˜", - "reassigned_assets_to_existing_person": "ПоĐŊОвО дОдĐĩŅ™ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›ĐžŅ˜ {name, select, null {ĐžŅĐžĐąĐ¸} other {{name}}}", + "reassigned_assets_to_existing_person": "ПоĐŊОвО дОдĐĩŅ™ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ†ĖĐžŅ˜ {name, select, null {ĐžŅĐžĐąĐ¸} other {{name}}}", "reassigned_assets_to_new_person": "ПоĐŊОвО дОдĐĩŅ™ĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}} ĐŊĐžĐ˛ĐžŅ˜ ĐžŅĐžĐąĐ¸", - "reassing_hint": "ДодĐĩĐģĐ¸Ņ‚Đĩ Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›ĐžŅ˜ ĐžŅĐžĐąĐ¸", + "reassing_hint": "ДодĐĩĐģĐ¸Ņ‚Đĩ Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ†ĖĐžŅ˜ ĐžŅĐžĐąĐ¸", "recent": "ĐĄĐēĐžŅ€Đ°ŅˆŅšĐ¸", "recent-albums": "НĐĩдавĐŊи аĐģĐąŅƒĐŧи", "recent_searches": "ĐĄĐēĐžŅ€Đ°ŅˆŅšĐĩ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", + "recently_added": "НĐĩдавĐŊĐž Đ´ĐžĐ´Đ°Ņ‚Đž", + "recently_added_page_title": "НĐĩдавĐŊĐž Đ”ĐžĐ´Đ°Ņ‚Đž", + "recently_taken": "НĐĩдавĐŊĐž ҁĐŊиĐŧŅ™ĐĩĐŊĐž", + "recently_taken_page_title": "НĐĩдавĐŊĐž ĐĄĐŊиĐŧŅ™ĐĩĐŊĐž", "refresh": "ĐžŅĐ˛ĐĩĐļи", - "refresh_encoded_videos": "ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ (ĐĩĐŊŅ†ĐžĐ´ĐĩĐ´) видĐĩĐž СаĐŋĐ¸ŅĐĩ", + "refresh_encoded_videos": "ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐĩ (ĐĩĐŊcodeĐ´) видĐĩĐž СаĐŋĐ¸ŅĐĩ", "refresh_faces": "ĐžŅĐ˛ĐĩĐļи ĐģĐ¸Ņ†Đ°", "refresh_metadata": "ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ", "refresh_thumbnails": "ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đĩ", "refreshed": "ĐžŅĐ˛ĐĩĐļĐĩĐŊĐž", - "refreshes_every_file": "ПоĐŊОвО Ņ‡Đ¸Ņ‚Đ° ŅĐ˛Đĩ ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›Đĩ и ĐŊОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "refreshing_encoded_video": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐžĐŗ (ĐĩĐŊŅ†ĐžĐ´ĐĩĐ´) видĐĩа", - "refreshing_faces": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°Ņše ĐģĐ¸Ņ†Đ°", + "refreshes_every_file": "ПоĐŊОвО Ņ‡Đ¸Ņ‚Đ° ŅĐ˛Đĩ ĐŋĐžŅŅ‚ĐžŅ˜Đĩ҆ˁĐĩ и ĐŊОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "refreshing_encoded_video": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ĐēĐžĐ´Đ¸Ņ€Đ°ĐŊĐžĐŗ (ĐĩĐŊcodeĐ´) видĐĩа", + "refreshing_faces": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ĐģĐ¸Ņ†Đ°", "refreshing_metadata": "ĐžŅĐ˛ĐĩĐļĐ°Đ˛Đ°ŅšĐĩ ĐŧĐĩŅ‚Đ°-ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", "regenerating_thumbnails": "ОбĐŊĐ°Đ˛Ņ™Đ°ŅšĐĩ ҁĐģĐ¸Ņ‡Đ¸Ņ†Đ°", "remove": "ĐŖĐēĐģĐžĐŊи", @@ -1450,31 +1473,32 @@ "remove_photo_from_memory": "ĐŖĐēĐģĐžĐŊĐ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ иС ОвĐĩ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ", "remove_url": "ĐŖĐēĐģĐžĐŊи URL", "remove_user": "ĐŖĐēĐģĐžĐŊи ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "removed_api_key": "ĐŖĐēĐģĐžŅšĐĩĐŊ АПИ ĐēŅ™ŅƒŅ‡ (key): {name}", + "removed_api_key": "ĐŖĐēĐģĐžŅšĐĩĐŊ АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy): {name}", "removed_from_archive": "ĐŖĐēĐģĐžŅšĐĩĐŊĐž иС Đ°Ņ€Ņ…Đ¸Đ˛Đĩ", "removed_from_favorites": "ĐŖĐēĐģĐžŅšĐĩĐŊĐž иС ĐžĐŧĐ¸Ņ™ĐĩĐŊĐ¸Ņ… (Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩҁ)", "removed_from_favorites_count": "{count, plural, other {ĐŖĐēĐģĐžŅšĐĩĐŊĐž #}} иС ĐžĐŧĐ¸Ņ™ĐĩĐŊĐ¸Ņ…", "removed_memory": "ĐŖĐēĐģĐžŅšĐĩĐŊа ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đ°", "removed_photo_from_memory": "ĐĄĐģиĐēа ҘĐĩ ҃ĐēĐģĐžŅšĐĩĐŊа иС ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Đĩ", - "removed_tagged_assets": "ĐŖĐēĐģĐžŅšĐĩĐŊа ОСĐŊаĐēа (tag) иС {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", + "removed_tagged_assets": "ĐŖĐēĐģĐžŅšĐĩĐŊа ОСĐŊаĐēа иС {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", "rename": "ĐŸŅ€ĐĩиĐŧĐĩĐŊ҃Ҙ", "repair": "ПоĐŋŅ€Đ°Đ˛Đ¸", - "repair_no_results_message": "ОвдĐĩ Ņ›Đĩ ҁĐĩ ĐŋĐžŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ ĐŋŅ€Đ°Ņ›ĐĩĐŊĐĩ и ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒ", + "repair_no_results_message": "ОвдĐĩ ҆ˁĐĩ ҁĐĩ ĐŋĐžŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ĐēĐžŅ˜Đĩ ĐŊĐ¸ŅŅƒ ĐŋŅ€Đ°Ņ†ĖĐĩĐŊĐĩ и ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Ņƒ", "replace_with_upload": "ЗаĐŧĐĩĐŊĐ¸Ņ‚Đĩ ŅĐ° ҃ĐŋĐģОад-ĐžĐŧ", - "repository": "Đ ĐĩĐŋĐžĐˇĐ¸Ņ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧ (Repository)", + "repository": "Đ ĐĩĐŋĐžĐˇĐ¸Ņ‚ĐžŅ€Đ¸Ņ˜ŅƒĐŧ (Đ ĐĩĐŋĐžŅĐ¸Ņ‚ĐžŅ€y)", "require_password": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐŊа ĐģОСиĐŊĐēа", "require_user_to_change_password_on_first_login": "Đ—Đ°Ņ…Ņ‚ĐĩĐ˛Đ°Ņ‚Đ¸ Од ĐēĐžŅ€Đ¸ŅĐŊиĐēа да ĐŋŅ€ĐžĐŧĐĩĐŊи ĐģОСиĐŊĐē҃ ĐŋŅ€Đ¸ ĐŋŅ€Đ˛ĐžĐŧ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšŅƒ", "rescan": "ПоĐŊОвО ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜", "reset": "Đ ĐĩҁĐĩŅ‚ĐžĐ˛Đ°Ņ‚Đ¸", "reset_password": "Đ ĐĩҁĐĩŅ‚ĐžĐ˛Đ°Ņ‚Đ¸ ĐģОСиĐŊĐē҃", "reset_people_visibility": "Đ ĐĩҁĐĩŅ‚ŅƒŅ˜Ņ‚Đĩ Đ˛Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ ĐžŅĐžĐąĐ°", + "reset_pin_code": "Đ ĐĩҁĐĩŅ‚ŅƒŅ˜ ПИН ĐēОд", "reset_to_default": "Đ ĐĩҁĐĩŅ‚ŅƒŅ˜Ņ‚Đĩ ĐŊа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐĩ Đ˛Ņ€ĐĩĐ´ĐŊĐžŅŅ‚Đ¸", "resolve_duplicates": "Đ ĐĩŅˆĐ¸ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ", "resolved_all_duplicates": "Хви Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ¸ ҁ҃ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊи", "restore": "ĐŸĐžĐ˛Ņ€Đ°Ņ‚Đ¸", "restore_all": "ĐŸĐžĐ˛Ņ€Đ°Ņ‚Đ¸ ŅĐ˛Đĩ", "restore_user": "ĐŸĐžĐ˛Ņ€Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "restored_asset": "ĐŸĐžĐ˛Ņ€Đ°Ņ›ĐĩĐŊĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž", + "restored_asset": "ĐŸĐžĐ˛Ņ€Đ°Ņ†ĖĐĩĐŊĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž", "resume": "ПоĐŊОвО ĐŋĐžĐēŅ€ĐĩĐŊи", "retry_upload": "ПоĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ĐŋĐžĐŊОвО да ҃ĐŋĐģĐžĐ°Đ´ŅƒŅ˜ĐĩŅ‚Đĩ", "review_duplicates": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ", @@ -1482,12 +1506,12 @@ "role_editor": "ĐŖŅ€ĐĩĐ´ĐŊиĐē", "role_viewer": "ГĐģĐĩдаĐģĐ°Ņ†", "save": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜", - "save_to_gallery": "Save to gallery", - "saved_api_key": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ АПИ ĐēŅ™ŅƒŅ‡ (key)", + "save_to_gallery": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°Ņ˜ ҃ ĐŗĐ°ĐģĐĩŅ€Đ¸Ņ˜Ņƒ", + "saved_api_key": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ АПИ ĐēŅ™ŅƒŅ‡ (ĐēĐĩy)", "saved_profile": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ ĐŋŅ€ĐžŅ„Đ¸Đģ", "saved_settings": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°ĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "say_something": "Đ ĐĩŅ†Đ¸ ĐŊĐĩŅˆŅ‚Đž", - "scaffold_body_error_occurred": "Error occurred", + "scaffold_body_error_occurred": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž ĐŗŅ€Đĩ҈ĐēĐĩ", "scan_all_libraries": "ĐĄĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜ ŅĐ˛Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "scan_library": "ĐĄĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜", "scan_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐ°", @@ -1498,50 +1522,50 @@ "search_by_description": "ĐĸŅ€Đ°Đļи ĐŋĐž ĐžĐŋĐ¸ŅŅƒ", "search_by_description_example": "ДаĐŊ ĐŋĐĩŅˆĐ°Ņ‡ĐĩŅšĐ° ҃ ХаĐŋи", "search_by_filename": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ĐŋĐž иĐŧĐĩĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ иĐģи ĐĩĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đ¸", - "search_by_filename_example": "ĐŊĐŋŅ€. IMG_1234.JPG иĐģи PNG", + "search_by_filename_example": "ĐŊĐŋŅ€. ИМГ_1234.ЈПГ иĐģи ПНГ", "search_camera_make": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ° ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžŅ’Đ°Ņ‡Đ° ĐēаĐŧĐĩŅ€Đĩ...", "search_camera_model": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи ĐŧОдĐĩĐģ ĐēаĐŧĐĩŅ€Đĩ...", "search_city": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи ĐŗŅ€Đ°Đ´...", "search_country": "ĐĸŅ€Đ°Đļи СĐĩĐŧŅ™Ņƒ...", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", + "search_filter_apply": "ĐŸŅ€Đ¸ĐŧĐĩĐŊи Ņ„Đ¸ĐģŅ‚ĐĩŅ€", + "search_filter_camera_title": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚Đ¸Đŋ ĐēаĐŧĐĩŅ€Đĩ", + "search_filter_date": "Đ”Đ°Ņ‚Đĩ", + "search_filter_date_interval": "{start} Đ´Đž {end}", + "search_filter_date_title": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐŋĐĩŅ€Đ¸ĐžĐ´", + "search_filter_display_option_not_in_album": "ĐĐžŅ‚ иĐŊ аĐģĐąŅƒĐŧ", + "search_filter_display_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸ĐēаСа", + "search_filter_filename": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ° ĐŋĐž иĐŧĐĩĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "search_filter_location": "ЛоĐēĐ°Ņ†Đ¸Ņ˜Đ°", + "search_filter_location_title": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ", + "search_filter_media_type": "МĐĩдиа ĐĸyĐŋĐĩ", + "search_filter_media_type_title": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚Đ¸Đŋ ĐŧĐĩĐ´Đ¸Ņ˜Đ°", + "search_filter_people_title": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ Ņ™ŅƒĐ´Đĩ", "search_for": "ĐĸŅ€Đ°Đļи", - "search_for_existing_person": "ĐŸĐžŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ›Ņƒ ĐžŅĐžĐąŅƒ", - "search_no_more_result": "No more results", + "search_for_existing_person": "ĐŸĐžŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ĐŋĐžŅŅ‚ĐžŅ˜ĐĩŅ†ĖŅƒ ĐžŅĐžĐąŅƒ", + "search_no_more_result": "НĐĩĐŧа Đ˛Đ¸ŅˆĐĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ°", "search_no_people": "БĐĩС ĐžŅĐžĐąĐ°", "search_no_people_named": "НĐĩĐŧа ĐžŅĐžĐąĐ° ŅĐ° иĐŧĐĩĐŊĐžĐŧ „{name}“", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "ĐĐ¸ŅŅƒ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊи Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸, ĐŋĐžĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ŅĐ° Đ´Ņ€ŅƒĐŗĐ¸Đŧ Ņ‚ĐĩŅ€ĐŧиĐŊĐžĐŧ Са ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ иĐģи ĐēĐžĐŧйиĐŊĐ°Ņ†Đ¸Ņ˜ĐžĐŧ", "search_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", - "search_page_no_objects": "No Objects Info Available", - "search_page_no_places": "No Places Info Available", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", - "search_page_things": "Things", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", + "search_page_categories": "ĐšĐ°Ņ‚ĐĩĐŗĐžŅ€Đ¸Ņ˜Đĩ", + "search_page_motion_photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ҃ ĐŋĐžĐēŅ€ĐĩŅ‚Ņƒ", + "search_page_no_objects": "БĐĩС иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ°", + "search_page_no_places": "НĐĩĐŧа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đ° Đž ĐŧĐĩŅŅ‚Ņƒ", + "search_page_screenshots": "ĐĄĐŊиĐŧŅ†Đ¸ ĐĩĐēŅ€Đ°ĐŊа", + "search_page_search_photos_videos": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ", + "search_page_selfies": "ĐĄĐĩĐģŅ„Đ¸Ņ˜Đ¸", + "search_page_things": "ĐĄŅ‚Đ˛Đ°Ņ€Đ¸", + "search_page_view_all_button": "ĐŸŅ€Đ¸ĐēаĐļи ŅĐ˛Đĩ", + "search_page_your_activity": "Đ’Đ°ŅˆĐ° аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚", + "search_page_your_map": "Đ’Đ°ŅˆĐ° ĐŧаĐŋа", "search_people": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи ĐžŅĐžĐąĐĩ", "search_places": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи ĐŧĐĩŅŅ‚Đ°", "search_rating": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ° ĐŋĐž ĐžŅ†ĐĩĐŊи...", - "search_result_page_new_search_hint": "New Search", + "search_result_page_new_search_hint": "Нова ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ°", "search_settings": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ° ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", "search_state": "ĐĸŅ€Đ°Đļи Ņ€ĐĩĐŗĐ¸ĐžĐŊ...", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", + "search_suggestion_list_smart_search_hint_1": "ПаĐŧĐĩŅ‚ĐŊа ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐ° ҘĐĩ ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа, Са ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ ĐŧĐĩŅ‚Đ°ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ŅĐ¸ĐŊŅ‚Đ°Đēҁ҃ ", + "search_suggestion_list_smart_search_hint_2": "Đŧ:Đ˛Đ°Ņˆ-ĐŋĐžŅ˜Đ°Đŧ-Са-ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗŅƒ", "search_tags": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи ОСĐŊаĐēĐĩ (tags)...", "search_timezone": "ĐŸŅ€ĐĩŅ‚Ņ€Đ°Đļи Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐē҃ СОĐŊ҃...", "search_type": "Đ’Ņ€ŅŅ‚Đ° ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ", @@ -1560,18 +1584,19 @@ "select_keep_all": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ да ĐˇĐ°Đ´Ņ€ĐļĐ¸Ņ‚Đĩ ŅĐ˛Đĩ", "select_library_owner": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ вĐģĐ°ŅĐŊиĐēа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ", "select_new_face": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐŊОвО ĐģĐ¸Ņ†Đĩ", + "select_person_to_tag": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐžŅĐžĐąŅƒ Са ОСĐŊĐ°Ņ‡Đ°Đ˛Đ°ŅšĐĩ", "select_photos": "ОдабĐĩŅ€Đ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", "select_trash_all": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ да ŅĐ˛Đĩ ĐąĐ°Ņ†Đ¸Ņ‚Đĩ ĐŊа ĐžŅ‚Đŋад", - "select_user_for_sharing_page_err_album": "Failed to create album", + "select_user_for_sharing_page_err_album": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐēŅ€ĐĩĐ¸Ņ€Đ°ŅšĐĩ аĐģĐąŅƒĐŧа", "selected": "ĐžĐ´Đ°ĐąŅ€Đ°ĐŊĐž", "selected_count": "{count, plural, other {# Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊĐž}}", "send_message": "ĐŸĐžŅˆĐ°Ņ™Đ¸ ĐŋĐžŅ€ŅƒĐē҃", "send_welcome_email": "ĐŸĐžŅˆĐ°Ņ™Đ¸Ņ‚Đĩ Đĩ-ĐŋĐžŅˆŅ‚Ņƒ Đ´ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģĐ¸Ņ†Đĩ", - "server_endpoint": "Server Endpoint", - "server_info_box_app_version": "App Version", - "server_info_box_server_url": "Server URL", + "server_endpoint": "ĐšŅ€Đ°Ņ˜ŅšĐ° Ņ‚Đ°Ņ‡Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "server_info_box_app_version": "ВĐĩŅ€ĐˇĐ¸Ņ˜Đ° АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "server_info_box_server_url": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ URL", "server_offline": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ваĐŊ ĐŧŅ€ĐĩĐļĐĩ (offline)", - "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐŊa ĐŧŅ€ĐĩĐļи (online)", + "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐŊа ĐŧŅ€ĐĩĐļи (online)", "server_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_version": "ВĐĩŅ€ĐˇĐ¸Ņ˜Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "set": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸", @@ -1581,91 +1606,90 @@ "set_date_of_birth": "ПодĐĩŅĐ¸Ņ‚Đĩ Đ´Đ°Ņ‚ŅƒĐŧ Ņ€ĐžŅ’ĐĩŅšĐ°", "set_profile_picture": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸ ĐŋŅ€ĐžŅ„Đ¸ĐģĐŊ҃ ҁĐģиĐē҃", "set_slideshow_to_fullscreen": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ˜ĐĩĐēŅ†Đ¸Ņ˜Ņƒ ҁĐģĐ°Ņ˜Đ´ĐžĐ˛Đ° ĐŊа ҆ĐĩĐž ĐĩĐēŅ€Đ°ĐŊ", - "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", - "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", - "setting_image_viewer_original_title": "Load original image", - "setting_image_viewer_preview_subtitle": "Enable to load a medium-resolution image. Disable to either directly load the original or only use the thumbnail.", - "setting_image_viewer_preview_title": "Load preview image", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}", - "setting_notifications_notify_hours": "{} hours", - "setting_notifications_notify_immediately": "immediately", - "setting_notifications_notify_minutes": "{} minutes", - "setting_notifications_notify_never": "never", - "setting_notifications_notify_seconds": "{} seconds", - "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset", - "setting_notifications_single_progress_title": "Show background backup detail progress", - "setting_notifications_subtitle": "Adjust your notification preferences", - "setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)", - "setting_notifications_total_progress_title": "Show background backup total progress", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_image_viewer_help": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡ Đ´ĐĩŅ‚Đ°Ņ™Đ° ĐŋŅ€Đ˛Đž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ° ĐŧаĐģ҃ ҁĐģĐ¸Ņ‡Đ¸Ņ†Ņƒ, ĐˇĐ°Ņ‚Đ¸Đŧ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ҁҀĐĩĐ´ŅšĐĩ вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐĩ (аĐēĐž ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊ), и ĐŊа ĐēŅ€Đ°Ņ˜Ņƒ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ (аĐēĐž ҘĐĩ ĐžĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊ).", + "setting_image_viewer_original_subtitle": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ҁĐģиĐēа ҃ Đŋ҃ĐŊĐžŅ˜ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ¸ (ВĐĩĐģиĐēа!). ДĐĩаĐēŅ‚Đ¸Đ˛Đ°Ņ†Đ¸Ņ˜ĐžĐŧ ОвĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ĐŧĐžĐļĐĩ҈ да ҁĐŧĐ°ŅšĐ¸Ņˆ ĐŋĐžŅ‚Ņ€ĐžŅˆŅšŅƒ иĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚Đ° и ĐˇĐ°ŅƒĐˇĐĩŅ‚ĐžĐŗ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ° ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ.", + "setting_image_viewer_original_title": "ĐŖŅ‡Đ¸Ņ‚Đ°Ņ˜ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊ҃ ҁĐģиĐē҃", + "setting_image_viewer_preview_subtitle": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ҁĐģиĐēа ҃ ҁҀĐĩĐ´ŅšĐžŅ˜ Ņ€ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ¸. ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ да ҁĐĩ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐŊĐž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ° ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ, иĐģи да ҁĐĩ ŅĐ°ĐŧĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŧиĐŊĐ¸Ņ˜Đ°Ņ‚ŅƒŅ€Đ°.", + "setting_image_viewer_preview_title": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ˜ ҁĐģиĐē҃", + "setting_image_viewer_title": "ĐĄĐģиĐēĐĩ", + "setting_languages_apply": "ĐŸŅ€Đ¸ĐŧĐĩĐŊи", + "setting_languages_subtitle": "ĐŸŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ҘĐĩСиĐē аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "setting_languages_title": "ЈĐĩĐˇĐ¸Ņ†Đ¸", + "setting_notifications_notify_failures_grace_period": "ОбавĐĩŅŅ‚Đ¸ Đž ĐŗŅ€Đĩ҈ĐēаĐŧа ҃ ĐŋŅ€Đ°Đ˛Ņ™ĐĩҚ҃ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋОСадиĐŊи: {duration}", + "setting_notifications_notify_hours": "{count} ŅĐ°Ņ‚Đ¸", + "setting_notifications_notify_immediately": "ОдĐŧĐ°Ņ…", + "setting_notifications_notify_minutes": "{count} ĐŧиĐŊŅƒŅ‚Đ°", + "setting_notifications_notify_never": "ĐŊиĐēада", + "setting_notifications_notify_seconds": "{count} ҁĐĩĐē҃ĐŊди", + "setting_notifications_single_progress_subtitle": "ДĐĩŅ‚Đ°Ņ™ĐŊĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Đž ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšŅƒ, ĐŋĐž СаĐŋĐ¸ŅŅƒ", + "setting_notifications_single_progress_title": "ĐŸŅ€Đ¸ĐēаĐļи Đ´ĐĩŅ‚Đ°Ņ™Đĩ ĐŋОСадиĐŊҁĐēĐžĐŗ ĐŋŅ€Đ°Đ˛Ņ™ĐĩŅšĐ° Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ°", + "setting_notifications_subtitle": "ИСĐŧĐĩĐŊи ĐŊĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đĩ", + "setting_notifications_total_progress_subtitle": "ĐŖĐē҃ĐŋĐŊĐž ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐ¸Ņ… ŅŅ‚Đ°Đ˛Đēи (ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž/҃Đē҃ĐŋĐŊĐž ŅŅ‚Đ°Đ˛Đēи)", + "setting_notifications_total_progress_title": "ĐŸŅ€Đ¸ĐēаĐļи ҃Đē҃ĐŋаĐŊ ĐŊаĐŋŅ€ĐĩдаĐē ĐŋŅ€Đ°Đ˛Ņ™ĐĩŅšĐ° Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ° ҃ ĐŋОСадиĐŊи", + "setting_video_viewer_looping_title": "ПĐĩŅ‚Ņ™Đ°ŅšĐĩ (ЛооĐŋиĐŊĐŗ)", + "setting_video_viewer_original_video_subtitle": "ĐŸŅ€Đ¸ĐģиĐēĐžĐŧ ŅŅ‚Ņ€Đ¸ĐŧĐžĐ˛Đ°ŅšĐ° видĐĩа ŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅƒŅ˜Ņ‚Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ Ņ‡Đ°Đē и Đēада ҘĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ. МоĐļĐĩ дОвĐĩŅŅ‚Đ¸ Đ´Đž ĐąĐ°Ņ„ĐĩŅ€ĐžĐ˛Đ°ŅšĐ°. ВидĐĩĐž ҁĐŊиĐŧŅ†Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋĐŊи ĐģĐžĐēаĐģĐŊĐž ҁĐĩ Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐē҃Ҙ҃ ҃ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐžĐŧ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚Ņƒ ĐąĐĩС ĐžĐąĐˇĐ¸Ņ€Đ° ĐŊа ОвО ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ.", + "setting_video_viewer_original_video_title": "ĐŸŅ€Đ¸ŅĐ¸ĐģĐŊĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊи видĐĩĐž", "settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", - "settings_require_restart": "Please restart Immich to apply this setting", + "settings_require_restart": "Đ ĐĩŅŅ‚Đ°Ņ€Ņ‚ŅƒŅ˜Ņ‚Đĩ Immich да ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐŋŅ€ĐžĐŧĐĩĐŊ҃", "settings_saved": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊа", + "setup_pin_code": "ПодĐĩŅĐ¸Ņ‚Đĩ ПИН ĐēОд", "share": "ПодĐĩĐģи", - "share_add_photos": "Add photos", - "share_assets_selected": "{} selected", - "share_dialog_preparing": "Preparing...", + "share_add_photos": "Đ”ĐžĐ´Đ°Ņ˜ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ", + "share_assets_selected": "Đ˜ĐˇĐ°ĐąŅ€Đ°ĐŊĐž ҘĐĩ {count}", + "share_dialog_preparing": "ĐŸŅ€Đ¸ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ...", "shared": "ДĐĩŅ™ĐĩĐŊĐž", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", + "shared_album_activities_input_disable": "КоĐŧĐĩĐŊŅ‚Đ°Ņ€ ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊ", + "shared_album_activity_remove_content": "Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐžĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ ĐžĐ˛Ņƒ аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚?", + "shared_album_activity_remove_title": "ĐžĐąŅ€Đ¸ŅˆĐ¸ аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚", + "shared_album_section_people_action_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐŊаĐŋŅƒŅˆŅ‚Đ°ŅšŅƒ/҃ĐēĐģĐ°ŅšĐ°ŅšŅƒ иС аĐģĐąŅƒĐŧа", + "shared_album_section_people_action_leave": "ĐŖĐēĐģĐžĐŊи ĐēĐžŅ€Đ¸ŅĐŊиĐēа иС аĐģĐąŅƒĐŧа", + "shared_album_section_people_action_remove_user": "ĐŖĐēĐģĐžĐŊи ĐēĐžŅ€Đ¸ŅĐŊиĐēа иС аĐģĐąŅƒĐŧа", + "shared_album_section_people_title": "ПЕОПЛЕ", "shared_by": "ПодĐĩĐģиО", "shared_by_user": "ДĐĩĐģи {user}", "shared_by_you": "Ви Đ´ĐĩĐģĐ¸Ņ‚Đĩ", "shared_from_partner": "ĐĄĐģиĐēĐĩ Од {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", + "shared_intent_upload_button_progress_text": "ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐž ҘĐĩ {current} / {total}", + "shared_link_app_bar_title": "ДĐĩŅ™ĐĩĐŊи linkОви", + "shared_link_clipboard_copied_massage": "КоĐŋĐ¸Ņ€Đ°ĐŊĐž ҃ ĐŧĐĩŅ’ŅƒŅĐŋŅ€ĐĩĐŧĐŊиĐē (҆ĐģиĐŋĐąĐžĐ°Ņ€Đ´)", + "shared_link_clipboard_text": "ЛиĐŊĐē: {link}\nЛозиĐŊĐēа: {password}", + "shared_link_create_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐēŅ€ĐĩĐ¸Ņ€Đ°ŅšŅƒ Đ´ĐĩŅ™ĐĩĐŊĐžĐŗ linkа", + "shared_link_edit_description_hint": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐžĐŋĐ¸Ņ Đ´ĐĩŅ™ĐĩŅšĐ°", + "shared_link_edit_expire_after_option_days": "{count} даĐŊа", + "shared_link_edit_expire_after_option_hour": "1 ŅĐ°Ņ‚", + "shared_link_edit_expire_after_option_hours": "{count} ŅĐ°Ņ‚Đ¸", + "shared_link_edit_expire_after_option_minute": "1 ĐŧиĐŊŅƒŅ‚Đĩ", + "shared_link_edit_expire_after_option_minutes": "{count} ĐŧиĐŊŅƒŅ‚Đ°", + "shared_link_edit_expire_after_option_months": "{count} ĐŧĐĩҁĐĩŅ†Đ¸", + "shared_link_edit_expire_after_option_year": "{count} ĐŗĐžĐ´Đ¸ĐŊа", + "shared_link_edit_password_hint": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐģОСиĐŊĐē҃ Са Đ´ĐĩŅ™ĐĩҚĐĩ", + "shared_link_edit_submit_button": "ĐŖĐŋdate link", + "shared_link_error_server_url_fetch": "НĐĩ ĐŧĐžĐŗŅƒ да ĐŋŅ€ĐĩŅƒĐˇĐŧĐĩĐŧ URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "shared_link_expires_day": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} даĐŊ(а)", + "shared_link_expires_days": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} даĐŊа", + "shared_link_expires_hour": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ŅĐ°Ņ‚", + "shared_link_expires_hours": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ŅĐ°Ņ‚Đ¸(а)", + "shared_link_expires_minute": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ĐŧиĐŊŅƒŅ‚", + "shared_link_expires_minutes": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ĐŧиĐŊŅƒŅ‚Đ°", + "shared_link_expires_never": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ ∞", + "shared_link_expires_second": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ҁĐĩĐē҃ĐŊĐ´Ņƒ", + "shared_link_expires_seconds": "Đ˜ŅŅ‚Đ¸Ņ‡Đĩ Са {count} ҁĐĩĐē҃ĐŊди", + "shared_link_individual_shared": "ĐŸĐžŅ˜ĐĩдиĐŊĐ°Ņ‡ĐŊĐž Đ´ĐĩŅ™ĐĩĐŊĐž", + "shared_link_manage_links": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đ´ĐĩŅ™ĐĩĐŊиĐŧ linkОвиĐŧа", "shared_link_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ Đ´ĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ", "shared_links": "ДĐĩŅ™ĐĩĐŊĐĩ вĐĩСĐĩ", - "shared_links_description": "ДĐĩĐģĐ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ĐŋĐžĐŧĐžŅ›Ņƒ ĐģиĐŊĐēа", + "shared_links_description": "ДĐĩĐģĐ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ĐŋĐžĐŧĐžŅ†ĖŅƒ linkа", "shared_photos_and_videos_count": "{assetCount, plural, other {# Đ´ĐĩŅ™ĐĩĐŊĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ.}}", - "shared_with_me": "Shared with me", + "shared_with_me": "ДĐĩŅ™ĐĩĐŊĐž ŅĐ° ĐŧĐŊĐžĐŧ", "shared_with_partner": "ДĐĩĐģи ҁĐĩ ŅĐ° {partner}", "sharing": "ДĐĩŅ™ĐĩҚĐĩ", "sharing_enter_password": "ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ĐģОСиĐŊĐē҃ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи ĐžĐ˛Ņƒ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ.", - "sharing_page_album": "Shared albums", - "sharing_page_description": "Create shared albums to share photos and videos with people in your network.", - "sharing_page_empty_list": "EMPTY LIST", + "sharing_page_album": "ДĐĩŅ™ĐĩĐŊи аĐģĐąŅƒĐŧи", + "sharing_page_description": "НаĐŋŅ€Đ°Đ˛Đ¸ Đ´ĐĩŅ™ĐĩĐŊĐĩ аĐģĐąŅƒĐŧĐĩ да Đ´ĐĩĐģĐ¸Ņˆ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ŅĐ° Ņ™ŅƒĐ´Đ¸Đŧа ĐŊа Ņ‚Đ˛ĐžŅ˜ĐžŅ˜ ĐŧŅ€ĐĩĐļи.", + "sharing_page_empty_list": "ПРАЗНА ЛИСĐĸА", "sharing_sidebar_description": "ĐŸŅ€Đ¸ĐēаĐļĐ¸Ņ‚Đĩ вĐĩĐˇŅƒ Đ´Đž ДĐĩŅ™ĐĩŅšĐ° ĐŊа ĐąĐžŅ‡ĐŊĐžŅ˜ Ņ‚Ņ€Đ°Ņ†Đ¸", - "sharing_silver_appbar_create_shared_album": "New shared album", - "sharing_silver_appbar_share_partner": "Share with partner", + "sharing_silver_appbar_create_shared_album": "НаĐŋŅ€Đ°Đ˛Đ¸ Đ´ĐĩŅ™ĐĩĐŊи аĐģĐąŅƒĐŧ", + "sharing_silver_appbar_share_partner": "ПодĐĩĐģи ŅĐ° ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€ĐžĐŧ", "shift_to_permanent_delete": "ĐŋŅ€Đ¸Ņ‚Đ¸ŅĐŊĐ¸Ņ‚Đĩ ⇧ да Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", "show_album_options": "ĐŸŅ€Đ¸ĐēаĐļи ĐžĐŋŅ†Đ¸Ņ˜Đĩ аĐģĐąŅƒĐŧа", "show_albums": "ĐŸŅ€Đ¸ĐēаĐļи аĐģĐąŅƒĐŧĐĩ", @@ -1694,8 +1718,8 @@ "sign_up": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸ ҁĐĩ", "size": "ВĐĩĐģĐ¸Ņ‡Đ¸ĐŊа", "skip_to_content": "ĐŸŅ€ĐĩŅ’Đ¸ ĐŊа ŅĐ°Đ´Ņ€ĐļĐ°Ņ˜", - "skip_to_folders": "ĐŸŅ€ĐĩҁĐēĐžŅ‡Đ¸ ĐŊа Ņ„Đ°ŅŅ†Đ¸ĐēĐģĐĩ", - "skip_to_tags": "ĐŸŅ€ĐĩҁĐēĐžŅ‡Đ¸ ĐŊа ОСĐŊаĐēĐĩ (tags)", + "skip_to_folders": "ĐŸŅ€ĐĩҁĐēĐžŅ‡Đ¸ Đ´Đž ĐŧаĐŋа (Ņ„ĐžĐģĐ´ĐĩҀҁ)", + "skip_to_tags": "ĐŸŅ€ĐĩҁĐēĐžŅ‡Đ¸ Đ´Đž ОСĐŊаĐēа (tags)", "slideshow": "ĐĄĐģĐ°Ņ˜Đ´ĐžĐ˛Đ¸", "slideshow_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ҁĐģĐ°Ņ˜Đ´ĐžĐ˛Đ°", "sort_albums_by": "ĐĄĐžŅ€Ņ‚Đ¸Ņ€Đ°Ņ˜ аĐģĐąŅƒĐŧĐĩ ĐŋĐž...", @@ -1719,51 +1743,52 @@ "status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ", "stop_motion_photo": "Đ—Đ°ŅƒŅŅ‚Đ°Đ˛Đ¸ ĐŋĐžĐēŅ€ĐĩŅ‚ĐŊ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ", "stop_photo_sharing": "ЖĐĩĐģĐ¸Ņ‚Đĩ да ĐˇĐ°ŅƒŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ Đ´ĐĩŅ™ĐĩҚĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°?", - "stop_photo_sharing_description": "{partner} Đ˛Đ¸ŅˆĐĩ ĐŊĐĩŅ›Đĩ ĐŧĐžŅ›Đ¸ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°Đŧа.", + "stop_photo_sharing_description": "{partner} Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ҆ˁĐĩ ĐŧĐžŅ†ĖĐ¸ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋи Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°Đŧа.", "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩŅŅ‚Đ°ĐŊĐ¸Ņ‚Đĩ да Đ´ĐĩĐģĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ŅĐ° ОвиĐŧ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐžĐŧ", "storage": "ĐĄĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ (Storage space)", "storage_label": "ОзĐŊаĐēа Са ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩҚĐĩ", + "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚ĐĩŅšĐ°", "storage_usage": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ҁĐĩ {used} Од {available}", "submit": "Đ”ĐžŅŅ‚Đ°Đ˛Đ¸", "suggestions": "ĐĄŅƒĐŗĐĩŅŅ‚Đ¸Ņ˜Đĩ", "sunrise_on_the_beach": "ИСĐģаСаĐē ҁ҃ĐŊŅ†Đ° ĐŊа ĐŋĐģаĐļи", "support": "ĐŸĐžĐ´Ņ€ŅˆĐēа", "support_and_feedback": "ĐŸĐžĐ´Ņ€ŅˆĐēа и ĐŋĐžĐ˛Ņ€Đ°Ņ‚ĐŊĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ", - "support_third_party_description": "Đ’Đ°ŅˆĐ° иĐŧĐŧĐ¸Ņ†Ņ… иĐŊŅŅ‚Đ°ĐģĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ҁĐŋаĐēОваĐŊа Од ŅŅ‚Ņ€Đ°ĐŊĐĩ ҂ҀĐĩŅ›Đĩ ŅŅ‚Ņ€Đ°ĐŊĐĩ. ĐŸŅ€ĐžĐąĐģĐĩĐŧи ŅĐ° ĐēĐžŅ˜Đ¸Đŧа ҁĐĩ ŅŅƒĐžŅ‡Đ°Đ˛Đ°Ņ‚Đĩ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ŅƒĐˇŅ€ĐžĐēОваĐŊи Ņ‚Đ¸Đŧ ĐŋаĐēĐĩŅ‚ĐžĐŧ, Đŋа Đ˛Đ°Ņ ĐŧĐžĐģиĐŧĐž да иĐŧ ĐŋŅ€Đ˛Đž ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧĐĩ ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ›Đ¸ Đ´ĐžŅšĐĩ вĐĩСĐĩ.", + "support_third_party_description": "Đ’Đ°ŅˆĐ° иĐŧĐŧĐ¸Ņ†Ņ… иĐŊŅŅ‚Đ°ĐģĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ҁĐŋаĐēОваĐŊа Од ŅŅ‚Ņ€Đ°ĐŊĐĩ ҂ҀĐĩ҆ˁĐĩ ŅŅ‚Ņ€Đ°ĐŊĐĩ. ĐŸŅ€ĐžĐąĐģĐĩĐŧи ŅĐ° ĐēĐžŅ˜Đ¸Đŧа ҁĐĩ ŅŅƒĐžŅ‡Đ°Đ˛Đ°Ņ‚Đĩ ĐŧĐžĐŗŅƒ ĐąĐ¸Ņ‚Đ¸ ŅƒĐˇŅ€ĐžĐēОваĐŊи Ņ‚Đ¸Đŧ ĐŋаĐēĐĩŅ‚ĐžĐŧ, Đŋа Đ˛Đ°Ņ ĐŧĐžĐģиĐŧĐž да иĐŧ ĐŋŅ€Đ˛Đž ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧĐĩ ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ†ĖĐ¸ Đ´ĐžŅšĐĩ вĐĩСĐĩ.", "swap_merge_direction": "ЗаĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋŅ€Đ°Đ˛Đ°Ņ† ҁĐŋĐ°Ņ˜Đ°ŅšĐ°", "sync": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_albums": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜ аĐģĐąŅƒĐŧĐĩ", + "sync_albums_manual_subtitle": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜Ņ‚Đĩ ŅĐ˛Đĩ ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐĩ видĐĩĐž СаĐŋĐ¸ŅĐĩ и Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ ŅĐ° Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊиĐŧ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊиĐŧ аĐģĐąŅƒĐŧиĐŧа", + "sync_upload_album_setting_subtitle": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ и ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đĩ и видĐĩĐž СаĐŋĐ¸ŅĐĩ ҃ ĐžĐ´Đ°ĐąŅ€Đ°ĐŊĐĩ аĐģĐąŅƒĐŧĐĩ ĐŊа Immich-u", "tag": "ОзĐŊаĐēа (tag)", - "tag_assets": "ОзĐŊĐ°Ņ‡Đ¸Ņ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "tag_assets": "ОзĐŊĐ°Ņ‡Đ¸Ņ‚Đĩ (tag) ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°", "tag_created": "НаĐŋŅ€Đ°Đ˛Ņ™ĐĩĐŊа ОСĐŊаĐēа (tag): {tag}", "tag_feature_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Đ˛Đ°ŅšĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° и видĐĩĐž ҁĐŊиĐŧаĐēа ĐŗŅ€ŅƒĐŋĐ¸ŅĐ°ĐŊĐ¸Ņ… ĐŋĐž ĐģĐžĐŗĐ¸Ņ‡ĐŊиĐŧ Ņ‚ĐĩĐŧаĐŧа ОСĐŊаĐēа", "tag_not_found_question": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩŅ‚Đĩ ОСĐŊаĐē҃ (tag)? НаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņƒ ОСĐŊаĐē҃", "tag_people": "ОзĐŊĐ°Ņ‡Đ¸Ņ‚Đĩ Ņ™ŅƒĐ´Đĩ", "tag_updated": "АĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа ОСĐŊаĐēа (tag): {tag}", - "tagged_assets": "ОзĐŊĐ°Ņ‡ĐĩĐŊĐž (tagged) {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", + "tagged_assets": "ОзĐŊĐ°Ņ‡ĐĩĐŊĐž (tagĐŗĐĩĐ´) {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", "tags": "ОзĐŊаĐēĐĩ (tags)", "template": "ШайĐģĐžĐŊ (ĐĸĐĩĐŧĐŋĐģĐ°Ņ‚Đĩ)", "theme": "ĐĸĐĩĐŧĐĩ", "theme_selection": "Đ˜ĐˇĐąĐžŅ€ Ņ‚ĐĩĐŧĐĩ", "theme_selection_description": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ Ņ‚ĐĩĐŧ҃ ĐŊа ŅĐ˛ĐĩŅ‚Đģ҃ иĐģи Ņ‚Đ°ĐŧĐŊ҃ ĐŊа ĐžŅĐŊĐžĐ˛Ņƒ ŅĐ¸ŅŅ‚ĐĩĐŧҁĐēĐ¸Ņ… ĐŋŅ€ĐĩŅ„ĐĩŅ€ĐĩĐŊŅ†Đ¸Ņ˜Đ° Đ˛Đ°ŅˆĐĩĐŗ ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐļĐ¸Đ˛Đ°Ņ‡Đ°", - "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles", - "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", - "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", - "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", - "theme_setting_theme_subtitle": "Choose the app's theme setting", - "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", - "theme_setting_three_stage_loading_title": "Enable three-stage loading", - "they_will_be_merged_together": "ОĐŊи Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ҁĐŋĐžŅ˜ĐĩĐŊи ĐˇĐ°Ņ˜ĐĩĐ´ĐŊĐž", - "third_party_resources": "Đ ĐĩŅŅƒŅ€ŅĐ¸ ҂ҀĐĩŅ›Đ¸Ņ… ŅŅ‚Ņ€Đ°ĐŊа", - "time_based_memories": "ĐĄĐĩŅ›Đ°ŅšĐ° ĐˇĐ°ŅĐŊОваĐŊа ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃", + "theme_setting_asset_list_storage_indicator_title": "ĐŸŅ€Đ¸ĐēаĐļи иĐŊдиĐēĐ°Ņ‚ĐžŅ€ ĐŋŅ€ĐžŅŅ‚ĐžŅ€Đ° ĐŊа СаĐŋĐ¸ŅĐ¸Đŧа", + "theme_setting_asset_list_tiles_per_row_title": "Đ‘Ņ€ĐžŅ˜ СаĐŋĐ¸ŅĐ° ĐŋĐž Ņ€ĐĩĐ´Ņƒ {count}", + "theme_setting_colorful_interface_subtitle": "НаĐŊĐĩŅĐ¸Ņ‚Đĩ ĐžŅĐŊОвĐŊ҃ ĐąĐžŅ˜Ņƒ ĐŊа ĐŋОСадиĐŊҁĐēĐĩ ĐŋĐžĐ˛Ņ€ŅˆĐ¸ĐŊĐĩ.", + "theme_setting_colorful_interface_title": "Đ¨Đ°Ņ€ĐĩĐŊи иĐŊŅ‚ĐĩҀ҄ĐĩҘҁ", + "theme_setting_image_viewer_quality_subtitle": "ĐŸŅ€Đ¸ĐģĐ°ĐŗĐžĐ´Đ¸Ņ‚Đĩ ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€Đ¸ĐēаСа Са Đ´ĐĩŅ‚Đ°Ņ™ĐŊĐž ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Đ˛Đ°ŅšĐĩ ҁĐģиĐēĐĩ", + "theme_setting_image_viewer_quality_title": "КваĐģĐ¸Ņ‚ĐĩŅ‚ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ° ҁĐģиĐēа", + "theme_setting_primary_color_subtitle": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ĐąĐžŅ˜Ņƒ Са ĐŗĐģавĐŊĐĩ Ņ€Đ°Đ´ŅšĐĩ и аĐē҆ĐĩĐŊŅ‚Đĩ.", + "theme_setting_primary_color_title": "ĐŸŅ€Đ¸ĐŧĐ°Ņ€ĐŊа ĐąĐžŅ˜Đ°", + "theme_setting_system_primary_color_title": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ŅĐ¸ŅŅ‚ĐĩĐŧҁĐē҃ ĐąĐžŅ˜Ņƒ", + "theme_setting_system_theme_switch": "ĐŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи (ĐŸŅ€Đ°Ņ‚Đ¸ ĐžĐŋŅ†Đ¸Ņ˜Đĩ ŅĐ¸ŅŅ‚ĐĩĐŧа)", + "theme_setting_theme_subtitle": "ОдабĐĩŅ€Đ¸ Ņ‚ĐĩĐŧ҃ ŅĐ¸ŅŅ‚ĐĩĐŧа", + "theme_setting_three_stage_loading_subtitle": "ĐĸŅ€ĐžŅŅ‚ĐĩĐŋĐĩĐŊĐž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ĐŧĐžĐļда ŅƒĐąŅ€ĐˇĐ° ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ, ĐŋĐž ҆ĐĩĐŊ҃ ĐŋĐžŅ‚Ņ€ĐžŅˆŅšĐĩ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа", + "theme_setting_three_stage_loading_title": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ Ņ‚Ņ€ĐžŅŅ‚ĐĩĐŋĐĩĐŊĐž ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ", + "they_will_be_merged_together": "ОĐŊи ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ ҁĐŋĐžŅ˜ĐĩĐŊи ĐˇĐ°Ņ˜ĐĩĐ´ĐŊĐž", + "third_party_resources": "Đ ĐĩŅŅƒŅ€ŅĐ¸ ҂ҀĐĩŅ†ĖĐ¸Ņ… ŅŅ‚Ņ€Đ°ĐŊа", + "time_based_memories": "ĐĄĐĩŅ†ĖĐ°ŅšĐ° ĐˇĐ°ŅĐŊОваĐŊа ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃", "timeline": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēа ĐģиĐŊĐ¸Ņ˜Đ°", "timezone": "Đ’Ņ€ĐĩĐŧĐĩĐŊҁĐēа СОĐŊа", "to_archive": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜", @@ -1771,36 +1796,38 @@ "to_favorite": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸ ĐēаО Ņ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚", "to_login": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ°", "to_parent": "Đ’Ņ€Đ°Ņ‚Đ¸ ҁĐĩ ĐŊаСад", - "to_trash": "ĐĄĐŧĐĩŅ›Đĩ", - "toggle_settings": "НаĐŧĐĩŅŅ‚Đ¸ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", - "toggle_theme": "НаĐŧĐĩŅŅ‚Đ¸ Ņ‚Đ°ĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", + "to_trash": "ĐĄĐŧĐĩ҆ˁĐĩ", + "toggle_settings": "NameŅŅ‚Đ¸ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°", + "toggle_theme": "NameŅŅ‚Đ¸ Ņ‚Đ°ĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", "total": "ĐŖĐē҃ĐŋĐŊĐž", "total_usage": "ĐŖĐē҃ĐŋĐŊа ҃ĐŋĐžŅ‚Ņ€Đĩйа", "trash": "ĐžŅ‚Đŋад", "trash_all": "Đ‘Đ°Ņ†Đ¸ ŅĐ˛Đĩ ҃ ĐžŅ‚Đŋад", "trash_count": "ĐžŅ‚Đŋад {count, number}", "trash_delete_asset": "ĐžŅ‚Đŋад/Đ˜ĐˇĐąŅ€Đ¸ŅˆĐ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", - "trash_emptied": "Emptied trash", - "trash_no_results_message": "ĐĄĐģиĐēĐĩ и видĐĩĐž СаĐŋĐ¸ŅĐ¸ ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ Ņ›Đĩ ҁĐĩ ĐŋĐžŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ОвдĐĩ.", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", - "trashed_items_will_be_permanently_deleted_after": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ĐŊаĐēĐžĐŊ {days, plural, one {# даĐŊ} few {# даĐŊа} other {# даĐŊа}}.", + "trash_emptied": "Đ˜ŅĐŋŅ€Đ°ĐˇĐŊиО ҁĐŧĐĩ҆ˁĐĩ", + "trash_no_results_message": "ĐĄĐģиĐēĐĩ и видĐĩĐž СаĐŋĐ¸ŅĐ¸ ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ ҆ˁĐĩ ҁĐĩ ĐŋĐžŅ˜Đ°Đ˛Đ¸Ņ‚Đ¸ ОвдĐĩ.", + "trash_page_delete_all": "ĐžĐąŅ€Đ¸ŅˆĐ¸ ŅĐ˛Đĩ", + "trash_page_empty_trash_dialog_content": "Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ¸ŅĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đ° ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°? Ови ĐŋŅ€ĐĩĐ´ĐŧĐĩŅ‚Đ¸ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ҃ĐēĐģĐžŅšĐĩĐŊи иС Immich-a", + "trash_page_info": "ĐĄŅ‚Đ°Đ˛ĐēĐĩ Đ¸ĐˇĐąĐ°Ņ‡ĐĩĐŊĐĩ иС ĐžŅ‚Đŋада ĐąĐ¸Ņ†ĖĐĩ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ĐŊаĐēĐžĐŊ {days} даĐŊа", + "trash_page_no_assets": "НĐĩĐŧа ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ", + "trash_page_restore_all": "Đ’Ņ€Đ°Ņ‚Đ¸ ŅĐ˛Đĩ", + "trash_page_select_assets_btn": "ИСайĐĩŅ€Đ¸Ņ‚Đĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°", + "trash_page_title": "ĐžŅ‚Đŋад ({count})", + "trashed_items_will_be_permanently_deleted_after": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ ҃ ĐžŅ‚ĐŋĐ°Đ´Ņƒ ҆ˁĐĩ ĐąĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐĩ ĐŊаĐēĐžĐŊ {days, plural, one {# даĐŊ} few {# даĐŊа} other {# даĐŊа}}.", "type": "Đ’Ņ€ŅŅ‚Đ°", + "unable_to_change_pin_code": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ПИН ĐēОд", + "unable_to_setup_pin_code": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ ĐŋОдĐĩŅĐ¸Ņ‚Đ¸ ПИН ĐēОд", "unarchive": "Đ’Ņ€Đ°Ņ‚Đ¸ иС Đ°Ņ€Ņ…Đ¸Đ˛Đĩ", - "unarchived_count": "{count, plural, other {Nearhivirano#}}", + "unarchived_count": "{count, plural, other {НĐĩĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž#}}", "unfavorite": "Đ˜ĐˇĐąĐ°Ņ†Đ¸ иС ĐžĐŧĐ¸Ņ™ĐĩĐŊĐ¸Ņ… (҃ĐŊŅ„Đ°Đ˛ĐžŅ€Đ¸Ņ‚Đĩ)", "unhide_person": "ĐžŅ‚ĐēŅ€Đ¸Ņ˜ ĐžŅĐžĐąŅƒ", "unknown": "НĐĩĐŋОСĐŊĐ°Ņ‚", "unknown_country": "НĐĩĐŋОСĐŊĐ°Ņ‚Đ° СĐĩĐŧŅ™Đ°", "unknown_year": "НĐĩĐŋОСĐŊĐ°Ņ‚Đ° ГодиĐŊа", "unlimited": "НĐĩĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊĐž", - "unlink_motion_video": "ĐŸŅ€ĐĩĐēиĐŊĐ¸Ņ‚Đĩ вĐĩĐˇŅƒ ŅĐ° видĐĩĐž ҁĐŊиĐŧĐēĐžĐŧ", - "unlink_oauth": "ĐŸŅ€ĐĩĐēиĐŊи вĐĩĐˇŅƒ ŅĐ° Oauth-om", + "unlink_motion_video": "ОдвĐĩĐļи видĐĩĐž Од ҁĐģиĐēĐĩ", + "unlink_oauth": "ĐŸŅ€ĐĩĐēиĐŊи вĐĩĐˇŅƒ ŅĐ° ĐžĐ°ŅƒŅ‚Ņ…-ĐžĐŧ", "unlinked_oauth_account": "ОĐŋОСваĐŊа вĐĩСа OAuth ĐŊаĐģĐžĐŗĐ°", "unmute_memories": "ĐŖĐēŅ™ŅƒŅ‡Đ¸ ҃ҁĐŋĐžĐŧĐĩĐŊĐĩ", "unnamed_album": "НĐĩиĐŧĐĩĐŊОваĐŊи аĐģĐąŅƒĐŧ", @@ -1811,14 +1838,15 @@ "unselect_all_duplicates": "ПоĐŊĐ¸ŅˆŅ‚Đ¸ Đ¸ĐˇĐąĐžŅ€ ŅĐ˛Đ¸Ņ… Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đ°", "unstack": "Đ Đ°ĐˇĐŗĐžĐŧиĐģĐ°Ņ˜ (ĐŖĐŊ-ŅŅ‚Đ°Ņ†Đē)", "unstacked_assets_count": "НĐĩҁĐģĐžĐļĐĩĐŊĐž {count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ}}", - "untracked_files": "НĐĩĐŋŅ€Đ°Ņ›ĐĩĐŊĐĩ Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", - "untracked_files_decription": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đ¸ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. one ĐŧĐžĐŗŅƒ ĐŊĐ°ŅŅ‚Đ°Ņ‚Đ¸ ĐˇĐąĐžĐŗ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ… ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩŅšĐ°, ĐˇĐąĐžĐŗ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° иĐģи ĐēаО ĐŋŅ€ĐĩĐžŅŅ‚Đ°Ņ‚Đ°Đē ĐˇĐąĐžĐŗ ĐŗŅ€Đĩ҈ĐēĐĩ", - "up_next": "ĐĄĐģĐĩĐ´ĐĩŅ›(Đĩ/и)", + "untracked_files": "НĐĩĐŋŅ€Đ°Ņ†ĖĐĩĐŊĐĩ Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ", + "untracked_files_decription": "АĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋŅ€Đ°Ņ‚Đ¸ ОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ. ОĐŊĐĩ ĐŧĐžĐŗŅƒ ĐŊĐ°ŅŅ‚Đ°Ņ‚Đ¸ ĐˇĐąĐžĐŗ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐ¸Ņ… ĐŋŅ€ĐĩĐŧĐĩŅˆŅ‚ĐĩŅšĐ°, ĐˇĐąĐžĐŗ ĐŋŅ€ĐĩĐēиĐŊŅƒŅ‚Đ¸Ņ… ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐ° иĐģи ĐēаО ĐŋŅ€ĐĩĐžŅŅ‚Đ°Ņ‚Đ°Đē ĐˇĐąĐžĐŗ ĐŗŅ€Đĩ҈ĐēĐĩ", + "up_next": "ĐĄĐģĐĩĐ´Đĩ҆ˁĐĩ", + "updated_at": "АĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊĐž", "updated_password": "АĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊа ĐģОСиĐŊĐēа", "upload": "ĐŖĐŋĐģĐžĐ°Đ´ŅƒŅ˜", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐĩĐģĐŊĐž ҃ĐŋĐģĐžĐ°Đ´ĐžĐ˛Đ°ŅšĐĩ", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", + "upload_dialog_info": "Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊаĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋĐ¸Ņ˜Ņƒ Đ¸ĐˇĐ°ĐąŅ€Đ°ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ?", + "upload_dialog_title": "ĐžŅ‚ĐŋŅ€ĐĩĐŧи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", "upload_errors": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ ҘĐĩ ĐˇĐ°Đ˛Ņ€ŅˆĐĩĐŊĐž ŅĐ° {count, plural, one {# ĐŗŅ€Đĩ҈ĐēĐžĐŧ} other {# ĐŗŅ€ĐĩŅˆĐ°Đēа}}, ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи ĐŊОвĐĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ Са ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ (҃ĐŋĐģОад).", "upload_progress": "ĐŸŅ€ĐĩĐžŅŅ‚Đ°ĐģĐž {remaining, number} – ĐžĐąŅ€Đ°Ņ’ĐĩĐŊĐž {processed, number}/{total, number}", "upload_skipped_duplicates": "ĐŸŅ€ĐĩҁĐēĐžŅ‡ĐĩĐŊĐž {count, plural, one {# Đ´ŅƒĐŋĐģа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} other {# Đ´ŅƒĐŋĐģĐ¸Ņ… Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", @@ -1826,43 +1854,44 @@ "upload_status_errors": "Đ“Ņ€Đĩ҈ĐēĐĩ", "upload_status_uploaded": "ĐžŅ‚ĐŋŅ€ĐĩĐŧŅ™ĐĩĐŊĐž (ĐŖĐŋĐģОадĐĩĐ´)", "upload_success": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ ҘĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž, ĐžŅĐ˛ĐĩĐļĐ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ да ĐąĐ¸ŅŅ‚Đĩ видĐĩĐģи ĐŊОва ҁҀĐĩĐ´ŅŅ‚Đ˛Đ° Са ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ (҃ĐŋĐģОад).", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "ĐŖĐ Đ›", + "upload_to_immich": "ĐžŅ‚ĐŋŅ€ĐĩĐŧи ҃ Immich ({count})", + "uploading": "ĐžŅ‚ĐŋŅ€ĐĩĐŧĐ°ŅšĐĩ", "usage": "ĐŖĐŋĐžŅ‚Ņ€Đĩйа", - "use_current_connection": "use current connection", + "use_current_connection": "ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ҂ҀĐĩĐŊŅƒŅ‚ĐŊ҃ вĐĩĐˇŅƒ", "use_custom_date_range": "ĐŖĐŧĐĩŅŅ‚Đž Ņ‚ĐžĐŗĐ° ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸ĐģĐ°ĐŗĐžŅ’ĐĩĐŊи ĐŋĐĩŅ€Đ¸ĐžĐ´", "user": "ĐšĐžŅ€Đ¸ŅĐŊиĐē", "user_id": "ИД ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "user_liked": "{user} ҘĐĩ ĐģĐ°Ņ˜ĐēОваО {type, select, photo {ĐžĐ˛Ņƒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Ņƒ} video {ĐžĐ˛Đ°Ņ˜ видĐĩĐž СаĐŋĐ¸Ņ} asset {ĐžĐ˛Ņƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃} other {ОвО}}", + "user_pin_code_settings": "ПИН ĐēОд", + "user_pin_code_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đ¸Đŧ ПИН ĐēОдОĐŧ", "user_purchase_settings": "ĐšŅƒĐŋОвиĐŊа", "user_purchase_settings_description": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ Đē҃ĐŋОвиĐŊĐžĐŧ", "user_role_set": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸ {user} ĐēаО {role}", - "user_usage_detail": "ДĐĩŅ‚Đ°Ņ™Đ¸ ĐēĐžŅ€Đ¸ŅˆŅ›ĐĩŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа", - "user_usage_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ĐēĐžŅ€Đ¸ŅˆŅ›ĐĩŅšĐ° ĐŊаĐģĐžĐŗĐ°", - "user_usage_stats_description": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đē҃ ĐēĐžŅ€Đ¸ŅˆŅ›ĐĩŅšĐ° ĐŊаĐģĐžĐŗĐ°", + "user_usage_detail": "ДĐĩŅ‚Đ°Ņ™Đ¸ ĐēĐžŅ€Đ¸ŅˆŅ†ĖĐĩŅšĐ° ĐēĐžŅ€Đ¸ŅĐŊиĐēа", + "user_usage_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ĐēĐžŅ€Đ¸ŅˆŅ†ĖĐĩŅšĐ° ĐŊаĐģĐžĐŗĐ°", + "user_usage_stats_description": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đē҃ ĐēĐžŅ€Đ¸ŅˆŅ†ĖĐĩŅšĐ° ĐŊаĐģĐžĐŗĐ°", "username": "ĐšĐžŅ€Đ¸ŅĐŊĐ¸Ņ‡ĐēĐž иĐŧĐĩ", "users": "ĐšĐžŅ€Đ¸ŅĐŊĐ¸Ņ†Đ¸", "utilities": "АĐģĐ°Ņ‚Đ¸", "validate": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "МоĐģиĐŧĐž Đ˛Đ°Ņ да ҃ĐŊĐĩҁĐĩŅ‚Đĩ ваĐļĐĩŅ†ĖĐ¸ URL", "variables": "ĐŸŅ€ĐžĐŧĐĩĐŊŅ™Đ¸Đ˛Đĩ (Đ˛Đ°Ņ€Đ¸Đ°ĐąĐģĐĩҁ)", "version": "ВĐĩŅ€ĐˇĐ¸Ņ˜Đ°", "version_announcement_closing": "ĐĸĐ˛ĐžŅ˜ ĐŋŅ€Đ¸Ņ˜Đ°Ņ‚ĐĩŅ™, АĐģĐĩĐēҁ", - "version_announcement_message": "Đ—Đ´Ņ€Đ°Đ˛Đž ĐŋŅ€Đ¸Ņ˜Đ°Ņ‚ĐĩŅ™Ņƒ, ĐŋĐžŅŅ‚ĐžŅ˜Đ¸ ĐŊОва вĐĩŅ€ĐˇĐ¸Ņ˜Đ° аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ, ĐŧĐžĐģиĐŧĐž Đ˛Đ°Ņ да ĐžĐ´Đ˛ĐžŅ˜Đ¸Ņ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩ да ĐŋĐžŅĐĩŅ‚Đ¸Ņ‚Đĩ ĐŊаĐŋĐžĐŧĐĩĐŊĐĩ Đž Đ¸ĐˇĐ´Đ°ŅšŅƒ и ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ҁĐĩ да ҘĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ ĐēаĐēĐž йи ҁĐĩ ҁĐŋŅ€ĐĩŅ‡Đ¸ĐģĐĩ йиĐģĐž ĐēаĐēвĐĩ ĐŋĐžĐŗŅ€Đĩ҈ĐŊĐĩ ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Đĩ, ĐŋĐžŅĐĩĐąĐŊĐž аĐēĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ WatchTower иĐģи йиĐģĐž ĐēĐžŅ˜Đ¸ ĐŧĐĩŅ…Đ°ĐŊиСаĐŧ ĐēĐžŅ˜Đ¸ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи ҃ĐŋŅ€Đ°Đ˛Ņ™Đ° аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšĐĩĐŧ Đ˛Đ°ŅˆĐĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", - "version_announcement_overlay_release_notes": "release notes", - "version_announcement_overlay_text_1": "Hi friend, there is a new release of", - "version_announcement_overlay_text_2": "please take your time to visit the ", - "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.", - "version_announcement_overlay_title": "New Server Version Available 🎉", + "version_announcement_message": "Đ—Đ´Ņ€Đ°Đ˛Đž! Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ҘĐĩ ĐŊОва вĐĩŅ€ĐˇĐ¸Ņ˜Đ° Immich-a. МоĐģиĐŧĐž Đ˛Đ°Ņ да ĐžĐ´Đ˛ĐžŅ˜Đ¸Ņ‚Đĩ ĐŧаĐģĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊа да ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°Ņ‚Đĩ ĐąĐĩĐģĐĩ҈ĐēĐĩ Đž Đ¸ĐˇĐ´Đ°ŅšŅƒ ĐēаĐēĐž ĐąĐ¸ŅŅ‚Đĩ йиĐģи ŅĐ¸ĐŗŅƒŅ€ĐŊи да ҘĐĩ Đ˛Đ°ŅˆĐĩ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊĐž и ҁĐŋŅ€ĐĩŅ‡Đ¸Đģи ĐĩвĐĩĐŊŅ‚ŅƒĐ°ĐģĐŊĐĩ ĐŋĐžĐŗŅ€Đĩ҈ĐŊĐĩ ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Đĩ, ĐŋĐžŅĐĩĐąĐŊĐž аĐēĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ WĐ°Ņ‚Ņ†Ņ…ĐĸĐžwĐĩŅ€ иĐģи йиĐģĐž ĐēĐžŅ˜Đ¸ ĐŧĐĩŅ…Đ°ĐŊиСаĐŧ ĐēĐžŅ˜Đ¸ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи аĐļŅƒŅ€Đ¸Ņ€Đ° Đ˛Đ°ŅˆŅƒ Immich иĐŊŅŅ‚Đ°ĐŊŅ†Ņƒ.", + "version_announcement_overlay_release_notes": "ĐŊОвиĐŊĐĩ ĐŊОвĐĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ", + "version_announcement_overlay_text_1": "Ћао, ĐŊОва вĐĩŅ€ĐˇĐ¸Ņ˜Đ°", + "version_announcement_overlay_text_2": "ĐŧĐžĐģиĐŧĐž Đ’Đ°Ņ Đ¸ĐˇĐ´Đ˛ĐžŅ˜Đ¸Ņ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊа да ĐŋĐžĐŗĐģĐĩdate ", + "version_announcement_overlay_text_3": " и ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да ҁ҃ Đ’Đ°Ņˆ Đ´ĐžŅ†ĐēĐĩŅ€-Ņ†ĐžĐŧĐŋĐžŅĐĩ и .ĐĩĐŊв ĐŊĐ°Ņ˜ĐŊĐžĐ˛Đ¸Ņ˜Đĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ да йи иСйĐĩĐŗĐģи ĐŗŅ€Đĩ҈ĐēĐĩ ҃ Ņ€Đ°Đ´Ņƒ. ĐŸĐžĐŗĐžŅ‚ĐžĐ˛Ņƒ аĐēĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ WĐ°Ņ‚Ņ†Ņ…ĐĸĐžwĐĩŅ€ иĐģи йиĐģĐž ĐēĐžŅ˜Đ¸ Đ´Ņ€ŅƒĐŗĐ¸ ĐŧĐĩŅ…Đ°ĐŊиСаĐŧ ĐēĐžŅ˜Đ¸ Đ°ŅƒŅ‚ĐžĐŧĐ°Ņ‚ŅĐēи иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ° ĐŊОвĐĩ вĐĩŅ€ĐˇĐ¸Ņ˜Đĩ Đ˛Đ°ŅˆĐĩ ҁĐĩŅ€Đ˛ĐĩҀҁĐēĐĩ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đĩ.", + "version_announcement_overlay_title": "Нова вĐĩŅ€ĐˇĐ¸Ņ˜Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҘĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊа 🎉", "version_history": "Đ˜ŅŅ‚ĐžŅ€Đ¸Ņ˜Đ° вĐĩŅ€ĐˇĐ¸Ņ˜Đ°", - "version_history_item": "ИĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž {version} on {date}", + "version_history_item": "ИĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž {version} {date}", "video": "ВидĐĩĐž СаĐŋĐ¸Ņ", "video_hover_setting": "ĐŸŅƒŅŅ‚Đ¸ ҁĐģĐ¸Ņ‡Đ¸Ņ†Ņƒ видĐĩа Đēада ĐģĐĩйди", - "video_hover_setting_description": "ĐŸŅƒŅŅ‚Đ¸ ҁĐģĐ¸Ņ‡Đ¸Ņ†Ņƒ видĐĩа Đēада ĐŧĐ¸Ņˆ ĐŋŅ€ĐĩŅ’Đĩ ĐŋŅ€ĐĩĐēĐž ŅŅ‚Đ°Đ˛ĐēĐĩ. ЧаĐē и Đēада ҘĐĩ oneĐŧĐžĐŗŅƒŅ›ĐĩĐŊа, Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅ†Đ¸Ņ˜Đ° ҁĐĩ ĐŧĐžĐļĐĩ ĐŋĐžĐēŅ€ĐĩĐŊŅƒŅ‚Đ¸ ĐŋŅ€ĐĩĐģĐ°ŅĐēĐžĐŧ ĐŧĐ¸ŅˆĐ° ĐŋŅ€ĐĩĐēĐž иĐēone Са Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅ†Đ¸Ņ˜Ņƒ.", + "video_hover_setting_description": "ĐŸŅƒŅŅ‚Đ¸ ҁĐģĐ¸Ņ‡Đ¸Ņ†Ņƒ видĐĩа Đēада ĐŧĐ¸Ņˆ ĐŋŅ€ĐĩŅ’Đĩ ĐŋŅ€ĐĩĐēĐž ŅŅ‚Đ°Đ˛ĐēĐĩ. ЧаĐē и Đēада ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа, Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅ†Đ¸Ņ˜Đ° ҁĐĩ ĐŧĐžĐļĐĩ ĐŋĐžĐēŅ€ĐĩĐŊŅƒŅ‚Đ¸ ĐŋŅ€ĐĩĐģĐ°ŅĐēĐžĐŧ ĐŧĐ¸ŅˆĐ° ĐŋŅ€ĐĩĐēĐž иĐēone Са Ņ€ĐĩĐŋŅ€ĐžĐ´ŅƒĐēŅ†Đ¸Ņ˜Ņƒ.", "videos": "ВидĐĩĐž СаĐŋĐ¸ŅĐ¸", "videos_count": "{count, plural, one {# видĐĩĐž СаĐŋĐ¸Ņ} few {# видĐĩĐž СаĐŋĐ¸ŅĐ°} other {# видĐĩĐž СаĐŋĐ¸ŅĐ°}}", - "view": "ГĐģĐĩĐ´Đ°Ņ˜ (view)", + "view": "ГĐģĐĩĐ´Đ°Ņ˜ (виĐĩw)", "view_album": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜ аĐģĐąŅƒĐŧ", "view_all": "ĐŸŅ€Đ¸ĐēаĐļи ХвĐĩ", "view_all_users": "ĐŸŅ€Đ¸ĐēаĐļи ŅĐ˛Đĩ ĐēĐžŅ€Đ¸ŅĐŊиĐēĐĩ", @@ -1870,24 +1899,24 @@ "view_link": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜ вĐĩĐˇŅƒ", "view_links": "ĐŸŅ€Đ¸ĐēаĐļи вĐĩСĐĩ", "view_name": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ‚Đ¸", - "view_next_asset": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ҁĐģĐĩĐ´ĐĩŅ›Ņƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", + "view_next_asset": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ ҁĐģĐĩĐ´ĐĩŅ†ĖŅƒ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", "view_previous_asset": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜ ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊ҃ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃", - "view_qr_code": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ QR ĐēОд", + "view_qr_code": "ĐŸĐžĐŗĐģĐĩĐ´Đ°Ņ˜Ņ‚Đĩ QĐ  ĐēОд", "view_stack": "ĐŸŅ€Đ¸ĐēаĐļи ĐŗĐžĐŧиĐģ҃", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", + "viewer_remove_from_stack": "ĐŖĐēĐģĐžĐŊи иС ҁ҂ĐĩĐēа", + "viewer_stack_use_as_main_asset": "ĐšĐžŅ€Đ¸ŅŅ‚Đ¸ ĐēаО ĐŗĐģавĐŊи Ņ€ĐĩŅŅƒŅ€Ņ", + "viewer_unstack": "ĐŖĐŊ-ĐĄŅ‚Đ°Ņ†Đē", "visibility_changed": "Đ’Đ¸Đ´Ņ™Đ¸Đ˛ĐžŅŅ‚ ҘĐĩ ĐŋŅ€ĐžĐŧĐĩҚĐĩĐŊа Са {count, plural, one {# ĐžŅĐžĐąŅƒ} other {# ĐžŅĐžĐąĐĩ}}", "waiting": "ЧĐĩĐēаĐŧ", "warning": "ĐŖĐŋĐžĐˇĐžŅ€ĐĩҚĐĩ", "week": "НĐĩĐ´ĐĩŅ™Đ°", "welcome": "Đ”ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģи", - "welcome_to_immich": "Đ”ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģи ҃ ИĐŧĐ¸Ņ‡ (Immich)", - "wifi_name": "WiFi Name", + "welcome_to_immich": "Đ”ĐžĐąŅ€ĐžĐ´ĐžŅˆĐģи ҃ иĐŧĐŧĐ¸Ņ†Ņ…", + "wifi_name": "Назив Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", "year": "ГодиĐŊа", "years_ago": "ĐŋŅ€Đĩ {years, plural, one {# ĐŗĐžĐ´Đ¸ĐŊĐĩ} other {# ĐŗĐžĐ´Đ¸ĐŊа}}", "yes": "Да", "you_dont_have_any_shared_links": "НĐĩĐŧĐ°Ņ‚Đĩ ĐŊĐ¸Ņ˜ĐĩĐ´ĐŊĐž Đ´ĐĩŅ™ĐĩҚĐĩ вĐĩСĐĩ", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "ИĐŧĐĩ Đ˛Đ°ŅˆĐĩ Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", "zoom_image": "Đ—ŅƒĐŧĐ¸Ņ€Đ°Ņ˜ ҁĐģиĐē҃" } diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index c3280ee4fb..b63002ef79 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -1,10 +1,9 @@ { - "about": "O Aplikaciji", - "account": "Profil", + "about": "O aplikaciji", + "account": "Nalog", "account_settings": "PodeÅĄavanja za Profil", "acknowledge": "Potvrdi", "action": "Postupak", - "action_common_update": "Update", "actions": "Postupci", "active": "Aktivni", "activity": "Aktivnost", @@ -14,8 +13,8 @@ "add_a_location": "Dodaj Lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", - "add_endpoint": "Add endpoint", - "add_exclusion_pattern": "Dodaj obrazac izuzimanja", + "add_endpoint": "Dodajte krajnju tačku", + "add_exclusion_pattern": "Dodajte obrazac izuzimanja", "add_import_path": "Dodaj putanju za preuzimanje", "add_location": "Dodaj lokaciju", "add_more_users": "Dodaj korisnike", @@ -39,11 +38,11 @@ "authentication_settings_disable_all": "Da li ste sigurni da Åželite da onemogucˁite sve metode prijavljivanja? Prijava cˁe biti potpuno onemogucˁena.", "authentication_settings_reenable": "Da biste ponovo omogucˁili, koristite komandu servera.", "background_task_job": "Pozadinski zadaci", - "backup_database": "Rezervna kopija baze podataka", - "backup_database_enable_description": "Omogucˁite rezervne kopije baze podataka", - "backup_keep_last_amount": "Količina prethodnih rezervnih kopija za čuvanje", - "backup_settings": "PodeÅĄavanja rezervne kopije", - "backup_settings_description": "Upravljajte postavkama rezervne kopije baze podataka", + "backup_database": "Kreirajte rezervnu kopiju baze podataka", + "backup_database_enable_description": "Omogucˁi dampove baze podataka", + "backup_keep_last_amount": "Količina prethodnih dampova koje treba zadrÅžati", + "backup_settings": "PodeÅĄavanja dampa baze podataka", + "backup_settings_description": "Upravljajte podeÅĄavanjima dampa baze podataka. Napomena: Ovi poslovi se ne prate i necˁete biti obaveÅĄteni o neuspehu.", "check_all": "Proveri sve", "cleanup": "ČiÅĄcˁenje", "cleared_jobs": "OčiÅĄcˁeni poslovi za: {job}", @@ -53,6 +52,7 @@ "confirm_email_below": "Da biste potvrdili, unesite \"{email}\" ispod", "confirm_reprocess_all_faces": "Da li ste sigurni da Åželite da ponovo obradite sva lica? Ovo cˁe takođe obrisati imenovane osobe.", "confirm_user_password_reset": "Da li ste sigurni da Åželite da resetujete lozinku korisnika {user}?", + "confirm_user_pin_code_reset": "Da li ste sigurni da Åželite da resetujete PIN kod korisnika {user}?", "create_job": "Kreirajte posao", "cron_expression": "Cron izraz (expression)", "cron_expression_description": "Podesite interval skeniranja koristecˁi cron format. Za viÅĄe informacija pogledajte npr. Crontab Guru", @@ -106,7 +106,7 @@ "library_scanning_enable_description": "Omogucˁite periodično skeniranje biblioteke", "library_settings": "Spoljna biblioteka", "library_settings_description": "Upravljajte podeÅĄavanjima spoljne biblioteke", - "library_tasks_description": "Skenirajte spoljne biblioteke u potrazi za novim i/ili promenjenim sredstvima", + "library_tasks_description": "Obavljaj zadatke biblioteke", "library_watching_enable_description": "Pratite spoljne biblioteke za promene datoteka", "library_watching_settings": "Nadgledanje biblioteke (EKSPERIMENTALNO)", "library_watching_settings_description": "Automatski pratite promenjene datoteke", @@ -141,7 +141,7 @@ "machine_learning_smart_search_description": "PotraÅžite slike semantički koristecˁi ugrađeni CLIP", "machine_learning_smart_search_enabled": "Omogucˁite pametnu pretragu", "machine_learning_smart_search_enabled_description": "Ako je onemogucˁeno, slike necˁe biti kodirane za pametnu pretragu.", - "machine_learning_url_description": "URL servera za maÅĄinsko učenje. Ako je obezbeđeno viÅĄe URL-ova, svaki server će biti pokuÅĄan redom, jedan po jedan, dok jedan ne odgovori uspeÅĄno, po redosledu od prvog do poslednjeg. Serveri koji ne reaguju bicˁe privremeno zanemareni dok se ne vrate na mreÅžu.", + "machine_learning_url_description": "URL servera za maÅĄinsko učenje. Ako je navedeno viÅĄe URL adresa, svaki server cˁe biti pokuÅĄavan pojedinačno dok ne odgovori uspeÅĄno, redom od prvog do poslednjeg. Serveri koji ne odgovore bicˁe privremeno ignorisani dok se ponovo ne poveÅžu sa mreÅžom.", "manage_concurrency": "Upravljanje paralelnoÅĄcˁu", "manage_log_settings": "Upravljajte podeÅĄavanjima evidencije", "map_dark_style": "Tamni stil", @@ -192,26 +192,22 @@ "oauth_auto_register": "Automatska registracija", "oauth_auto_register_description": "Automatski registrujte nove korisnike nakon ÅĄto se prijavite pomocˁu OAuth-a", "oauth_button_text": "Tekst dugmeta", - "oauth_client_id": "ID klijenta", - "oauth_client_secret": "Tajna klijenta", + "oauth_client_secret_description": "Potrebno ako OAuth provajder ne podrÅžava PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Prijavite se pomocˁu OAuth-a", - "oauth_issuer_url": "URL izdavača", "oauth_mobile_redirect_uri": "URI za preusmeravanje mobilnih uređaja", "oauth_mobile_redirect_uri_override": "Zamena URI-ja mobilnog preusmeravanja", "oauth_mobile_redirect_uri_override_description": "Omogucˁi kada OAuth dobavljač (provider) ne dozvoljava mobilni URI, kao ÅĄto je '{callback}'", - "oauth_profile_signing_algorithm": "Algoritam za potpisivanje profila", - "oauth_profile_signing_algorithm_description": "Algoritam koji se koristi za potpisivanje korisničkog profila.", - "oauth_scope": "Obim", "oauth_settings": "OAutorizacija", "oauth_settings_description": "Upravljajte podeÅĄavanjima za prijavu sa OAutorizacijom", "oauth_settings_more_details": "Za viÅĄe detalja o ovoj funkciji pogledajte dokumente.", - "oauth_signing_algorithm": "Algoritam potpisivanja", "oauth_storage_label_claim": "Zahtev za nalepnicu za skladiÅĄtenje", "oauth_storage_label_claim_description": "Automatski podesite oznaku za skladiÅĄtenje korisnika na vrednost ovog zahteva.", "oauth_storage_quota_claim": "Zahtev za kvotu skladiÅĄtenja", "oauth_storage_quota_claim_description": "Automatski podesite kvotu memorijskog prostora korisnika na vrednost ovog zahteva.", "oauth_storage_quota_default": "Podrazumevana kvota za skladiÅĄtenje (GiB)", "oauth_storage_quota_default_description": "Kvota u GiB koja se koristi kada nema potraÅživanja (unesite 0 za neograničenu kvotu).", + "oauth_timeout": "Vremensko ograničenje zahteva", + "oauth_timeout_description": "Vremensko ograničenje za zahteve u milisekundama", "offline_paths": "VanmreÅžne putanje", "offline_paths_description": "Ovi rezultati mogu biti posledica ručnog brisanja datoteka koje nisu deo spoljne biblioteke.", "password_enable_description": "Prijavite se pomocˁu e-poÅĄte i lozinke", @@ -251,7 +247,7 @@ "storage_template_hash_verification_enabled_description": "Omogucˁava heÅĄ verifikaciju, ne onemogucˁavajte ovo osim ako niste sigurni u posledice", "storage_template_migration": "Migracija ÅĄablona za skladiÅĄtenje", "storage_template_migration_description": "Primenite trenutni {template} na prethodno otpremljene elemente", - "storage_template_migration_info": "Å ablon za skladiÅĄtenje cˁe pretvoriti sve ekstenzije u mala slova. Promene ÅĄablona cˁe se primeniti samo na nove datoteke. Da biste retroaktivno primenili ÅĄablon na prethodno otpremljene datoteke, pokrenite {job}.", + "storage_template_migration_info": "Promene ÅĄablona cˁe se primeniti samo na nove datoteke. Da biste retroaktivno primenili ÅĄablon na prethodno otpremljene datoteke, pokrenite {job}.", "storage_template_migration_job": "Posao migracije skladiÅĄta", "storage_template_more_details": "Za viÅĄe detalja o ovoj funkciji pogledajte Å ablon za skladiÅĄte i njegove implikacije", "storage_template_onboarding_description": "Kada je omogucˁena, ova funkcija cˁe automatski organizovati datoteke na osnovu ÅĄablona koji definiÅĄe korisnik. Zbog problema sa stabilnoÅĄcˁu ova funkcija je podrazumevano isključena. Za viÅĄe informacija pogledajte dokumentaciju.", @@ -310,7 +306,7 @@ "transcoding_max_b_frames": "Maksimalni B-kadri", "transcoding_max_b_frames_description": "ViÅĄe vrednosti poboljÅĄavaju efikasnost kompresije, ali usporavaju kodiranje. MoÅžda nije kompatibilno sa hardverskim ubrzanjem na starijim uređajima. 0 onemogucˁava B-kadre, dok -1 automatski postavlja ovu vrednost.", "transcoding_max_bitrate": "Maksimalni bitrate", - "transcoding_max_bitrate_description": "PodeÅĄavanje maksimalnog bitrate-a moÅže učiniti veličine datoteka predvidljivijim uz manju cenu kvaliteta. Pri 720p, tipične vrednosti su 2600 kbit/s za VP9 ili HEVC, ili 4500 kbit/s za H.264. Onemogucˁeno ako je postavljeno na 0.", + "transcoding_max_bitrate_description": "PodeÅĄavanje maksimalnog bitrate-a moÅže učiniti veličine datoteka predvidljivijim uz manju cenu kvaliteta. Pri 720p, tipične vrednosti su 2600k za VP9 ili HEVC, ili 4500k za H.264. Onemogucˁeno ako je postavljeno na 0.", "transcoding_max_keyframe_interval": "Maksimalni interval keyframe-a", "transcoding_max_keyframe_interval_description": "Postavlja maksimalnu udaljenost kadrova između ključnih kadrova. NiÅže vrednosti pogorÅĄavaju efikasnost kompresije, ali poboljÅĄavaju vreme traÅženja i mogu poboljÅĄati kvalitet scena sa brzim kretanjem. 0 automatski postavlja ovu vrednost.", "transcoding_optimal_description": "Video snimci vecˁi od ciljne rezolucije ili nisu u prihvacˁenom formatu", @@ -324,7 +320,7 @@ "transcoding_reference_frames_description": "Broj okvira (frames) za referencu prilikom kompresije datog okvira. ViÅĄe vrednosti poboljÅĄavaju efikasnost kompresije, ali usporavaju kodiranje. 0 automatski postavlja ovu vrednost.", "transcoding_required_description": "Samo video snimci koji nisu u prihvacˁenom formatu", "transcoding_settings": "PodeÅĄavanja video transkodiranja", - "transcoding_settings_description": "Upravljajte koje video snimke Åželite da transkodujete i kako ih obraditi", + "transcoding_settings_description": "Upravljajte rezolucijom i informacijama o kodiranju video datoteka", "transcoding_target_resolution": "Ciljana rezolucija", "transcoding_target_resolution_description": "Vecˁe rezolucije mogu da sačuvaju viÅĄe detalja, ali im je potrebno viÅĄe vremena za kodiranje, imaju vecˁe veličine datoteka i mogu da smanje brzinu aplikacije.", "transcoding_temporal_aq": "Vremenski (Temporal) AQ", @@ -352,6 +348,7 @@ "user_delete_delay_settings_description": "Broj dana nakon uklanjanja za trajno brisanje korisničkog naloga i datoteka. Posao brisanja korisnika se pokrecˁe u ponocˁ da bi se proverili korisnici koji su spremni za brisanje. Promene ove postavke cˁe biti procenjene pri sledecˁem izvrÅĄenju.", "user_delete_immediately": "Nalog i datoteke {user} cˁe biti stavljeni na čekanje za trajno brisanje odmah.", "user_delete_immediately_checkbox": "Stavite korisnika i datoteke u red za trenutno brisanje", + "user_details": "Detalji korisnika", "user_management": "Upravljanje korisnicima", "user_password_has_been_reset": "Lozinka korisnika je resetovana:", "user_password_reset_description": "Molimo da dostavite privremenu lozinku korisniku i obavestite ga da cˁe morati da promeni lozinku prilikom sledecˁeg prijavljivanja.", @@ -371,16 +368,20 @@ "admin_password": "Administratorska Lozinka", "administration": "Administracija", "advanced": "Napredno", - "advanced_settings_log_level_title": "Log level: {}", - "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.", - "advanced_settings_prefer_remote_title": "Prefer remote images", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", - "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", - "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", - "advanced_settings_tile_subtitle": "Advanced user's settings", - "advanced_settings_troubleshooting_subtitle": "Enable additional features for troubleshooting", - "advanced_settings_troubleshooting_title": "Troubleshooting", + "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tokom sinhronizacije na osnovu alternativnih kriterijuma. PokuÅĄajte ovo samo ako imate problema sa aplikacijom da otkrije sve albume.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite filter za sinhronizaciju albuma na alternativnom uređaju", + "advanced_settings_log_level_title": "Nivo evidencije (log): {level}", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji veoma sporo učitavaju sličice sa sredstava na uređaju. Aktivirajte ovo podeÅĄavanje da biste umesto toga učitali udaljene slike.", + "advanced_settings_prefer_remote_title": "Preferirajte udaljene slike", + "advanced_settings_proxy_headers_subtitle": "DefiniÅĄite proksi zaglavlja koje Immich treba da poÅĄalje sa svakim mreÅžnim zahtevom", + "advanced_settings_proxy_headers_title": "Proksi Headeri (headers)", + "advanced_settings_self_signed_ssl_subtitle": "Preskače verifikaciju SSL sertifikata za krajnju tačku servera. Obavezno za samopotpisane sertifikate.", + "advanced_settings_self_signed_ssl_title": "Dozvoli samopotpisane SSL sertifikate", + "advanced_settings_sync_remote_deletions_subtitle": "Automatski izbriÅĄite ili vratite sredstvo na ovom uređaju kada se ta radnja preduzme na vebu", + "advanced_settings_sync_remote_deletions_title": "Sinhronizujte udaljena brisanja [EKSPERIMENTALNO]", + "advanced_settings_tile_subtitle": "Napredna korisnička podeÅĄavanja", + "advanced_settings_troubleshooting_subtitle": "Omogucˁite dodatne funkcije za reÅĄavanje problema", + "advanced_settings_troubleshooting_title": "ReÅĄavanje problema", "age_months": "Starost{months, plural, one {# mesec} other {# meseci}}", "age_year_months": "Starost 1 godina, {months, plural, one {# mesec} other {# mesec(a/i)}}", "age_years": "{years, plural, other {Starost #}}", @@ -400,20 +401,20 @@ "album_remove_user_confirmation": "Da li ste sigurni da Åželite da uklonite {user}?", "album_share_no_users": "Izgleda da ste podelili ovaj album sa svim korisnicima ili da nemate nijednog korisnika sa kojim biste delili.", "album_thumbnail_card_item": "1 stavka", - "album_thumbnail_card_items": "{} stavki", - "album_thumbnail_card_shared": "Deljeno", - "album_thumbnail_shared_by": "Shared by {}", + "album_thumbnail_card_items": "{count} stavki", + "album_thumbnail_card_shared": " Deljeno", + "album_thumbnail_shared_by": "Deli {user}", "album_updated": "Album aÅžuriran", "album_updated_setting_description": "Primite obaveÅĄtenje e-poÅĄtom kada deljeni album ima nova svojstva", "album_user_left": "Napustio/la {album}", "album_user_removed": "Uklonjen {user}", - "album_viewer_appbar_delete_confirm": "Are you sure you want to delete this album from your account?", + "album_viewer_appbar_delete_confirm": "Da li ste sigurni da Åželite da izbriÅĄete ovaj album sa svog naloga?", "album_viewer_appbar_share_err_delete": "NeuspeÅĄno brisanje albuma", "album_viewer_appbar_share_err_leave": "NeuspeÅĄno izlaÅženje iz albuma", "album_viewer_appbar_share_err_remove": "Problemi sa brisanjem zapisa iz albuma", "album_viewer_appbar_share_err_title": "NeuspeÅĄno menjanje naziva albuma", "album_viewer_appbar_share_leave": "Izađi iz albuma", - "album_viewer_appbar_share_to": "Share To", + "album_viewer_appbar_share_to": "Podeli sa", "album_viewer_page_share_add_users": "Dodaj korisnike", "album_with_link_access": "Neka svako ko ima vezu vidi fotografije i ljude u ovom albumu.", "albums": "Albumi", @@ -432,111 +433,109 @@ "api_key_description": "Ova vrednost cˁe biti prikazana samo jednom. Obavezno kopirajte pre nego ÅĄto zatvorite prozor.", "api_key_empty": "Ime vaÅĄeg API ključa ne bi trebalo da bude prazno", "api_keys": "API ključevi (keys)", - "app_bar_signout_dialog_content": "Are you sure you want to sign out?", - "app_bar_signout_dialog_ok": "Yes", - "app_bar_signout_dialog_title": "Sign out", + "app_bar_signout_dialog_content": "Da li ste sigurni da Åželite da se odjavite?", + "app_bar_signout_dialog_ok": "Da", + "app_bar_signout_dialog_title": "Odjavite se", "app_settings": "PodeÅĄavanja aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", "archive_or_unarchive_photo": "Arhivirajte ili poniÅĄtite arhiviranje fotografije", - "archive_page_no_archived_assets": "No archived assets found", - "archive_page_title": "Archive ({})", + "archive_page_no_archived_assets": "Nisu pronađena arhivirana sredstva", + "archive_page_title": "Arhiva ({count})", "archive_size": "Veličina arhive", "archive_size_description": "Podesi veličinu arhive za preuzimanje (u GiB)", - "archived": "Archived", + "archived": "Arhivirano", "archived_count": "{count, plural, other {Arhivirano #}}", "are_these_the_same_person": "Da li su ovo ista osoba?", "are_you_sure_to_do_this": "Jeste li sigurni da Åželite ovo da uradite?", - "asset_action_delete_err_read_only": "Cannot delete read only asset(s), skipping", - "asset_action_share_err_offline": "Cannot fetch offline asset(s), skipping", + "asset_action_delete_err_read_only": "Ne mogu da obriÅĄem element(e) samo za čitanje, preskačem", + "asset_action_share_err_offline": "Nije mogucˁe preuzeti oflajn resurs(e), preskačem", "asset_added_to_album": "Dodato u album", "asset_adding_to_album": "Dodaje se u albumâ€Ļ", "asset_description_updated": "Opis datoteke je aÅžuriran", "asset_filename_is_offline": "Datoteka {filename} je van mreÅže (offline)", "asset_has_unassigned_faces": "Datoteka ima nedodeljena lica", "asset_hashing": "HeÅĄiranjeâ€Ļ", - "asset_list_group_by_sub_title": "Group by", + "asset_list_group_by_sub_title": "GrupiÅĄi po", "asset_list_layout_settings_dynamic_layout_title": "Dinamični raspored", - "asset_list_layout_settings_group_automatically": "Automatic", + "asset_list_layout_settings_group_automatically": "Automatski", "asset_list_layout_settings_group_by": "GrupiÅĄi zapise po", "asset_list_layout_settings_group_by_month_day": "Mesec + Dan", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Opcije za mreÅžni prikaz fotografija", "asset_list_settings_title": "MreÅžni prikaz fotografija", "asset_offline": "Datoteka odsutna", "asset_offline_description": "Ova vanjska datoteka se viÅĄe ne nalazi na disku. Molimo kontaktirajte svog Immich administratora za pomocˁ.", - "asset_restored_successfully": "Asset restored successfully", + "asset_restored_successfully": "Imovina je uspeÅĄno vracˁena", "asset_skipped": "Preskočeno", "asset_skipped_in_trash": "U otpad", "asset_uploaded": "Otpremljeno (Uploaded)", "asset_uploading": "Otpremanjeâ€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "Asset Viewer", + "asset_viewer_settings_subtitle": "Upravljajte podeÅĄavanjima pregledača galerije", + "asset_viewer_settings_title": "Pregledač imovine", "assets": "Zapisi", "assets_added_count": "Dodato {count, plural, one {# datoteka} other {# datoteka}}", "assets_added_to_album_count": "Dodato je {count, plural, one {# datoteka} other {# datoteka}} u album", "assets_added_to_name_count": "Dodato {count, plural, one {# datoteka} other {# datoteke}} u {hasName, select, true {{name}} other {novi album}}", "assets_count": "{count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", + "assets_deleted_permanently": "{count} elemenata trajno obrisano", + "assets_deleted_permanently_from_server": "{count} resurs(a) trajno obrisan(a) sa Immich servera", "assets_moved_to_trash_count": "PremeÅĄteno {count, plural, one {# datoteka} few {# datoteke} other {# datoteka}} u otpad", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", "assets_removed_count": "Uklonjeno {count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", + "assets_removed_permanently_from_device": "{count} elemenata trajno uklonjeno sa vaÅĄeg uređaja", "assets_restore_confirmation": "Da li ste sigurni da Åželite da vratite sve svoje datoteke koje su u otpadu? Ne moÅžete poniÅĄtiti ovu radnju! Imajte na umu da se vanmreÅžna sredstva ne mogu vratiti na ovaj način.", "assets_restored_count": "Vracˁeno {count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", + "assets_restored_successfully": "{count} elemenata uspeÅĄno vracˁeno", + "assets_trashed": "{count} elemenata je prebačeno u otpad", "assets_trashed_count": "Bačeno u otpad {count, plural, one {# datoteka} few{# datoteke} other {# datoteka}}", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", + "assets_trashed_from_server": "{count} resurs(a) obrisanih sa Immich servera", "assets_were_part_of_album_count": "{count, plural, one {Datoteka je} other {Datoteke su}} vecˁ deo albuma", "authorized_devices": "OvlaÅĄcˁeni uređaji", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "PoveÅžite se lokalno preko određenog Wi-Fi-ja kada je dostupan i koristite alternativne veze na drugim mestima", + "automatic_endpoint_switching_title": "Automatska promena URL-ova", "back": "Nazad", "back_close_deselect": "Nazad, zatvorite ili opozovite izbor", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "Albuma na uređaju ({})", + "background_location_permission": "Dozvola za lokaciju u pozadini", + "background_location_permission_content": "Da bi se menjale mreÅže dok se radi u pozadini, Imih mora *uvek* imati precizan pristup lokaciji kako bi aplikacija mogla da pročita ime Wi-Fi mreÅže", + "backup_album_selection_page_albums_device": "Albuma na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirni da uključiÅĄ, dodirni dvaput da isključiÅĄ", "backup_album_selection_page_assets_scatter": "Zapisi se mogu naći u viÅĄe različitih albuma. Odatle albumi se mogu uključiti ili isključiti tokom procesa pravljenja pozadinskih kopija.", "backup_album_selection_page_select_albums": "Odaberi albume", "backup_album_selection_page_selection_info": "Informacije o selekciji", "backup_album_selection_page_total_assets": "Ukupno jedinstvenih ***", "backup_all": "Sve", - "backup_background_service_backup_failed_message": "NeuspeÅĄno pravljenje rezervne kopije. PokuÅĄavam ponovo...", - "backup_background_service_connection_failed_message": "NeuspeÅĄno povezivanje sa serverom. PokuÅĄavam ponovo...", - "backup_background_service_current_upload_notification": "Otpremanje {}", - "backup_background_service_default_notification": "Proveravanje novih zapisa", + "backup_background_service_backup_failed_message": "Pravljenje rezervne kopije elemenata nije uspelo. PokuÅĄava se ponovoâ€Ļ", + "backup_background_service_connection_failed_message": "Povezivanje sa serverom nije uspelo. PokuÅĄavam ponovoâ€Ļ", + "backup_background_service_current_upload_notification": "Otpremanje {filename}", + "backup_background_service_default_notification": "Proveravanje novih zapisaâ€Ļ", "backup_background_service_error_title": "GreÅĄka u pravljenju rezervnih kopija", - "backup_background_service_in_progress_notification": "Pravljenje rezervnih kopija zapisa", - "backup_background_service_upload_failure_notification": "NeuspeÅĄno otpremljeno: {}", + "backup_background_service_in_progress_notification": "Pravljenje rezervnih kopija zapisaâ€Ļ", + "backup_background_service_upload_failure_notification": "NeuspeÅĄno otpremljeno: {filename}", "backup_controller_page_albums": "Napravi rezervnu kopiju albuma", - "backup_controller_page_background_app_refresh_disabled_content": "Aktiviraj pozadinsko osveÅžavanje u Opcije Generalne Pozadinsko OsveÅžavanje kako bi napravili rezervne kopije u pozadini", + "backup_controller_page_background_app_refresh_disabled_content": "Aktiviraj pozadinsko osveÅžavanje u Opcije > Generalne > Pozadinsko OsveÅžavanje kako bi napravili rezervne kopije u pozadini.", "backup_controller_page_background_app_refresh_disabled_title": "Pozadinsko osveÅžavanje isključeno", "backup_controller_page_background_app_refresh_enable_button_text": "Idi u podeÅĄavanja", "backup_controller_page_background_battery_info_link": "PokaÅži mi kako", "backup_controller_page_background_battery_info_message": "Za najpouzdanije pravljenje rezervnih kopija, ugasite bilo koju opciju u optimizacijama koje bi sprečavale Immich sa pravilnim radom.\n\nOvaj postupak varira od uređaja do uređaja, proverite potrebne korake za VaÅĄ uređaj.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizacija Baterije", "backup_controller_page_background_charging": "Samo tokom punjenja", "backup_controller_page_background_configure_error": "NeuspeÅĄno konfigurisanje pozadinskog servisa", - "backup_controller_page_background_delay": "Vreme između pravljejna rezervnih kopija zapisa: {}", + "backup_controller_page_background_delay": "Vreme između pravljejna rezervnih kopija zapisa: {duration}", "backup_controller_page_background_description": "Uključi pozadinski servis da automatski praviÅĄ rezervne kopije, bez da otvaraÅĄ aplikaciju", "backup_controller_page_background_is_off": "Automatsko pravljenje rezervnih kopija u pozadini je isključeno", "backup_controller_page_background_is_on": "Automatsko pravljenje rezervnih kopija u pozadini je uključeno", "backup_controller_page_background_turn_off": "Isključi pozadinski servis", "backup_controller_page_background_turn_on": "Uključi pozadinski servis", - "backup_controller_page_background_wifi": "Samo na WiFi", + "backup_controller_page_background_wifi": "Samo na Wi-Fi", "backup_controller_page_backup": "Napravi rezervnu kopiju", - "backup_controller_page_backup_selected": "Odabrano:", + "backup_controller_page_backup_selected": "Odabrano: ", "backup_controller_page_backup_sub": "ZavrÅĄeno pravljenje rezervne kopije fotografija i videa", - "backup_controller_page_created": "Napravljeno:{}", + "backup_controller_page_created": "Napravljeno:{date}", "backup_controller_page_desc_backup": "Uključi pravljenje rezervnih kopija u prvom planu da automatski napravite rezervne kopije kada otvorite aplikaciju.", - "backup_controller_page_excluded": "Isključeno:", - "backup_controller_page_failed": "NeuspeÅĄno ({})", - "backup_controller_page_filename": "Ime fajla:{} [{}]", - "backup_controller_page_id": "ID:{}", + "backup_controller_page_excluded": "Isključeno: ", + "backup_controller_page_failed": "NeuspeÅĄno ({count})", + "backup_controller_page_filename": "Ime fajla: {filename} [{size}]", + "backup_controller_page_id": "ID:{id}", "backup_controller_page_info": "Informacije", "backup_controller_page_none_selected": "NiÅĄta odabrano", "backup_controller_page_remainder": "Podsetnik", @@ -545,7 +544,7 @@ "backup_controller_page_start_backup": "Pokreni pravljenje rezervne kopije", "backup_controller_page_status_off": "Automatsko pravljenje rezervnih kopija u prvom planu je isključeno", "backup_controller_page_status_on": "Automatsko pravljenje rezervnih kopija u prvom planu je uključeno", - "backup_controller_page_storage_format": "{} od {} iskoriÅĄÄ‡eno", + "backup_controller_page_storage_format": "{used} od {total} iskoriÅĄÄ‡eno", "backup_controller_page_to_backup": "Albumi koji će se otpremiti", "backup_controller_page_total_sub": "Sve jedinstvene fotografije i videi iz odabranih albuma", "backup_controller_page_turn_off": "Isključi pravljenje rezervnih kopija u prvom planu", @@ -553,12 +552,10 @@ "backup_controller_page_uploading_file_info": "Otpremanje svojstava datoteke", "backup_err_only_album": "Nemoguće brisanje jedinog albuma", "backup_info_card_assets": "zapisi", - "backup_manual_cancelled": "Cancelled", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", - "backup_manual_success": "Success", - "backup_manual_title": "Upload status", - "backup_options_page_title": "Backup options", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_manual_cancelled": "Otkazano", + "backup_manual_in_progress": "Otpremanje je vecˁ u toku. PokuÅĄajte kasnije", + "backup_manual_success": "Uspeh", + "backup_setting_subtitle": "Upravljajte podeÅĄavanjima otpremanja u pozadini i prednjem planu", "backward": "Unazad", "birthdate_saved": "Datum rođenja uspeÅĄno sačuvan", "birthdate_set_description": "Datum rođenja se koristi da bi se izračunale godine ove osobe u dobu određene fotografije.", @@ -570,35 +567,34 @@ "bulk_keep_duplicates_confirmation": "Da li ste sigurni da Åželite da zadrÅžite {count, plural, one {1 dupliranu datoteku} few {# duplirane datoteke} other {# dupliranih datoteka}}? Ovo cˁe reÅĄiti sve duplirane grupe bez brisanja bilo čega.", "bulk_trash_duplicates_confirmation": "Da li ste sigurni da Åželite grupno da odbacite {count, plural, one {1 dupliranu datoteku} few {# duplirane datoteke} other {# dupliranih datoteka}}? Ovo cˁe zadrÅžati najvecˁu datoteku svake grupe i odbaciti sve ostale duplikate.", "buy": "Kupite licencu Immich-a", - "cache_settings_album_thumbnails": "Sličice na stranici biblioteke", + "cache_settings_album_thumbnails": "Sličice na stranici biblioteke ({count} assets)", "cache_settings_clear_cache_button": "ObriÅĄi keÅĄ memoriju", "cache_settings_clear_cache_button_title": "Ova opcija briÅĄe keÅĄ memoriju aplikacije. Ovo će bitno uticati na performanse aplikacije dok se keÅĄ memorija ne učita ponovo.", - "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", - "cache_settings_duplicated_assets_title": "Duplicated Assets ({})", - "cache_settings_image_cache_size": "Veličina keÅĄ memorije slika ({} stavki)", + "cache_settings_duplicated_assets_subtitle": "Fotografije i video snimci koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_title": "Duplirani elementi ({count})", + "cache_settings_image_cache_size": "Veličina keÅĄ memorije slika ({count} assets)", "cache_settings_statistics_album": "Minijature biblioteka", - "cache_settings_statistics_assets": "{} stavki ({})", + "cache_settings_statistics_assets": "{count} stavki ({size})", "cache_settings_statistics_full": "Pune slike", "cache_settings_statistics_shared": "Minijature deljenih albuma", "cache_settings_statistics_thumbnail": "Minijature", "cache_settings_statistics_title": "IskoriÅĄÄ‡ena keÅĄ memorija", "cache_settings_subtitle": "Kontrole za keÅĄ memoriju mobilne aplikacije Immich", - "cache_settings_thumbnail_size": "KeÅĄ memorija koju zauzimaju minijature ({} stavki)", - "cache_settings_tile_subtitle": "Control the local storage behaviour", - "cache_settings_tile_title": "Local Storage", + "cache_settings_thumbnail_size": "KeÅĄ memorija koju zauzimaju minijature ({count} stavki)", + "cache_settings_tile_subtitle": "KontroliÅĄite ponaÅĄanje lokalnog skladiÅĄtenja", + "cache_settings_tile_title": "Lokalna memorija", "cache_settings_title": "Opcije za keÅĄiranje", "camera": "Kamera", "camera_brand": "Brend kamere", "camera_model": "Model kamere", "cancel": "Odustani", "cancel_search": "OtkaÅži pretragu", - "canceled": "Canceled", + "canceled": "Otkazano", "cannot_merge_people": "Ne moÅže spojiti osobe", "cannot_undo_this_action": "Ne moÅžete poniÅĄtiti ovu radnju!", "cannot_update_the_description": "Ne moÅže aÅžurirati opis", "change_date": "Promeni datum", - "change_display_order": "Change display order", + "change_display_order": "Promeni redosled prikaza", "change_expiration_time": "Promeni vreme isteka", "change_location": "Promeni mesto", "change_name": "Promeni ime", @@ -606,16 +602,17 @@ "change_password": "Promeni Lozinku", "change_password_description": "Ovo je ili prvi put da se prijavljujete na sistem ili je podnet zahtev za promenu lozinke. Unesite novu lozinku ispod.", "change_password_form_confirm_password": "Ponovo unesite ÅĄifru", - "change_password_form_description": "Ćao, {name}\n\nOvo je verovatno VaÅĄe prvo pristupanje sistemu, ili je podneÅĄen zahtev za promenu ÅĄifre. Molimo Vas, unesite novu ÅĄifru ispod", + "change_password_form_description": "Ćao, {name}\n\nOvo je verovatno VaÅĄe prvo pristupanje sistemu, ili je podneÅĄen zahtev za promenu ÅĄifre. Molimo Vas, unesite novu ÅĄifru ispod.", "change_password_form_new_password": "Nova ÅĄifra", "change_password_form_password_mismatch": "Å ifre se ne podudaraju", "change_password_form_reenter_new_password": "Ponovo unesite novu ÅĄifru", + "change_pin_code": "Promena PIN koda", "change_your_password": "Promeni svoju ÅĄifru", "changed_visibility_successfully": "Vidljivost je uspeÅĄno promenjena", "check_all": "Å tiklirati sve", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "Proverite da li postoje oÅĄtecˁene rezervne kopije imovine", + "check_corrupt_asset_backup_button": "IzvrÅĄite proveru", + "check_corrupt_asset_backup_description": "Pokrenite ovu proveru samo preko Wi-Fi mreÅže i nakon ÅĄto se napravi rezervna kopija svih podataka. Postupak moÅže potrajati nekoliko minuta.", "check_logs": "Proverite dnevnike (logs)", "choose_matching_people_to_merge": "Izaberite odgovarajucˁe osobe za spajanje", "city": "Grad", @@ -624,14 +621,11 @@ "clear_all_recent_searches": "ObriÅĄite sve nedavne pretrage", "clear_message": "ObriÅĄi poruku", "clear_value": "Jasna vrednost", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", + "client_cert_import_success_msg": "Sertifikat klijenta je uvezen", + "client_cert_invalid_msg": "NevaÅžecˁa datoteka sertifikata ili pogreÅĄna lozinka", + "client_cert_remove_msg": "Sertifikat klijenta je uklonjen", + "client_cert_subtitle": "PodrÅžava samo PKCS12 (.p12, .pfx) format. Uvoz/uklanjanje sertifikata je dostupno samo pre prijave", + "client_cert_title": "SSL klijentski sertifikat", "clockwise": "U smeru kazaljke", "close": "Zatvori", "collapse": "Skupi", @@ -643,26 +637,27 @@ "comments_and_likes": "Komentari i lajkovi", "comments_are_disabled": "Komentari su onemogucˁeni", "common_create_new_album": "Kreiraj novi album", - "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", - "completed": "Completed", + "common_server_error": "Molimo vas da proverite mreÅžnu vezu, uverite se da je server dostupan i da su verzije aplikacija/servera kompatibilne.", + "completed": "ZavrÅĄeno", "confirm": "Potvrdi", "confirm_admin_password": "Potvrdi Administrativnu Lozinku", "confirm_delete_face": "Da li ste sigurni da Åželite da izbriÅĄete osobu {name} iz dela?", "confirm_delete_shared_link": "Da li ste sigurni da Åželite da izbriÅĄete ovaj deljeni link?", "confirm_keep_this_delete_others": "Sve ostale datoteke u grupi cˁe biti izbrisane osim ove datoteke. Da li ste sigurni da Åželite da nastavite?", + "confirm_new_pin_code": "Potvrdite novi PIN kod", "confirm_password": "Ponovo unesi ÅĄifru", "contain": "Obuhvati", "context": "Kontekst", "continue": "Nastavi", - "control_bottom_app_bar_album_info_shared": "{} stvari podeljeno", + "control_bottom_app_bar_album_info_shared": "{count} stvari podeljeno", "control_bottom_app_bar_create_new_album": "Kreiraj novi album", - "control_bottom_app_bar_delete_from_immich": "Delete from Immich", - "control_bottom_app_bar_delete_from_local": "Delete from device", - "control_bottom_app_bar_edit_location": "Edit Location", - "control_bottom_app_bar_edit_time": "Edit Date & Time", - "control_bottom_app_bar_share_link": "Share Link", - "control_bottom_app_bar_share_to": "Share To", - "control_bottom_app_bar_trash_from_immich": "Move to Trash", + "control_bottom_app_bar_delete_from_immich": "ObriÅĄi iz Immich-a", + "control_bottom_app_bar_delete_from_local": "ObriÅĄi sa uređaja", + "control_bottom_app_bar_edit_location": "Izmeni lokaciju", + "control_bottom_app_bar_edit_time": "Izmeni datum i vreme", + "control_bottom_app_bar_share_link": "Deli link", + "control_bottom_app_bar_share_to": "Podeli sa", + "control_bottom_app_bar_trash_from_immich": "Premesti u otpad", "copied_image_to_clipboard": "Kopirana slika u međuspremnik (clipboard).", "copied_to_clipboard": "Kopirano u međuspremnik (clipboard)!", "copy_error": "GreÅĄka pri kopiranju", @@ -682,29 +677,30 @@ "create_link": "Napravi vezu", "create_link_to_share": "Napravi vezu za deljenje", "create_link_to_share_description": "Neka svako sa vezom vidi izabrane fotografije", - "create_new": "CREATE NEW", "create_new_person": "Napravi novu osobu", "create_new_person_hint": "Dodelite izabrane datoteke novoj osobi", "create_new_user": "Napravi novog korisnika", - "create_shared_album_page_share_add_assets": "DODAJ ", + "create_shared_album_page_share_add_assets": "DODAJ SREDSTVA", "create_shared_album_page_share_select_photos": "Odaberi fotografije", "create_tag": "Kreirajte oznaku (tag)", "create_tag_description": "Napravite novu oznaku (tag). Za ugneŞđene oznake, unesite punu putanju oznake uključujucˁi kose crte.", "create_user": "Napravi korisnika", "created": "Napravljen", - "crop": "Crop", - "curated_object_page_title": "Things", + "created_at": "Kreirano", + "crop": "Obrezivanje", + "curated_object_page_title": "Stvari", "current_device": "Trenutni uređaj", - "current_server_address": "Current server address", + "current_pin_code": "Trenutni PIN kod", + "current_server_address": "Trenutna adresa servera", "custom_locale": "Prilagođena lokacija (locale)", "custom_locale_description": "Formatirajte datume i brojeve na osnovu jezika i regiona", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", + "daily_title_text_date": "E dd MMM", + "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Tamno", "date_after": "Datum posle", "date_and_time": "Datum i Vreme", "date_before": "Datum pre", - "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_format": "E d LLL y â€ĸ H:mm", "date_of_birth_saved": "Datum rođenja uspeÅĄno sačuvan", "date_range": "Raspon datuma", "day": "Dan", @@ -719,29 +715,28 @@ "delete_album": "ObriÅĄi album", "delete_api_key_prompt": "Da li ste sigurni da Åželite da izbriÅĄete ovaj API ključ (key)?", "delete_dialog_alert": "Ove stvari će permanentno biti obrisane sa Immich-a i VaÅĄeg uređaja", - "delete_dialog_alert_local": "These items will be permanently removed from your device but still be available on the Immich server", - "delete_dialog_alert_local_non_backed_up": "Some of the items aren't backed up to Immich and will be permanently removed from your device", - "delete_dialog_alert_remote": "These items will be permanently deleted from the Immich server", - "delete_dialog_ok_force": "Delete Anyway", + "delete_dialog_alert_local": "Ove stavke cˁe biti trajno uklonjene sa vaÅĄeg uređaja, ali cˁe i dalje biti dostupne na Immich serveru", + "delete_dialog_alert_local_non_backed_up": "Neke stavke nisu rezervno kopirane na Immich-u i bicˁe trajno uklonjene sa vaÅĄeg uređaja", + "delete_dialog_alert_remote": "Ove stavke cˁe biti trajno izbrisane sa Immich servera", + "delete_dialog_ok_force": "Ipak obriÅĄi", "delete_dialog_title": "ObriÅĄi permanentno", "delete_duplicates_confirmation": "Da li ste sigurni da Åželite da trajno izbriÅĄete ove duplikate?", "delete_face": "IzbriÅĄi osobu", "delete_key": "IzbriÅĄi ključ", "delete_library": "ObriÅĄi biblioteku", "delete_link": "ObriÅĄi vezu", - "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", - "delete_local_dialog_ok_force": "Delete Anyway", + "delete_local_dialog_ok_backed_up_only": "ObriÅĄi samo rezervne kopije", + "delete_local_dialog_ok_force": "Ipak obriÅĄi", "delete_others": "IzbriÅĄite druge", "delete_shared_link": "ObriÅĄi deljenu vezu", - "delete_shared_link_dialog_title": "Delete Shared Link", + "delete_shared_link_dialog_title": "ObriÅĄi deljeni link", "delete_tag": "ObriÅĄi oznaku (tag)", "delete_tag_confirmation_prompt": "Da li stvarno Åželite da izbriÅĄete oznaku {tagName}?", "delete_user": "ObriÅĄi korisnika", "deleted_shared_link": "ObriÅĄena deljena veza", "deletes_missing_assets": "BriÅĄe sredstva koja nedostaju sa diska", "description": "Opis", - "description_input_hint_text": "Add description...", - "description_input_submit_error": "Error updating description, check the log for more details", + "description_input_submit_error": "GreÅĄka pri aÅžuriranju opisa, proverite dnevnik za viÅĄe detalja", "details": "Detalji", "direction": "Smer", "disabled": "Onemogucˁeno", @@ -758,26 +753,25 @@ "documentation": "Dokumentacija", "done": "Urađeno", "download": "Preuzmi", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", + "download_canceled": "Preuzmi otkazano", + "download_complete": "Preuzmi zavrÅĄeno", + "download_enqueue": "Preuzimanje je stavljeno u red", + "download_failed": "Preuzimanje nije uspelo", + "download_filename": "datoteka: {filename}", + "download_finished": "Preuzimanje zavrÅĄeno", "download_include_embedded_motion_videos": "Ugrađeni video snimci", "download_include_embedded_motion_videos_description": "Uključite video zapise ugrađene u fotografije u pokretu kao zasebnu datoteku", - "download_notfound": "Download not found", - "download_paused": "Download paused", + "download_notfound": "Preuzimanje nije pronađeno", + "download_paused": "Preuzimanje je pauzirano", "download_settings": "Preuzimanje", "download_settings_description": "Upravljajte podeÅĄavanjima vezanim za preuzimanje datoteka", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", + "download_started": "Preuzimanje je započeto", + "download_sucess": "Preuzimanje je uspeÅĄno", + "download_sucess_android": "Mediji su preuzeti na DCIM/Immich", + "download_waiting_to_retry": "Čekanje na ponovni pokuÅĄaj", "downloading": "Preuzimanje u toku", "downloading_asset_filename": "Preuzimanje datoteke {filename}", - "downloading_media": "Downloading media", + "downloading_media": "Preuzimanje medija", "drop_files_to_upload": "Ubacite datoteke bilo gde da ih otpremite (upload-ujete)", "duplicates": "Duplikati", "duplicates_description": "RazreÅĄite svaku grupu tako ÅĄto cˁete navesti duplikate, ako ih ima", @@ -794,7 +788,7 @@ "edit_key": "Izmeni ključ", "edit_link": "Uredi vezu", "edit_location": "Uredi lokaciju", - "edit_location_dialog_title": "Location", + "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi osobe", "edit_tag": "Uredi oznaku (tag)", @@ -807,19 +801,20 @@ "editor_crop_tool_h2_aspect_ratios": "Proporcije (aspect ratios)", "editor_crop_tool_h2_rotation": "Rotacija", "email": "E-poÅĄta", - "empty_folder": "This folder is empty", + "email_notifications": "ObaveÅĄtenja e-poÅĄtom", + "empty_folder": "Ova mapa je prazna", "empty_trash": "Ispraznite smecˁe", "empty_trash_confirmation": "Da li ste sigurni da Åželite da ispraznite smecˁe? Ovo cˁe trajno ukloniti sve datoteke u smecˁu iz Immich-a.\nNe moÅžete poniÅĄtiti ovu radnju!", "enable": "Omogucˁi (Enable)", "enabled": "Omogucˁeno (Enabled)", "end_date": "Krajnji datum", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Stavljeno u red", + "enter_wifi_name": "Unesite naziv Wi-Fi mreÅže", "error": "GreÅĄka", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "Promena redosleda sortiranja albuma nije uspela", "error_delete_face": "GreÅĄka pri brisanju osobe iz dela", "error_loading_image": "GreÅĄka pri učitavanju slike", - "error_saving_image": "Error: {}", + "error_saving_image": "GreÅĄka: {error}", "error_title": "GreÅĄka – NeÅĄto je poÅĄlo naopako", "errors": { "cannot_navigate_next_asset": "Nije mogucˁe docˁi do sledecˁe datoteke", @@ -849,10 +844,12 @@ "failed_to_keep_this_delete_others": "Nije uspelo zadrÅžavanje ovog dela i brisanje ostalih datoteka", "failed_to_load_asset": "Učitavanje datoteka nije uspelo", "failed_to_load_assets": "Nije uspelo učitavanje datoteka", + "failed_to_load_notifications": "Učitavanje obaveÅĄtenja nije uspelo", "failed_to_load_people": "Učitavanje osoba nije uspelo", "failed_to_remove_product_key": "Uklanjanje ključa proizvoda nije uspelo", "failed_to_stack_assets": "Slaganje datoteka nije uspelo", "failed_to_unstack_assets": "Rasklapanje datoteka nije uspelo", + "failed_to_update_notification_status": "AÅžuriranje statusa obaveÅĄtenja nije uspelo", "import_path_already_exists": "Ova putanja uvoza vecˁ postoji.", "incorrect_email_or_password": "Neispravan e-mail ili lozinka", "paths_validation_failed": "{paths, plural, one {# putanja nije proÅĄla} few {# putanje nisu proÅĄle} other {# putanja nisu proÅĄle}} proveru valjanosti", @@ -920,6 +917,7 @@ "unable_to_remove_reaction": "Nije moguće ukloniti reakciju", "unable_to_repair_items": "Nije moguće popraviti stavke", "unable_to_reset_password": "Nije moguće resetovati lozinku", + "unable_to_reset_pin_code": "Nije mogucˁe resetovati PIN kod", "unable_to_resolve_duplicate": "Nije moguće razreÅĄiti duplikat", "unable_to_restore_assets": "Nije mogucˁe vratiti datoteke", "unable_to_restore_trash": "Nije moguće povratiti otpad", @@ -951,12 +949,10 @@ "exif_bottom_sheet_description": "Dodaj opis...", "exif_bottom_sheet_details": "DETALJI", "exif_bottom_sheet_location": "LOKACIJA", - "exif_bottom_sheet_people": "PEOPLE", - "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "Starost {age}", + "exif_bottom_sheet_person_age_months": "Starost {months} meseci", + "exif_bottom_sheet_person_age_year_months": "Starost 1 godina, {months} meseci", + "exif_bottom_sheet_person_age_years": "Starost {years}", "exit_slideshow": "Izađi iz projekcije slajdova", "expand_all": "ProÅĄiri sve", "experimental_settings_new_asset_list_subtitle": "U izradi", @@ -973,16 +969,16 @@ "extension": "Ekstenzija (Extension)", "external": "SpoljaÅĄnji", "external_libraries": "SpoljaÅĄnje Biblioteke", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Spoljna mreÅža", + "external_network_sheet_info": "Kada nije na Åželjenoj Wi-Fi mreÅži, aplikacija cˁe se povezati sa serverom preko prve od dole navedenih URL adresa do kojih moÅže da dođe, počevÅĄi od vrha do dna", "face_unassigned": "Neraspoređeni", - "failed": "Failed", + "failed": "NeuspeÅĄno", "failed_to_load_assets": "Datoteke nisu uspeÅĄno učitane", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "Učitavanje fascikle nije uspelo", "favorite": "Favorit", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Favoriti", - "favorites_page_no_favorites": "No favorite assets found", + "favorites_page_no_favorites": "Nije pronađen nijedan omiljeni materijal", "feature_photo_updated": "Glavna fotografija je aÅžurirana", "features": "Funkcije (features)", "features_setting_description": "Upravljajte funkcijama aplikacije", @@ -990,38 +986,38 @@ "file_name_or_extension": "Ime datoteke ili ekstenzija", "filename": "Ime datoteke", "filetype": "Vrsta dokumenta", - "filter": "Filter", "filter_people": "Filtriranje osoba", + "filter_places": "Filtrirajte mesta", "find_them_fast": "Brzo ih pronađite po imenu pomocˁu pretrage", "fix_incorrect_match": "Ispravite netačno podudaranje", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "Fascikla", + "folder_not_found": "Fascikla nije pronađena", "folders": "Fascikle (Folders)", "folders_feature_description": "Pregledavanje prikaza fascikle za fotografije i video zapisa u sistemu datoteka", "forward": "Napred", "general": "Generalno", "get_help": "Nađi pomocˁ", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "Nije mogucˁe dobiti ime Wi-Fi mreÅže. Uverite se da ste dali potrebne dozvole i da ste povezani na Wi-Fi mreÅžu", "getting_started": "Počinjem", "go_back": "Vrati se", "go_to_folder": "Idi u fasciklu", "go_to_search": "Idi na pretragu", - "grant_permission": "Grant permission", + "grant_permission": "Daj dozvolu", "group_albums_by": "Grupni albumi po...", "group_country": "Grupa po drÅžava", "group_no": "Bez grupisanja", "group_owner": "Grupirajte po vlasniku", "group_places_by": "Grupirajte mesta po...", "group_year": "Grupirajte po godini", - "haptic_feedback_switch": "Enable haptic feedback", - "haptic_feedback_title": "Haptic Feedback", + "haptic_feedback_switch": "Omogucˁi haptičku povratnu informaciju", + "haptic_feedback_title": "Haptičke povratne informacije", "has_quota": "Ima kvotu", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", + "header_settings_add_header_tip": "Dodaj zaglavlje", + "header_settings_field_validator_msg": "Vrednost ne moÅže biti prazna", + "header_settings_header_name_input": "Naziv zaglavlja", + "header_settings_header_value_input": "Vrednost zaglavlja", + "headers_settings_tile_subtitle": "DefiniÅĄite proksi zaglavlja koja aplikacija treba da ÅĄalje sa svakim mreÅžnim zahtevom", + "headers_settings_tile_title": "Prilagođeni proksi zaglavci", "hi_user": "Zdravo {name} ({email})", "hide_all_people": "Sakrij sve osobe", "hide_gallery": "Sakrij galeriju", @@ -1029,24 +1025,24 @@ "hide_password": "Sakrij lozinku", "hide_person": "Sakrij osobu", "hide_unnamed_people": "Sakrij neimenovane osobe", - "home_page_add_to_album_conflicts": "Dodat {added} zapis u album {album}. {failed} zapisi su već u albumu ", + "home_page_add_to_album_conflicts": "Dodat {added} zapis u album {album}. {failed} zapisi su već u albumu.", "home_page_add_to_album_err_local": "Trenutno nemoguće dodati lokalne zapise u albume, preskacu se", "home_page_add_to_album_success": "Dodate {added} stavke u album {album}.", - "home_page_album_err_partner": "Can not add partner assets to an album yet, skipping", - "home_page_archive_err_local": "Can not archive local assets yet, skipping", - "home_page_archive_err_partner": "Can not archive partner assets, skipping", + "home_page_album_err_partner": "JoÅĄ uvek nije mogucˁe dodati partnerska sredstva u album, preskačem", + "home_page_archive_err_local": "JoÅĄ uvek nije mogucˁe arhivirati lokalne resurse, preskačem", + "home_page_archive_err_partner": "Ne mogu da arhiviram partnersku imovinu, preskačem", "home_page_building_timeline": "Kreiranje hronoloÅĄke linije", - "home_page_delete_err_partner": "Can not delete partner assets, skipping", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", + "home_page_delete_err_partner": "Ne mogu da obriÅĄem partnersku imovinu, preskačem", + "home_page_delete_remote_err_local": "Lokalna sredstva u obrisavanju udaljenog izbora, preskakanje", "home_page_favorite_err_local": "Trenutno nije moguce dodati lokalne zapise u favorite, preskacu se", - "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", + "home_page_favorite_err_partner": "JoÅĄ uvek nije mogucˁe označiti partnerske resurse kao omiljene, preskačem", "home_page_first_time_notice": "Ako je ovo prvi put da koristite aplikaciju, molimo Vas da odaberete albume koje Åželite da sačuvate", - "home_page_share_err_local": "Can not share local assets via link, skipping", - "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", + "home_page_share_err_local": "Ne mogu da delim lokalne resurse preko linka, preskačem", + "home_page_upload_err_limit": "MoÅžete otpremiti najviÅĄe 30 elemenata istovremeno, preskačucˁi", "host": "Domacˁin (Host)", "hour": "Sat", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", + "ignore_icloud_photos": "IgnoriÅĄite iCloud fotografije", + "ignore_icloud_photos_description": "Fotografije koje su sačuvane na iCloud-u necˁe biti otpremljene na Immich server", "image": "Fotografija", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} snimljeno {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} snimljeno sa {person1} {date}", @@ -1058,10 +1054,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} sa {person1} i {person2} {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} snimljenou {city}, {country} sa {person1}, {person2} i {person3} {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} snimljeno u {city}, {country} sa {person1}, {person2} i joÅĄ {additionalCount, number} drugih {date}", - "image_saved_successfully": "Image saved", - "image_viewer_page_state_provider_download_started": "Download Started", + "image_saved_successfully": "Slika je sačuvana", + "image_viewer_page_state_provider_download_started": "Preuzimanje je započeto", "image_viewer_page_state_provider_download_success": "Preuzimanje UspeÅĄno", - "image_viewer_page_state_provider_share_error": "Share Error", + "image_viewer_page_state_provider_share_error": "GreÅĄka pri deljenju", "immich_logo": "Logo Immich-a", "immich_web_interface": "Web interfejs Immich-a", "import_from_json": "Uvezi iz JSON-a", @@ -1080,8 +1076,8 @@ "night_at_midnight": "Svaka noć u ponoć", "night_at_twoam": "Svaka noć u 2am" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", + "invalid_date": "NevaÅžecˁi datum", + "invalid_date_format": "NevaÅžecˁi format datuma", "invite_people": "Pozovite ljude", "invite_to_album": "Pozovi na album", "items_count": "{count, plural, one {# datoteka} other {# datoteka}}", @@ -1102,11 +1098,11 @@ "level": "Nivo", "library": "Biblioteka", "library_options": "Opcije biblioteke", - "library_page_device_albums": "Albums on Device", + "library_page_device_albums": "Albumi na uređaju", "library_page_new_album": "Novi album", - "library_page_sort_asset_count": "Number of assets", + "library_page_sort_asset_count": "Broj sredstava", "library_page_sort_created": "Najnovije kreirano", - "library_page_sort_last_modified": "Last modified", + "library_page_sort_last_modified": "Poslednja izmena", "library_page_sort_title": "Naziv albuma", "light": "Svetlo", "like_deleted": "Lajkuj izbrisano", @@ -1117,23 +1113,22 @@ "list": "Izlistaj", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretrage nije uspelo", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", - "location_picker_choose_on_map": "Choose on map", - "location_picker_latitude_error": "Enter a valid latitude", - "location_picker_latitude_hint": "Enter your latitude here", - "location_picker_longitude_error": "Enter a valid longitude", - "location_picker_longitude_hint": "Enter your longitude here", + "local_network_sheet_info": "Aplikacija cˁe se povezati sa serverom preko ove URL adrese kada koristi navedenu Vi-Fi mreÅžu", + "location_permission": "Dozvola za lokaciju", + "location_permission_content": "Da bi koristio funkciju automatskog prebacivanja, Immich-u je potrebna precizna dozvola za lokaciju kako bi mogao da pročita naziv trenutne Wi-Fi mreÅže", + "location_picker_choose_on_map": "Izaberite na mapi", + "location_picker_latitude_error": "Unesite vaÅžecˁu geografsku ÅĄirinu", + "location_picker_latitude_hint": "Unesite svoju geografsku ÅĄirinu ovde", + "location_picker_longitude_error": "Unesite vaÅžecˁu geografsku duÅžinu", + "location_picker_longitude_hint": "Unesite svoju geografsku duÅžinu ovde", "log_out": "Odjavi se", "log_out_all_devices": "Odjavite se sa svih uređaja", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", - "login_disabled": "Login has been disabled", - "login_form_api_exception": "API exception. Please check the server URL and try again.", - "login_form_back_button_text": "Back", + "login_disabled": "Prijava je onemogucˁena", + "login_form_api_exception": "Izuzetak API-ja. Molimo vas da proverite URL adresu servera i pokuÅĄate ponovo.", + "login_form_back_button_text": "Nazad", "login_form_email_hint": "vaÅĄemail@email.com", "login_form_endpoint_hint": "http://ip-vaÅĄeg-servera:port", "login_form_endpoint_url": "URL Servera", @@ -1145,14 +1140,13 @@ "login_form_failed_get_oauth_server_config": "Evidencija greÅĄaka koristeći OAuth, proveriti serverski link (URL)", "login_form_failed_get_oauth_server_disable": "OAuth opcija nije dostupna na ovom serveru", "login_form_failed_login": "NeuspeÅĄna prijava, proveri URL servera, email i ÅĄifru", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", + "login_form_handshake_exception": "DoÅĄlo je do izuzetka rukostiskanja sa serverom. Omogucˁite podrÅĄku za samopotpisane sertifikate u podeÅĄavanjima ako koristite samopotpisani sertifikat.", "login_form_password_hint": "ÅĄifra", "login_form_save_login": "Ostani prijavljen", - "login_form_server_empty": "Enter a server URL.", - "login_form_server_error": "Could not connect to server.", + "login_form_server_error": "Nije mogucˁe povezati se sa serverom.", "login_has_been_disabled": "Prijava je onemogucˁena.", - "login_password_changed_error": "There was an error updating your password", - "login_password_changed_success": "Password updated successfully", + "login_password_changed_error": "DoÅĄlo je do greÅĄke prilikom aÅžuriranja lozinke", + "login_password_changed_success": "Lozinka je uspeÅĄno aÅžurirana", "logout_all_device_confirmation": "Da li ste sigurni da Åželite da se odjavite sa svih uređaja?", "logout_this_device_confirmation": "Da li ste sigurni da Åželite da se odjavite sa ovog uređaja?", "longitude": "Geografska duÅžina", @@ -1170,40 +1164,41 @@ "manage_your_devices": "Upravljajte svojim prijavljenim uređajima", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Mapa", - "map_assets_in_bound": "{} photo", - "map_assets_in_bounds": "{} photos", - "map_cannot_get_user_location": "Cannot get user's location", - "map_location_dialog_yes": "Yes", - "map_location_picker_page_use_location": "Use this location", - "map_location_service_disabled_content": "Location service needs to be enabled to display assets from your current location. Do you want to enable it now?", - "map_location_service_disabled_title": "Location Service disabled", + "map_assets_in_bound": "{count} fotografija", + "map_assets_in_bounds": "{count} fotografija", + "map_cannot_get_user_location": "Nije mogucˁe dobiti lokaciju korisnika", + "map_location_dialog_yes": "Da", + "map_location_picker_page_use_location": "Koristite ovu lokaciju", + "map_location_service_disabled_content": "Usluga lokacije mora biti omogucˁena da bi se prikazivala sredstva sa vaÅĄe trenutne lokacije. Da li Åželite da je sada omogucˁite?", + "map_location_service_disabled_title": "Usluga lokacije je onemogucˁena", "map_marker_for_images": "Označivač na mapi za slike snimljene u {city}, {country}", "map_marker_with_image": "Marker na mapi sa slikom", - "map_no_assets_in_bounds": "No photos in this area", - "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", - "map_no_location_permission_title": "Location Permission denied", + "map_no_assets_in_bounds": "Nema fotografija u ovoj oblasti", + "map_no_location_permission_content": "Potrebna je dozvola za lokaciju da bi se prikazali resursi sa vaÅĄe trenutne lokacije. Da li Åželite da je sada dozvolite?", + "map_no_location_permission_title": "Dozvola za lokaciju je odbijena", "map_settings": "PodeÅĄavanja mape", - "map_settings_dark_mode": "Dark mode", - "map_settings_date_range_option_day": "Past 24 hours", - "map_settings_date_range_option_days": "Past {} days", - "map_settings_date_range_option_year": "Past year", - "map_settings_date_range_option_years": "Past {} years", - "map_settings_dialog_title": "Map Settings", - "map_settings_include_show_archived": "Include Archived", - "map_settings_include_show_partners": "Include Partners", - "map_settings_only_show_favorites": "Show Favorite Only", - "map_settings_theme_settings": "Map Theme", - "map_zoom_to_see_photos": "Zoom out to see photos", + "map_settings_dark_mode": "Tamni reÅžim", + "map_settings_date_range_option_day": "Poslednja 24 sata", + "map_settings_date_range_option_days": "Prethodnih {days} dana", + "map_settings_date_range_option_year": "ProÅĄla godina", + "map_settings_date_range_option_years": "Proteklih {years} godina", + "map_settings_dialog_title": "PodeÅĄavanja Mape", + "map_settings_include_show_archived": "Uključi arhivirano", + "map_settings_include_show_partners": "Uključi partnere", + "map_settings_only_show_favorites": "PrikaÅži samo omiljene", + "map_settings_theme_settings": "Tema mape", + "map_zoom_to_see_photos": "Umanjite da biste videli fotografije", + "mark_all_as_read": "Označi sve kao pročitano", + "mark_as_read": "Označi kao pročitano", + "marked_all_as_read": "Sve je označeno kao pročitano", "matches": "Podudaranja", "media_type": "Vrsta medija", "memories": "Secˁanja", - "memories_all_caught_up": "All caught up", - "memories_check_back_tomorrow": "Check back tomorrow for more memories", + "memories_all_caught_up": "Sve je uhvacˁeno", + "memories_check_back_tomorrow": "Vratite se sutra za joÅĄ uspomena", "memories_setting_description": "Upravljajte onim ÅĄto vidite u svojim secˁanjima", - "memories_start_over": "Start Over", - "memories_swipe_to_close": "Swipe up to close", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", + "memories_start_over": "Počni ispočetka", + "memories_swipe_to_close": "Prevucite nagore da biste zatvorili", "memory": "Memorija", "memory_lane_title": "Traka secˁanja {title}", "menu": "Meni", @@ -1216,24 +1211,25 @@ "minimize": "Minimizirajte", "minute": "Minut", "missing": "Nedostaje", - "model": "Model", "month": "Mesec", - "monthly_title_text_date_format": "MMMM y", "more": "ViÅĄe", + "moved_to_archive": "PremeÅĄteno {count, plural, one {# datoteka} other {# datoteke}} u arhivu", + "moved_to_library": "PremeÅĄteno {count, plural, one {# datoteka} other {# datoteke}} u biblioteku", "moved_to_trash": "PremeÅĄteno u smecˁe", - "multiselect_grid_edit_date_time_err_read_only": "Cannot edit date of read only asset(s), skipping", - "multiselect_grid_edit_gps_err_read_only": "Cannot edit location of read only asset(s), skipping", + "multiselect_grid_edit_date_time_err_read_only": "Ne moÅžete da izmenite datum elemenata samo za čitanje, preskačem", + "multiselect_grid_edit_gps_err_read_only": "Ne mogu da izmenim lokaciju elemenata samo za čitanje, preskačem", "mute_memories": "PriguÅĄi secˁanja", "my_albums": "Moji albumi", "name": "Ime", "name_or_nickname": "Ime ili nadimak", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "UmreÅžavanje", + "networking_subtitle": "Upravljajte podeÅĄavanjima krajnje tačke servera", "never": "Nikada", "new_album": "Novi Album", "new_api_key": "Novi API ključ (key)", "new_password": "Nova ÅĄifra", "new_person": "Nova osoba", + "new_pin_code": "Novi PIN kod", "new_user_created": "Novi korisnik je kreiran", "new_version_available": "DOSTUPNA NOVA VERZIJA", "newest_first": "Najnovije prvo", @@ -1245,36 +1241,36 @@ "no_albums_yet": "Izgleda da joÅĄ nemate nijedan album.", "no_archived_assets_message": "Arhivirajte fotografije i video zapise da biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA UPLOADIRATE SVOJU PRVU FOTOGRAFIJU", - "no_assets_to_show": "No assets to show", + "no_assets_to_show": "Nema elemenata za prikaz", "no_duplicates_found": "Nije pronađen nijedan duplikat.", "no_exif_info_available": "Nema dostupnih exif informacija", "no_explore_results_message": "Uploadujte joÅĄ fotografija da biste istraÅžili svoju kolekciju.", "no_favorites_message": "Postavite favorite da biste brzo naÅĄli vaÅĄe najbolje slike i video snimke", "no_libraries_message": "Napravite spoljnu biblioteku da biste videli svoje fotografije i video zapise", "no_name": "Nema imena", + "no_notifications": "Nema obaveÅĄtenja", + "no_people_found": "Nisu pronađeni odgovarajucˁi ljudi", "no_places": "Nema mesta", "no_results": "Nema rezultata", "no_results_description": "PokuÅĄajte sa sinonimom ili opÅĄtijom ključnom reči", "no_shared_albums_message": "Napravite album da biste delili fotografije i video zapise sa ljudima u vaÅĄoj mreÅži", "not_in_any_album": "Nema ni u jednom albumu", - "not_selected": "Not selected", + "not_selected": "Nije izabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primenili oznaku za skladiÅĄtenje na prethodno uploadirane datoteke, pokrenite", "notes": "Napomene", - "notification_permission_dialog_content": "Da bi ukljucili notifikacije, idite u Opcije i odaberite Dozvoli", - "notification_permission_list_tile_content": "Dozvoli Notifikacije\n", + "notification_permission_dialog_content": "Da bi ukljucili notifikacije, idite u Opcije i odaberite Dozvoli.", + "notification_permission_list_tile_content": "Dajte dozvolu za omogucˁavanje obaveÅĄtenja.", "notification_permission_list_tile_enable_button": "Uključi Notifikacije", "notification_permission_list_tile_title": "Dozvole za notifikacije", "notification_toggle_setting_description": "Omogucˁite obaveÅĄtenja putem e-poÅĄte", "notifications": "Notifikacije", "notifications_setting_description": "Upravljajte obaveÅĄtenjima", - "oauth": "OAuth", "official_immich_resources": "Zvanični Immich resursi", "offline": "Odsutan (Offline)", "offline_paths": "Nedostupne (Offline) putanje", "offline_paths_description": "Ovi rezultati mogu biti posledica ručnog brisanja datoteka koje nisu deo spoljne biblioteke.", - "ok": "Ok", "oldest_first": "Najstarije prvo", - "on_this_device": "On this device", + "on_this_device": "Na ovom uređaju", "onboarding": "Pristupanje (Onboarding)", "onboarding_privacy_description": "Sledecˁe (opcione) funkcije se oslanjaju na spoljne usluge i mogu se onemogucˁiti u bilo kom trenutku u podeÅĄavanjima administracije.", "onboarding_theme_description": "Izaberite temu boja za svoj nalog. Ovo moÅžete kasnije da promenite u podeÅĄavanjima.", @@ -1282,6 +1278,7 @@ "onboarding_welcome_user": "DobrodoÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo favoriti", + "open": "Otvori", "open_in_map_view": "Otvorite u prikaz karte", "open_in_openstreetmap": "Otvorite u OpenStreetMap-u", "open_the_search_filters": "Otvorite filtere za pretragu", @@ -1298,14 +1295,14 @@ "partner_can_access": "{partner} moÅže da pristupi", "partner_can_access_assets": "Sve vaÅĄe fotografije i video snimci osim onih u arhiviranim i izbrisanim", "partner_can_access_location": "Lokacija na kojoj su vaÅĄe fotografije snimljene", - "partner_list_user_photos": "{user}'s photos", - "partner_list_view_all": "View all", - "partner_page_empty_message": "Your photos are not yet shared with any partner.", - "partner_page_no_more_users": "No more users to add", - "partner_page_partner_add_failed": "Failed to add partner", - "partner_page_select_partner": "Select partner", - "partner_page_shared_to_title": "Shared to", - "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.", + "partner_list_user_photos": "Fotografije korisnika {user}", + "partner_list_view_all": "PrikaÅži sve", + "partner_page_empty_message": "VaÅĄe fotografije joÅĄ uvek nisu deljene ni sa jednim partnerom.", + "partner_page_no_more_users": "Nema viÅĄe korisnika za dodavanje", + "partner_page_partner_add_failed": "Dodavanje partnera nije uspelo", + "partner_page_select_partner": "Izaberite partnera", + "partner_page_shared_to_title": "Deljeno sa", + "partner_page_stop_sharing_content": "{partner} viÅĄe necˁe mocˁi da pristupi vaÅĄim fotografijama.", "partner_sharing": "Partnersko deljenje", "partners": "Partneri", "password": "Å ifra", @@ -1334,23 +1331,26 @@ "permanently_delete_assets_prompt": "Da li ste sigurni da Åželite da trajno izbriÅĄete {count, plural, one {ovu datoteku?} other {ove # datoteke?}}Ovo cˁe ih takođe ukloniti {count, plural, one {iz njihovog} other {iz njihovih}} albuma.", "permanently_deleted_asset": "Trajno izbrisana datoteka", "permanently_deleted_assets_count": "Trajno izbrisano {count, plural, one {# datoteka} other {# datoteke}}", - "permission_onboarding_back": "Back", - "permission_onboarding_continue_anyway": "Continue anyway", - "permission_onboarding_get_started": "Get started", - "permission_onboarding_go_to_settings": "Go to settings", - "permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.", - "permission_onboarding_permission_granted": "Permission granted! You are all set.", - "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", - "permission_onboarding_request": "Immich requires permission to view your photos and videos.", + "permission_onboarding_back": "Nazad", + "permission_onboarding_continue_anyway": "Ipak nastavi", + "permission_onboarding_get_started": "Započnite", + "permission_onboarding_go_to_settings": "Idi na podeÅĄavanja", + "permission_onboarding_permission_denied": "Dozvola odbijena. Da biste koristili Immich, dodelite dozvole za fotografije i video zapise u PodeÅĄavanjima.", + "permission_onboarding_permission_granted": "Dozvola odobrena! Spremni ste.", + "permission_onboarding_permission_limited": "Dozvola ograničena. Da biste omogucˁili Immich-u da pravi rezervne kopije i upravlja celom vaÅĄom kolekcijom galerije, dodelite dozvole za fotografije i video zapise u PodeÅĄavanjima.", + "permission_onboarding_request": "Immich zahteva dozvolu da vidi vaÅĄe fotografije i video zapise.", "person": "Osoba", "person_birthdate": "Rođen(a) {date}", "person_hidden": "{name}{hidden, select, true { (skriveno)} other {}}", "photo_shared_all_users": "Izgleda da ste podelili svoje fotografije sa svim korisnicima ili da nemate nijednog korisnika sa kojim biste delili.", - "photos": "Slike", + "photos": "Fotografije", "photos_and_videos": "Fotografije & Video zapisi", "photos_count": "{count, plural, one {{count, number} fotografija} few {{count, number} fotografije} other {{count, number} fotografija}}", "photos_from_previous_years": "Fotografije iz prethodnih godina", "pick_a_location": "Odaberi lokaciju", + "pin_code_changed_successfully": "PIN kod je uspeÅĄno promenjen", + "pin_code_reset_successfully": "PIN kod je uspeÅĄno resetovan", + "pin_code_setup_successfully": "UspeÅĄno podeÅĄavanje PIN koda", "place": "Mesto", "places": "Mesta", "places_count": "{count, plural, one {{count, number} Mesto} other {{count, number} Mesta}}", @@ -1359,8 +1359,8 @@ "play_motion_photo": "Pokreni pokretnu fotografiju", "play_or_pause_video": "Pokreni ili pauziraj video zapis", "port": "port", - "preferences_settings_subtitle": "Manage the app's preferences", - "preferences_settings_title": "Preferences", + "preferences_settings_subtitle": "Upravljajte podeÅĄavanjima aplikacije", + "preferences_settings_title": "PodeÅĄavanja", "preset": "Unapred podeÅĄeno", "preview": "Pregled", "previous": "ProÅĄlo", @@ -1368,20 +1368,20 @@ "previous_or_next_photo": "Prethodna ili sledecˁa fotografija", "primary": "Primarna (Primary)", "privacy": "Privatnost", + "profile": "Profil", "profile_drawer_app_logs": "Evidencija", - "profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.", - "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", + "profile_drawer_client_out_of_date_major": "Mobilna aplikacija je zastarela. Molimo vas da je aÅžurirate na najnoviju glavnu verziju.", + "profile_drawer_client_out_of_date_minor": "Mobilna aplikacija je zastarela. Molimo vas da je aÅžurirate na najnoviju sporednu verziju.", "profile_drawer_client_server_up_to_date": "Klijent i server su najnovije verzije", - "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", - "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", + "profile_drawer_server_out_of_date_major": "Server je zastareo. Molimo vas da aÅžurirate na najnoviju glavnu verziju.", + "profile_drawer_server_out_of_date_minor": "Server je zastareo. Molimo vas da aÅžurirate na najnoviju sporednu verziju.", "profile_image_of_user": "Slika profila od korisnika {user}", "profile_picture_set": "Profilna slika postavljena.", "public_album": "Javni album", "public_share": "Javno deljenje", "purchase_account_info": "PodrÅžavam softver", "purchase_activated_subtitle": "Hvala vam ÅĄto podrÅžavate Immich i softver otvorenog koda", - "purchase_activated_time": "Aktivirano {date, date}", + "purchase_activated_time": "Aktivirano {date}", "purchase_activated_title": "VaÅĄ ključ je uspeÅĄno aktiviran", "purchase_button_activate": "Aktiviraj", "purchase_button_buy": "Kupi", @@ -1409,7 +1409,6 @@ "purchase_remove_server_product_key_prompt": "Da li ste sigurni da Åželite da uklonite ÅĄifru proizvoda sa servera?", "purchase_server_description_1": "Za ceo server", "purchase_server_description_2": "Status podrÅĄke", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Ključem proizvoda servera upravlja administrator", "rating": "Ocena zvezdica", "rating_clear": "ObriÅĄi ocenu", @@ -1424,8 +1423,10 @@ "recent": "SkoraÅĄnji", "recent-albums": "Nedavni albumi", "recent_searches": "SkoraÅĄnje pretrage", - "recently_added": "Recently added", - "recently_added_page_title": "Recently Added", + "recently_added": "Nedavno dodato", + "recently_added_page_title": "Nedavno Dodato", + "recently_taken": "Nedavno snimljeno", + "recently_taken_page_title": "Nedavno Snimljeno", "refresh": "OsveÅži", "refresh_encoded_videos": "OsveÅžite kodirane (encoded) video zapise", "refresh_faces": "OsveÅži lica", @@ -1468,6 +1469,7 @@ "reset": "Resetovati", "reset_password": "Resetovati lozinku", "reset_people_visibility": "Resetujte vidljivost osoba", + "reset_pin_code": "Resetuj PIN kod", "reset_to_default": "Resetujte na podrazumevane vrednosti", "resolve_duplicates": "ReÅĄi duplikate", "resolved_all_duplicates": "Svi duplikati su razreÅĄeni", @@ -1482,12 +1484,12 @@ "role_editor": "Urednik", "role_viewer": "Gledalac", "save": "Sačuvaj", - "save_to_gallery": "Save to gallery", + "save_to_gallery": "Sačuvaj u galeriju", "saved_api_key": "Sačuvan API ključ (key)", "saved_profile": "Sačuvan profil", "saved_settings": "Sačuvana podeÅĄavanja", "say_something": "Reci neÅĄto", - "scaffold_body_error_occurred": "Error occurred", + "scaffold_body_error_occurred": "DoÅĄlo je do greÅĄke", "scan_all_libraries": "Skeniraj sve biblioteke", "scan_library": "Skeniraj", "scan_settings": "PodeÅĄavanja skeniranja", @@ -1503,45 +1505,41 @@ "search_camera_model": "PretraÅži model kamere...", "search_city": "PretraÅži grad...", "search_country": "TraÅži zemlju...", - "search_filter_apply": "Apply filter", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", - "search_filter_display_option_not_in_album": "Not in album", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", + "search_filter_apply": "Primeni filter", + "search_filter_camera_title": "Izaberite tip kamere", + "search_filter_date_title": "Izaberite period", + "search_filter_display_options": "Opcije prikaza", + "search_filter_filename": "Pretraga po imenu datoteke", + "search_filter_location": "Lokacija", + "search_filter_location_title": "Izaberite lokaciju", + "search_filter_media_type_title": "Izaberite tip medija", + "search_filter_people_title": "Izaberite ljude", "search_for": "TraÅži", "search_for_existing_person": "PotraÅžite postojecˁu osobu", - "search_no_more_result": "No more results", + "search_no_more_result": "Nema viÅĄe rezultata", "search_no_people": "Bez osoba", "search_no_people_named": "Nema osoba sa imenom „{name}“", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "Nisu pronađeni rezultati, pokuÅĄajte sa drugim terminom za pretragu ili kombinacijom", "search_options": "Opcije pretrage", - "search_page_categories": "Categories", - "search_page_motion_photos": "Motion Photos", + "search_page_categories": "Kategorije", + "search_page_motion_photos": "Fotografije u pokretu", "search_page_no_objects": "Bez informacija", "search_page_no_places": "Nema informacija o mestu", - "search_page_screenshots": "Screenshots", - "search_page_search_photos_videos": "Search for your photos and videos", - "search_page_selfies": "Selfies", + "search_page_screenshots": "Snimci ekrana", + "search_page_search_photos_videos": "PretraÅžite svoje fotografije i video zapise", + "search_page_selfies": "Selfiji", "search_page_things": "Stvari", - "search_page_view_all_button": "View all", - "search_page_your_activity": "Your activity", - "search_page_your_map": "Your Map", + "search_page_view_all_button": "PrikaÅži sve", + "search_page_your_activity": "VaÅĄa aktivnost", + "search_page_your_map": "VaÅĄa mapa", "search_people": "PretraÅži osobe", "search_places": "PretraÅži mesta", "search_rating": "Pretraga po oceni...", "search_result_page_new_search_hint": "Nova pretraga", "search_settings": "Pretraga podeÅĄavanja", "search_state": "TraÅži region...", - "search_suggestion_list_smart_search_hint_1": "Smart search is enabled by default, to search for metadata use the syntax ", - "search_suggestion_list_smart_search_hint_2": "m:your-search-term", + "search_suggestion_list_smart_search_hint_1": "Pametna pretraga je podrazumevano omogucˁena, za pretragu metapodataka koristite sintaksu ", + "search_suggestion_list_smart_search_hint_2": "m:vaÅĄ-pojam-za-pretragu", "search_tags": "PretraÅži oznake (tags)...", "search_timezone": "PretraÅži vremensku zonu...", "search_type": "Vrsta pretrage", @@ -1560,6 +1558,7 @@ "select_keep_all": "Izaberite da zadrÅžite sve", "select_library_owner": "Izaberite vlasnika biblioteke", "select_new_face": "Izaberite novo lice", + "select_person_to_tag": "Izaberite osobu za označavanje", "select_photos": "Odaberi fotografije", "select_trash_all": "Izaberite da sve bacite na otpad", "select_user_for_sharing_page_err_album": "NeuspeÅĄno kreiranje albuma", @@ -1567,9 +1566,8 @@ "selected_count": "{count, plural, other {# izabrano}}", "send_message": "PoÅĄalji poruku", "send_welcome_email": "PoÅĄaljite e-poÅĄtu dobrodoÅĄlice", - "server_endpoint": "Server Endpoint", + "server_endpoint": "Krajnja tačka servera", "server_info_box_app_version": "Verzija Aplikacije", - "server_info_box_server_url": "Server URL", "server_offline": "Server van mreÅže (offline)", "server_online": "Server na mreÅži (online)", "server_stats": "Statistika servera", @@ -1581,87 +1579,83 @@ "set_date_of_birth": "Podesite datum rođenja", "set_profile_picture": "Postavi profilnu sliku", "set_slideshow_to_fullscreen": "Postavite projekciju slajdova na ceo ekran", - "setting_image_viewer_help": "Detaljno pregledanje prvo učitava minijaturu, pa srednju, pa original. (Ako te opcije uključene)", + "setting_image_viewer_help": "Pregledač detalja prvo učitava malu sličicu, zatim pregled srednje veličine (ako je omogucˁen), i na kraju original (ako je omogucˁen).", "setting_image_viewer_original_subtitle": "Aktiviraj učitavanje slika u punoj rezoluciji (Velika!). Deaktivacijom ove stavke moÅžeÅĄ da smanjiÅĄ potroÅĄnju interneta i zauzetog prostora na uređaju.", "setting_image_viewer_original_title": "Učitaj originalnu sliku", "setting_image_viewer_preview_subtitle": "Aktiviraj učitavanje slika u srednjoj rezoluciji. Deaktiviraj da se direktno učitava original, ili da se samo koristi minijatura.", "setting_image_viewer_preview_title": "Pregledaj sliku", - "setting_image_viewer_title": "Images", - "setting_languages_apply": "Apply", - "setting_languages_subtitle": "Change the app's language", - "setting_languages_title": "Languages", - "setting_notifications_notify_failures_grace_period": "NeuspeÅĄne rezervne kopije: {}", - "setting_notifications_notify_hours": "{} sati", + "setting_image_viewer_title": "Slike", + "setting_languages_apply": "Primeni", + "setting_languages_subtitle": "Promenite jezik aplikacije", + "setting_languages_title": "Jezici", + "setting_notifications_notify_failures_grace_period": "Obavesti o greÅĄkama u pravljenju rezervnih kopija u pozadini: {duration}", + "setting_notifications_notify_hours": "{count} sati", "setting_notifications_notify_immediately": "odmah", - "setting_notifications_notify_minutes": "{} minuta", + "setting_notifications_notify_minutes": "{count} minuta", "setting_notifications_notify_never": "nikada", - "setting_notifications_notify_seconds": "{} sekundi", + "setting_notifications_notify_seconds": "{count} sekundi", "setting_notifications_single_progress_subtitle": "Detaljne informacije o otpremanju, po zapisu", "setting_notifications_single_progress_title": "PrikaÅži detalje pozadinskog pravljenja rezervnih kopija", "setting_notifications_subtitle": "Izmeni notifikacije", "setting_notifications_total_progress_subtitle": "Ukupno otpremljenih stavki (zavrÅĄeno/ukupno stavki)", - "setting_notifications_total_progress_title": "PrikaÅži ukupan napredak pozadinskog bekapovanja.\n\n", - "setting_video_viewer_looping_title": "Looping", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_notifications_total_progress_title": "PrikaÅži ukupan napredak pravljenja rezervnih kopija u pozadini", + "setting_video_viewer_looping_title": "Petljanje (Looping)", + "setting_video_viewer_original_video_subtitle": "Prilikom strimovanja videa sa servera, reprodukujte original čak i kada je dostupno transkodiranje. MoÅže dovesti do baferovanja. Video snimci dostupni lokalno se reprodukuju u originalnom kvalitetu bez obzira na ovo podeÅĄavanje.", + "setting_video_viewer_original_video_title": "Prisilno originalni video", "settings": "PodeÅĄavanja", "settings_require_restart": "Restartujte Immich da primenite ovu promenu", "settings_saved": "PodeÅĄavanja sačuvana", + "setup_pin_code": "Podesite PIN kod", "share": "Podeli", "share_add_photos": "Dodaj fotografije", - "share_assets_selected": "{} selected", + "share_assets_selected": "Izabrano je {count}", "share_dialog_preparing": "Pripremanje...", "shared": "Deljeno", - "shared_album_activities_input_disable": "Comment is disabled", - "shared_album_activity_remove_content": "Do you want to delete this activity?", - "shared_album_activity_remove_title": "Delete Activity", - "shared_album_section_people_action_error": "Error leaving/removing from album", - "shared_album_section_people_action_leave": "Remove user from album", - "shared_album_section_people_action_remove_user": "Remove user from album", - "shared_album_section_people_title": "PEOPLE", + "shared_album_activities_input_disable": "Komentar je onemogucˁen", + "shared_album_activity_remove_content": "Da li Åželite da obriÅĄete ovu aktivnost?", + "shared_album_activity_remove_title": "ObriÅĄi aktivnost", + "shared_album_section_people_action_error": "GreÅĄka pri napuÅĄtanju/uklanjanju iz albuma", + "shared_album_section_people_action_leave": "Ukloni korisnika iz albuma", + "shared_album_section_people_action_remove_user": "Ukloni korisnika iz albuma", "shared_by": "Podelio", "shared_by_user": "Deli {user}", "shared_by_you": "Vi delite", "shared_from_partner": "Slike od {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", - "shared_link_app_bar_title": "Shared Links", - "shared_link_clipboard_copied_massage": "Copied to clipboard", - "shared_link_clipboard_text": "Link: {}\nPassword: {}", - "shared_link_create_error": "Error while creating shared link", - "shared_link_edit_description_hint": "Enter the share description", - "shared_link_edit_expire_after_option_day": "1 day", - "shared_link_edit_expire_after_option_days": "{} days", - "shared_link_edit_expire_after_option_hour": "1 hour", - "shared_link_edit_expire_after_option_hours": "{} hours", - "shared_link_edit_expire_after_option_minute": "1 minute", - "shared_link_edit_expire_after_option_minutes": "{} minutes", - "shared_link_edit_expire_after_option_months": "{} months", - "shared_link_edit_expire_after_option_year": "{} year", - "shared_link_edit_password_hint": "Enter the share password", - "shared_link_edit_submit_button": "Update link", - "shared_link_error_server_url_fetch": "Cannot fetch the server url", - "shared_link_expires_day": "Expires in {} day", - "shared_link_expires_days": "Expires in {} days", - "shared_link_expires_hour": "Expires in {} hour", - "shared_link_expires_hours": "Expires in {} hours", - "shared_link_expires_minute": "Expires in {} minute", - "shared_link_expires_minutes": "Expires in {} minutes", - "shared_link_expires_never": "Expires ∞", - "shared_link_expires_second": "Expires in {} second", - "shared_link_expires_seconds": "Expires in {} seconds", - "shared_link_individual_shared": "Individual shared", - "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "Manage Shared links", + "shared_intent_upload_button_progress_text": "Otpremljeno je {current} / {total}", + "shared_link_app_bar_title": "Deljeni linkovi", + "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik (clipboard)", + "shared_link_clipboard_text": "Link: {link}\nLozinka: {password}", + "shared_link_create_error": "GreÅĄka pri kreiranju deljenog linka", + "shared_link_edit_description_hint": "Unesite opis deljenja", + "shared_link_edit_expire_after_option_days": "{count} dana", + "shared_link_edit_expire_after_option_hour": "1 sat", + "shared_link_edit_expire_after_option_hours": "{count} sati", + "shared_link_edit_expire_after_option_minutes": "{count} minuta", + "shared_link_edit_expire_after_option_months": "{count} meseci", + "shared_link_edit_expire_after_option_year": "{count} godina", + "shared_link_edit_password_hint": "Unesite lozinku za deljenje", + "shared_link_error_server_url_fetch": "Ne mogu da preuzmem URL servera", + "shared_link_expires_day": "Ističe za {count} dan(a)", + "shared_link_expires_days": "Ističe za {count} dana", + "shared_link_expires_hour": "Ističe za {count} sat", + "shared_link_expires_hours": "Ističe za {count} sati(a)", + "shared_link_expires_minute": "Ističe za {count} minut", + "shared_link_expires_minutes": "Ističe za {count} minuta", + "shared_link_expires_never": "Ističe ∞", + "shared_link_expires_second": "Ističe za {count} sekundu", + "shared_link_expires_seconds": "Ističe za {count} sekundi", + "shared_link_individual_shared": "Pojedinačno deljeno", + "shared_link_manage_links": "Upravljajte deljenim linkovima", "shared_link_options": "Opcije deljene veze", "shared_links": "Deljene veze", "shared_links_description": "Delite fotografije i video zapise pomocˁu linka", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljene fotografije i video zapise.}}", - "shared_with_me": "Shared with me", + "shared_with_me": "Deljeno sa mnom", "shared_with_partner": "Deli se sa {partner}", "sharing": "Deljenje", "sharing_enter_password": "Unesite lozinku da biste videli ovu stranicu.", "sharing_page_album": "Deljeni albumi", - "sharing_page_description": "Napravi deljene albume da deliÅĄ fotografije i video zapise sa ljudima na tvojoj mreÅži", + "sharing_page_description": "Napravi deljene albume da deliÅĄ fotografije i video zapise sa ljudima na tvojoj mreÅži.", "sharing_page_empty_list": "PRAZNA LISTA", "sharing_sidebar_description": "PrikaÅžite vezu do Deljenja na bočnoj traci", "sharing_silver_appbar_create_shared_album": "Napravi deljeni album", @@ -1716,13 +1710,13 @@ "start": "Početak", "start_date": "Datum početka", "state": "Stanje", - "status": "Status", "stop_motion_photo": "Zaustavi pokretnu fotografiju", "stop_photo_sharing": "ÅŊelite da zaustavite deljenje fotografija?", "stop_photo_sharing_description": "{partner} viÅĄe necˁe mocˁi da pristupi vaÅĄim fotografijama.", "stop_sharing_photos_with_user": "Prestanite da delite svoje fotografije sa ovim korisnikom", "storage": "SkladiÅĄte (Storage space)", "storage_label": "Oznaka za skladiÅĄtenje", + "storage_quota": "Kvota skladiÅĄtenja", "storage_usage": "Koristi se {used} od {available}", "submit": "Dostavi", "suggestions": "Sugestije", @@ -1732,9 +1726,9 @@ "support_third_party_description": "VaÅĄa immich instalacija je spakovana od strane trecˁe strane. Problemi sa kojima se suočavate mogu biti uzrokovani tim paketom, pa vas molimo da im prvo postavite probleme koristecˁi donje veze.", "swap_merge_direction": "Zamenite pravac spajanja", "sync": "Sinhronizacija", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", + "sync_albums": "Sinhronizuj albume", + "sync_albums_manual_subtitle": "Sinhronizujte sve otpremljene video zapise i fotografije sa izabranim rezervnim albumima", + "sync_upload_album_setting_subtitle": "Kreirajte i otpremite svoje fotografije i video zapise u odabrane albume na Immich-u", "tag": "Oznaka (tag)", "tag_assets": "Označite (tag) sredstva", "tag_created": "Napravljena oznaka (tag): {tag}", @@ -1749,14 +1743,14 @@ "theme_selection": "Izbor teme", "theme_selection_description": "Automatski postavite temu na svetlu ili tamnu na osnovu sistemskih preferencija vaÅĄeg pretraÅživača", "theme_setting_asset_list_storage_indicator_title": "PrikaÅži indikator prostora na zapisima", - "theme_setting_asset_list_tiles_per_row_title": "Broj zapisa po redu ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "Broj zapisa po redu {count}", + "theme_setting_colorful_interface_subtitle": "Nanesite osnovnu boju na pozadinske povrÅĄine.", + "theme_setting_colorful_interface_title": "Å areni interfejs", "theme_setting_image_viewer_quality_subtitle": "Prilagodite kvalitet prikaza za detaljno pregledavanje slike", "theme_setting_image_viewer_quality_title": "Kvalitet pregledača slika", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", + "theme_setting_primary_color_subtitle": "Izaberite boju za glavne radnje i akcente.", + "theme_setting_primary_color_title": "Primarna boja", + "theme_setting_system_primary_color_title": "Koristi sistemsku boju", "theme_setting_system_theme_switch": "Automatski (Prati opcije sistema)", "theme_setting_theme_subtitle": "Odaberi temu sistema", "theme_setting_three_stage_loading_subtitle": "Trostepeno učitavanje moÅžda ubrza učitavanje, po cenu potroÅĄnje podataka", @@ -1780,17 +1774,19 @@ "trash_all": "Baci sve u otpad", "trash_count": "Otpad {count, number}", "trash_delete_asset": "Otpad/IzbriÅĄi datoteku", - "trash_emptied": "Emptied trash", + "trash_emptied": "Ispraznio smecˁe", "trash_no_results_message": "Slike i video zapisi u otpadu cˁe se pojaviti ovde.", - "trash_page_delete_all": "Delete All", - "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich", - "trash_page_info": "Trashed items will be permanently deleted after {} days", - "trash_page_no_assets": "No trashed assets", - "trash_page_restore_all": "Restore All", - "trash_page_select_assets_btn": "Select assets", - "trash_page_title": "Trash ({})", + "trash_page_delete_all": "ObriÅĄi sve", + "trash_page_empty_trash_dialog_content": "Da li Åželite da ispraznite svoja premeÅĄtena sredstva? Ovi predmeti cˁe biti trajno uklonjeni iz Immich-a", + "trash_page_info": "Stavke izbačene iz otpada bicˁe trajno obrisane nakon {days} dana", + "trash_page_no_assets": "Nema elemenata u otpadu", + "trash_page_restore_all": "Vrati sve", + "trash_page_select_assets_btn": "Izaberite sredstva", + "trash_page_title": "Otpad ({count})", "trashed_items_will_be_permanently_deleted_after": "Datoteke u otpadu cˁe biti trajno izbrisane nakon {days, plural, one {# dan} few {# dana} other {# dana}}.", "type": "Vrsta", + "unable_to_change_pin_code": "Nije mogucˁe promeniti PIN kod", + "unable_to_setup_pin_code": "Nije mogucˁe podesiti PIN kod", "unarchive": "Vrati iz arhive", "unarchived_count": "{count, plural, other {Nearhivirano#}}", "unfavorite": "Izbaci iz omiljenih (unfavorite)", @@ -1814,11 +1810,12 @@ "untracked_files": "Nepracˁene Datoteke", "untracked_files_decription": "Aplikacija ne prati ove datoteke. One mogu nastati zbog neuspeÅĄnih premeÅĄtenja, zbog prekinutih otpremanja ili kao preostatak zbog greÅĄke", "up_next": "Sledecˁe", + "updated_at": "AÅžurirano", "updated_password": "AÅžurirana lozinka", "upload": "Uploaduj", "upload_concurrency": "Paralelno uploadovanje", - "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", - "upload_dialog_title": "Upload Asset", + "upload_dialog_info": "Da li Åželite da napravite rezervnu kopiju izabranih elemenata na serveru?", + "upload_dialog_title": "Otpremi element", "upload_errors": "Otpremanje je zavrÅĄeno sa {count, plural, one {# greÅĄkom} other {# greÅĄaka}}, osveÅžite stranicu da biste videli nove datoteke za otpremanje (upload).", "upload_progress": "Preostalo {remaining, number} – Obrađeno {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočeno {count, plural, one {# dupla datoteka} other {# duplih datoteka}}", @@ -1826,15 +1823,16 @@ "upload_status_errors": "GreÅĄke", "upload_status_uploaded": "Otpremljeno (Uploaded)", "upload_success": "Otpremanje je uspeÅĄno, osveÅžite stranicu da biste videli nova sredstva za otpremanje (upload).", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", + "upload_to_immich": "Otpremi u Immich ({count})", + "uploading": "Otpremanje", "usage": "Upotreba", - "use_current_connection": "use current connection", + "use_current_connection": "koristi trenutnu vezu", "use_custom_date_range": "Umesto toga koristite prilagođeni period", "user": "Korisnik", "user_id": "ID korisnika", "user_liked": "{user} je lajkovao {type, select, photo {ovu fotografiju} video {ovaj video zapis} asset {ovu datoteku} other {ovo}}", + "user_pin_code_settings": "PIN kod", + "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", "user_purchase_settings": "Kupovina", "user_purchase_settings_description": "Upravljajte kupovinom", "user_role_set": "Postavi {user} kao {role}", @@ -1845,15 +1843,15 @@ "users": "Korisnici", "utilities": "Alati", "validate": "Proveri", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "Molimo vas da unesete vaÅžecˁi URL", "variables": "Promenljive (variables)", "version": "Verzija", "version_announcement_closing": "Tvoj prijatelj, Aleks", - "version_announcement_message": "Zdravo prijatelju, postoji nova verzija aplikacije, molimo vas da odvojite vreme da posetite napomene o izdanju i uverite se da je server aÅžuriran kako bi se sprečile bilo kakve pogreÅĄne konfiguracije, posebno ako koristite WatchTower ili bilo koji mehanizam koji automatski upravlja aÅžuriranjem vaÅĄe aplikacije.", + "version_announcement_message": "Zdravo! Dostupna je nova verzija Immich-a. Molimo vas da odvojite malo vremena da pročitate beleÅĄke o izdanju kako biste bili sigurni da je vaÅĄe podeÅĄavanje aÅžurirano i sprečili eventualne pogreÅĄne konfiguracije, posebno ako koristite WatchTower ili bilo koji mehanizam koji automatski aÅžurira vaÅĄu Immich instancu.", "version_announcement_overlay_release_notes": "novine nove verzije", "version_announcement_overlay_text_1": "Ćao, nova verzija", - "version_announcement_overlay_text_2": "molimo Vas izdvojite vremena da pogledate", - "version_announcement_overlay_text_3": "i proverite da su VaÅĄ docker-compose i .env najnovije verzije da bi izbegli greÅĄke u radu. Pogotovu ako koristite WatchTower ili bilo koji drugi mehanizam koji automatski instalira nove verzije vaÅĄe serverske aplikacije.", + "version_announcement_overlay_text_2": "molimo Vas izdvojite vremena da pogledate ", + "version_announcement_overlay_text_3": " i proverite da su VaÅĄ docker-compose i .env najnovije verzije da bi izbegli greÅĄke u radu. Pogotovu ako koristite WatchTower ili bilo koji drugi mehanizam koji automatski instalira nove verzije vaÅĄe serverske aplikacije.", "version_announcement_overlay_title": "Nova verzija servera je dostupna 🎉", "version_history": "Istorija verzija", "version_history_item": "Instalirano {version} {date}", @@ -1874,20 +1872,19 @@ "view_previous_asset": "Pogledaj prethodnu datoteku", "view_qr_code": "Pogledajte QR kod", "view_stack": "PrikaÅži gomilu", - "viewer_remove_from_stack": "Remove from Stack", - "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack", + "viewer_remove_from_stack": "Ukloni iz steka", + "viewer_stack_use_as_main_asset": "Koristi kao glavni resurs", "visibility_changed": "Vidljivost je promenjena za {count, plural, one {# osobu} other {# osobe}}", "waiting": "Čekam", "warning": "Upozorenje", "week": "Nedelja", "welcome": "DobrodoÅĄli", "welcome_to_immich": "DobrodoÅĄli u immich", - "wifi_name": "WiFi Name", + "wifi_name": "Naziv Wi-Fi mreÅže", "year": "Godina", "years_ago": "pre {years, plural, one {# godine} other {# godina}}", "yes": "Da", "you_dont_have_any_shared_links": "Nemate nijedno deljenje veze", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Ime vaÅĄe Wi-Fi mreÅže", "zoom_image": "Zumiraj sliku" } diff --git a/i18n/sv.json b/i18n/sv.json index a727aa68e2..8aaa76a5ec 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -26,6 +26,7 @@ "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", "add_to_album_bottom_sheet_already_exists": "Redan i {album}", + "add_to_locked_folder": "Addera till lÃĨst mapp", "add_to_shared_album": "Lägg till i delat album", "add_url": "Lägg till URL", "added_to_archive": "Tillagd i arkiv", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Är du säker pÃĨ att du vill inaktivera alla inloggningsmetoder? Inloggning kommer att helt inaktiveras.", "authentication_settings_reenable": "FÃļr att ÃĨteraktivera, använd Server Command.", "background_task_job": "Bakgrundsaktiviteter", - "backup_database": "Databassäkerhetskopia", - "backup_database_enable_description": "Aktivera säkerhetskopiering av databas", - "backup_keep_last_amount": "Antal säkerhetskopior att behÃĨlla", - "backup_settings": "Säkerhetskopieringsinställningar", - "backup_settings_description": "Hantera inställningar fÃļr säkerhetskopiering av databas", + "backup_database": "Skapa Databasdump", + "backup_database_enable_description": "Aktivera dumpning av databas", + "backup_keep_last_amount": "Antal databasdumpar att behÃĨlla", + "backup_settings": "Inställningar databasdump", + "backup_settings_description": "Hantera inställningar fÃļr databasdumpning. Observera: Dessa jobb Ãļvervakas inte och du blir inte notifierad om misslyckanden.", "check_all": "Välj alla", "cleanup": "Uppstädning", "cleared_jobs": "Rensade jobben fÃļr:{job}", @@ -53,6 +54,7 @@ "confirm_email_below": "FÃļr att bekräfta, skriv ”{email}” nedan", "confirm_reprocess_all_faces": "Är du säker pÃĨ att du vill ÃĨterprocessa alla ansikten? Detta kommer ocksÃĨ rensa namngivna personer.", "confirm_user_password_reset": "Är du säker pÃĨ att du vill ÃĨterställa lÃļsenordet fÃļr {user}?", + "confirm_user_pin_code_reset": "Är du säker pÃĨ att du vill ÃĨterställa PIN-kod fÃļr {user}?", "create_job": "Skapa jobb", "cron_expression": "Cron uttryck", "cron_expression_description": "Sätt skanningsintervall genom att använda cron-format. FÃļr mer information se Crontab Guru", @@ -192,26 +194,20 @@ "oauth_auto_register": "Autoregistrera", "oauth_auto_register_description": "Registrera nya användare automatiskt efter inloggning med OAuth", "oauth_button_text": "Knapptext", - "oauth_client_id": "Klient-ID", - "oauth_client_secret": "Klienthemlighet", "oauth_enable_description": "Logga in med OAuth", - "oauth_issuer_url": "Utfärdar-URL", "oauth_mobile_redirect_uri": "Telefonomdirigernings-URI", "oauth_mobile_redirect_uri_override": "Telefonomdirigerings-URI Ãļverrskridning", "oauth_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantÃļren inte tillÃĨter mobila URI:er, sÃĨ som '{callback}'", - "oauth_profile_signing_algorithm": "Profilsigneringsalgorithm", - "oauth_profile_signing_algorithm_description": "Algorithm som används fÃļr att signera användarprofilen.", - "oauth_scope": "Omfattning", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "FÃļr ytterligare detaljer om denna funktion, se dokumentationen.", - "oauth_signing_algorithm": "Signeringsalgoritm", "oauth_storage_label_claim": "Användaranknuten lagringsetikett", "oauth_storage_label_claim_description": "Sätter automatiskt angiven användares lagringsetikett.", "oauth_storage_quota_claim": "Användaranknuten lagringskvot", "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 fÃļr obegränsad kvot).", + "oauth_timeout_description": "Timeout fÃļr fÃļrfrÃĨgningar i millisekunder", "offline_paths": "Filer som inte kan hittas", "offline_paths_description": "Dessa resultat kan bero pÃĨ manuell borttagning av filer som inte är en del av ett externt bibliotek.", "password_enable_description": "Logga in med epost och lÃļsenord", @@ -352,6 +348,7 @@ "user_delete_delay_settings_description": "Antal dagar efter borttagning fÃļr att permanent radera en användares konto och tillgÃĨngar. Arbetet med borttagning av användare kÃļrs vid midnatt fÃļr att sÃļka efter användare som är redo fÃļr radering. Ändringar av denna inställning kommer att utvärderas vid nästa kÃļrning.", "user_delete_immediately": "{user} konto och tillgÃĨngar kommer att stÃĨ i kÃļ fÃļr permanent radering.", "user_delete_immediately_checkbox": "KÃļa användare och tillgÃĨngar fÃļr omedelbar radering", + "user_details": "Användardetaljer", "user_management": "Användarhantering", "user_password_has_been_reset": "Användarens lÃļsenord har ÃĨterställts:", "user_password_reset_description": "Ange det tillfälliga lÃļsenordet till användaren och informera dem om att de kommer att behÃļva ändra lÃļsenordet vid nästa inloggning.", @@ -367,17 +364,16 @@ "video_conversion_job": "Omkoda videor", "video_conversion_job_description": "Koda om videor fÃļr bredare kompatibilitet med webbläsare och enheter" }, - "admin_email": "Admin Email", "admin_password": "Admin LÃļsenord", - "administration": "Administration", "advanced": "Avancerat", - "advanced_settings_log_level_title": "LoggnivÃĨ: {}", + "advanced_settings_log_level_title": "LoggnivÃĨ: {level}", "advanced_settings_prefer_remote_subtitle": "Vissa enheter är mycket lÃĨngsamma pÃĨ att ladda miniatyrer frÃĨn objekt pÃĨ enheten. Aktivera den här inställningen fÃļr att ladda bilder frÃĨn servern istället.", "advanced_settings_prefer_remote_title": "FÃļredra bilder frÃĨn servern", "advanced_settings_proxy_headers_subtitle": "Definiera proxy-headers som Immich ska skicka med i varje närverksanrop", "advanced_settings_proxy_headers_title": "Proxy-headers", "advanced_settings_self_signed_ssl_subtitle": "Hoppar Ãļver SSL-certifikatverifiering fÃļr serverändpunkten. Krävs fÃļr självsignerade certifikat.", "advanced_settings_self_signed_ssl_title": "TillÃĨt självsignerade SSL-certifikat", + "advanced_settings_sync_remote_deletions_title": "Synkonisera fjärradering [EXPERIMENTELL]", "advanced_settings_tile_subtitle": "Avancerade användarinställningar", "advanced_settings_troubleshooting_subtitle": "Aktivera funktioner fÃļr felsÃļkning", "advanced_settings_troubleshooting_title": "FelsÃļkning", @@ -400,9 +396,9 @@ "album_remove_user_confirmation": "Är du säker pÃĨ att du vill ta bort {user}?", "album_share_no_users": "Det verkar som att du har delat det här albumet med alla användare eller sÃĨ har du inte nÃĨgon användare att dela med.", "album_thumbnail_card_item": "1 objekt", - "album_thumbnail_card_items": "{} objekt", + "album_thumbnail_card_items": "{count} objekt", "album_thumbnail_card_shared": " ¡ Delad", - "album_thumbnail_shared_by": "Delat av {}", + "album_thumbnail_shared_by": "Delat av {user}", "album_updated": "Albumet uppdaterat", "album_updated_setting_description": "FÃĨ ett e-postmeddelande när ett delat album har nya tillgÃĨngar", "album_user_left": "Lämnade {album}", @@ -440,7 +436,7 @@ "archive": "Arkiv", "archive_or_unarchive_photo": "Arkivera eller oarkivera fotot", "archive_page_no_archived_assets": "Inga arkiverade objekt hittade", - "archive_page_title": "Arkiv ({})", + "archive_page_title": "Arkiv ({count})", "archive_size": "Arkivstorlek", "archive_size_description": "Konfigurera arkivstorleken fÃļr nedladdningar (i GiB)", "archived": "Arkiverade", @@ -460,7 +456,6 @@ "asset_list_layout_settings_group_automatically": "Automatiskt", "asset_list_layout_settings_group_by": "Gruppera bilder efter", "asset_list_layout_settings_group_by_month_day": "MÃĨnad + dag", - "asset_list_layout_sub_title": "Layout", "asset_list_settings_subtitle": "Layoutinställningar fÃļr bildrutnät", "asset_list_settings_title": "Bildrutnät", "asset_offline": "TillgÃĨng offline", @@ -477,18 +472,18 @@ "assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet", "assets_added_to_name_count": "Lade till {count, plural, one {# objekt} other {# objekt}} till {hasName, select, true {{name}} other {nytt album}}", "assets_count": "{count, plural, one {# objekt} other {# objekt}}", - "assets_deleted_permanently": "{} objekt har tagits bort permanent", - "assets_deleted_permanently_from_server": "{} objekt har tagits bort permanent frÃĨn Immich-servern", + "assets_deleted_permanently": "{count} objekt har tagits bort permanent", + "assets_deleted_permanently_from_server": "{count} objekt har tagits bort permanent frÃĨn Immich-servern", "assets_moved_to_trash_count": "Flyttade {count, plural, one {# asset} other {# assets}} till papperskorgen", "assets_permanently_deleted_count": "Raderad permanent {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Tog bort {count, plural, one {# asset} other {# assets}}", - "assets_removed_permanently_from_device": "{} objekt har raderats permanent frÃĨn din enhet", + "assets_removed_permanently_from_device": "{count} objekt har raderats permanent frÃĨn din enhet", "assets_restore_confirmation": "Är du säker pÃĨ att du vill ÃĨterställa alla dina papperskorgen? Du kan inte ÃĨngra den här ÃĨtgärden! Observera att offlineobjekt inte kan ÃĨterställas pÃĨ detta sätt.", "assets_restored_count": "Återställd {count, plural, one {# asset} other {# assets}}", - "assets_restored_successfully": "{} objekt har ÃĨterställts", - "assets_trashed": "{} objekt raderade", + "assets_restored_successfully": "{count} objekt har ÃĨterställts", + "assets_trashed": "{count} objekt raderade", "assets_trashed_count": "Till Papperskorgen {count, plural, one {# asset} other {# assets}}", - "assets_trashed_from_server": "{} objekt raderade frÃĨn Immich-servern", + "assets_trashed_from_server": "{count} objekt raderade frÃĨn Immich-servern", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Asset were}} är redan en del av albumet", "authorized_devices": "Auktoriserade enheter", "automatic_endpoint_switching_subtitle": "Anslut lokalt via det angivna Wi-Fi-nätverket när det är tillgängligt och använd alternativa anslutningar pÃĨ andra platser", @@ -497,7 +492,7 @@ "back_close_deselect": "Tillbaka, stäng eller avmarkera", "background_location_permission": "TillÃĨtelse fÃļr bakgrundsplats", "background_location_permission_content": "FÃļr att kunna byta nätverk när appen kÃļrs i bakgrunden mÃĨste Immich *alltid* ha ÃĨtkomst till exakt plats sÃĨ att appen kan läsa av Wi-Fi-nätverkets namn", - "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({})", + "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({count})", "backup_album_selection_page_albums_tap": "Tryck en gÃĨng fÃļr att inkludera, tryck tvÃĨ gÃĨnger fÃļr att exkludera", "backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda Ãļver flera album. DärfÃļr kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen", "backup_album_selection_page_select_albums": "Välj album", @@ -506,37 +501,36 @@ "backup_all": "Allt", "backup_background_service_backup_failed_message": "Säkerhetskopiering av foton och videor misslyckades. FÃļrsÃļker igen...", "backup_background_service_connection_failed_message": "Anslutning till servern misslyckades. FÃļrsÃļker igen...", - "backup_background_service_current_upload_notification": "Laddar upp {}", + "backup_background_service_current_upload_notification": "Laddar upp {filename}", "backup_background_service_default_notification": "SÃļker efter nya objekt...", "backup_background_service_error_title": "Fel vid säkerhetskopiering", "backup_background_service_in_progress_notification": "Säkerhetskopierar dina foton och videor...", - "backup_background_service_upload_failure_notification": "Kunde inte ladda upp {}", + "backup_background_service_upload_failure_notification": "Kunde inte ladda upp {filename}", "backup_controller_page_albums": "Säkerhetskopiera album", "backup_controller_page_background_app_refresh_disabled_content": "Aktivera uppdatering i bakgrunden i Inställningar > Allmänt > Uppdatering I Bakgrunden fÃļr att använda säkerhetskopiering i bakgrunden.", "backup_controller_page_background_app_refresh_disabled_title": "Uppdatering i bakgrunden är avaktiverat", "backup_controller_page_background_app_refresh_enable_button_text": "GÃĨ till inställningar", "backup_controller_page_background_battery_info_link": "Visa mig hur", "backup_controller_page_background_battery_info_message": "FÃļr optimal säkerhetskopiering i bakgrunden bÃļr du stänga av batterioptimering som begränsar bakgrundsaktivitet fÃļr Immich.\n\nEftersom detta är enhetsspecifikt sÃĨ bÃļr du sÃļka instruktioner frÃĨn din enhetstillverkare.", - "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Batterioptimering", "backup_controller_page_background_charging": "Endast vid laddning", "backup_controller_page_background_configure_error": "Kunde inte konfigurera bakgrundstjänsten", - "backup_controller_page_background_delay": "Skjut upp säkerhetskopiering av nya foton och videor: {}", + "backup_controller_page_background_delay": "Skjut upp säkerhetskopiering av nya foton och videor: {duration}", "backup_controller_page_background_description": "Aktivera säkerhetskopiering i bakgrunden fÃļr att automatiskt säkerhetskopiera nya foton och videor utan att Ãļppna appen", "backup_controller_page_background_is_off": "Automatisk säkerhetskopiering i bakgrunden är avstängd", "backup_controller_page_background_is_on": "Automatisk säkerhetskopiering i bakgrunden är aktiverad", "backup_controller_page_background_turn_off": "Stäng av säkerhetskopiering i bakgrunden", "backup_controller_page_background_turn_on": "Aktivera säkerhetskopiering i bakgrunden", - "backup_controller_page_background_wifi": "Endast med WiFi", + "backup_controller_page_background_wifi": "Endast med Wi-Fi", "backup_controller_page_backup": "Säkerhetskopiera", "backup_controller_page_backup_selected": "Valt: ", "backup_controller_page_backup_sub": "Säkerhetskopierade foton och videor", - "backup_controller_page_created": "Skapad den: {}", + "backup_controller_page_created": "Skapad: {date}", "backup_controller_page_desc_backup": "Aktivera fÃļrgrunds-säkerhetskopiering fÃļr att automatiskt ladda upp nya foton och videor när du Ãļppnar appen.", "backup_controller_page_excluded": "Exkluderat: ", - "backup_controller_page_failed": "Misslyckades ({})", - "backup_controller_page_filename": "Filnamn: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_failed": "Misslyckade ({count})", + "backup_controller_page_filename": "Filnamn: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Säkerhetskopieringsinformation", "backup_controller_page_none_selected": "Ingenting valt", "backup_controller_page_remainder": "Resterande", @@ -545,7 +539,7 @@ "backup_controller_page_start_backup": "Starta säkerhetskopiering", "backup_controller_page_status_off": "Automatisk säkerhetskopiering är avstängd", "backup_controller_page_status_on": "Automatisk säkerhetskopiering är aktiverad", - "backup_controller_page_storage_format": "{} av {} använt", + "backup_controller_page_storage_format": "{used} av {total} använt", "backup_controller_page_to_backup": "Album att säkerhetskopiera", "backup_controller_page_total_sub": "Alla unika foton och videor frÃĨn valda album", "backup_controller_page_turn_off": "Stäng av automatisk säkerhetskopiering", @@ -560,6 +554,10 @@ "backup_options_page_title": "Säkerhetskopieringsinställningar", "backup_setting_subtitle": "Hantera inställningar fÃļr fÃļr- och bakgrundsuppladdning", "backward": "BakÃĨt", + "biometric_auth_enabled": "Biometrisk autentisering aktiverad", + "biometric_locked_out": "Du är utelÃĨst frÃĨn biometrisk autentisering", + "biometric_no_options": "Inga biometriska alternativ tillgängliga", + "biometric_not_available": "Biometrisk autentisering är inte tillgänglig pÃĨ denna enhet", "birthdate_saved": "FÃļdelsedatumet har sparats", "birthdate_set_description": "FÃļdelsedatum används fÃļr att beräkna ÃĨldern pÃĨ denna person vid tidpunkten fÃļr ett foto.", "blurred_background": "Suddig bakgrund", @@ -570,21 +568,21 @@ "bulk_keep_duplicates_confirmation": "Är du säker pÃĨ att du vill behÃĨlla {count, plural, one {# duplicate asset} other {# duplicate assets}}? Detta kommer att lÃļsa alla dubbletter av grupper utan att ta bort nÃĨgonting.", "bulk_trash_duplicates_confirmation": "Är du säker pÃĨ att du vill skicka till papperskorgen {count, plural, one {# duplicate asset} other {# duplicate assets}}? Detta kommer att behÃĨlla den stÃļrsta tillgÃĨngen i varje grupp och alla andra dubbletter kasseras.", "buy": "KÃļp Immich", - "cache_settings_album_thumbnails": "Miniatyrbilder fÃļr bibliotek ({} bilder och videor)", + "cache_settings_album_thumbnails": "Miniatyrbilder fÃļr bibliotek ({count} bilder och videor)", "cache_settings_clear_cache_button": "Rensa cacheminnet", "cache_settings_clear_cache_button_title": "Rensar appens cacheminne. Detta kommer att avsevärt pÃĨverka appens prestanda tills cachen har byggts om.", "cache_settings_duplicated_assets_clear_button": "RENSA", "cache_settings_duplicated_assets_subtitle": "Foton och videor som är svartlistade av appen", - "cache_settings_duplicated_assets_title": "Duplicerade Objekt ({})", - "cache_settings_image_cache_size": "Cacheminnets storlek ({} bilder och videor)", + "cache_settings_duplicated_assets_title": "Duplicerade Objekt ({count})", + "cache_settings_image_cache_size": "Cacheminnets storlek ({count} bilder och videor)", "cache_settings_statistics_album": "Miniatyrbilder fÃļr bibliotek", - "cache_settings_statistics_assets": "{} bilder och videor ({})", + "cache_settings_statistics_assets": "{count} bilder och videor ({size})", "cache_settings_statistics_full": "Hela bilder", "cache_settings_statistics_shared": "Miniatyrbilder till delat album", "cache_settings_statistics_thumbnail": "Miniatyrbilder", "cache_settings_statistics_title": "CachefÃļrbrukning", "cache_settings_subtitle": "Hantera cachebeteendet fÃļr Immich-appen.", - "cache_settings_thumbnail_size": "Storlek pÃĨ cacheminnet ({} bilder och videor)", + "cache_settings_thumbnail_size": "Storlek pÃĨ cacheminnet ({count} bilder och videor)", "cache_settings_tile_subtitle": "Kontrollera beteende fÃļr lokal lagring", "cache_settings_tile_title": "Lokal Lagring", "cache_settings_title": "Cache Inställningar", @@ -624,7 +622,6 @@ "clear_all_recent_searches": "Rensa alla senaste sÃļkningar", "clear_message": "Rensa meddelande", "clear_value": "Rensa värde", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Ange LÃļsenord", "client_cert_import": "Importera", "client_cert_import_success_msg": "Klientcertifikatet är importerat", @@ -654,7 +651,7 @@ "contain": "Anpassa", "context": "Sammanhang", "continue": "Fortsätt", - "control_bottom_app_bar_album_info_shared": "{} objekt â€ĸ Delat", + "control_bottom_app_bar_album_info_shared": "{count} objekt â€ĸ Delat", "control_bottom_app_bar_create_new_album": "Skapa nytt album", "control_bottom_app_bar_delete_from_immich": "Ta bort frÃĨn Immich", "control_bottom_app_bar_delete_from_local": "Ta bort frÃĨn enhet", @@ -746,7 +743,6 @@ "direction": "Riktning", "disabled": "Inaktiverad", "disallow_edits": "TillÃĨt inte redigeringar", - "discord": "Discord", "discover": "Upptäck", "dismiss_all_errors": "Avvisa alla fel", "dismiss_error": "Avvisa fel", @@ -763,7 +759,7 @@ "download_enqueue": "Nedladdning kÃļad", "download_error": "Fel vid nedladdning", "download_failed": "Nedladdning misslyckades", - "download_filename": "fil: {}", + "download_filename": "fil: {filename}", "download_finished": "Nedladdning klar", "download_include_embedded_motion_videos": "Inbäddade videor", "download_include_embedded_motion_videos_description": "Inkludera videor inbäddade i rÃļrliga bilder som en separat fil", @@ -805,7 +801,6 @@ "editor_close_without_save_prompt": "Ändringarna kommer inte att sparas", "editor_close_without_save_title": "Stäng redigeraren?", "editor_crop_tool_h2_aspect_ratios": "BildfÃļrhÃĨllande", - "editor_crop_tool_h2_rotation": "Rotation", "email": "Epost", "empty_folder": "Mappen är tom", "empty_trash": "TÃļm papperskorg", @@ -814,12 +809,14 @@ "enabled": "Aktiverad", "end_date": "Slutdatum", "enqueued": "KÃļad", - "enter_wifi_name": "Ange WiFi-namn", + "enter_wifi_name": "Ange Wi-Fi-namn", + "enter_your_pin_code": "Skriv in din pinkod", + "enter_your_pin_code_subtitle": "Skriv in din pinkod fÃļr att komma ÃĨt lÃĨst mapp", "error": "Fel", "error_change_sort_album": "Kunde inte ändra sorteringsordning fÃļr album", "error_delete_face": "Fel uppstod när ansikte skulle tas bort frÃĨn objektet", "error_loading_image": "Fel vid bildladdning", - "error_saving_image": "Fel: {}", + "error_saving_image": "Fel: {error}", "error_title": "Fel – nÃĨgot gick fel", "errors": { "cannot_navigate_next_asset": "Det gÃĨr inte att navigera till nästa objekt", @@ -849,6 +846,7 @@ "failed_to_keep_this_delete_others": "Misslyckades att behÃĨlla detta objekt radera Ãļvriga objekt", "failed_to_load_asset": "Det gick inte att ladda objekt", "failed_to_load_assets": "Det gick inte att ladda objekten", + "failed_to_load_notifications": "Misslyckades med att ladda notifikationer", "failed_to_load_people": "Det gick inte att ladda personer", "failed_to_remove_product_key": "Det gick inte att ta bort produktnyckeln", "failed_to_stack_assets": "Det gick inte att stapla objekt", @@ -870,6 +868,7 @@ "unable_to_archive_unarchive": "Det gÃĨr inte att {archived, select, true {archive} other {archive}}", "unable_to_change_album_user_role": "Kunde inte ändra albumanvändarens roll", "unable_to_change_date": "Kunde inte ändra datum", + "unable_to_change_description": "Kunde inte ändra beskrivning", "unable_to_change_favorite": "Det gÃĨr inte att ändra favorit fÃļr objekt", "unable_to_change_location": "Kunde inte ändra plats", "unable_to_change_password": "Det gÃĨr inte att ändra lÃļsenord", @@ -907,6 +906,7 @@ "unable_to_log_out_all_devices": "Det gick inte att logga ut alla enheter", "unable_to_log_out_device": "Det gick inte att logga ut enheten", "unable_to_login_with_oauth": "Det gick inte att logga in med OAuth", + "unable_to_move_to_locked_folder": "Kunde inte flytta till lÃĨst mapp", "unable_to_play_video": "Kunde inte spela upp video", "unable_to_reassign_assets_existing_person": "Det gÃĨr inte att tilldela om tillgÃĨngar till {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "Kunde inte tilldela objekt till en annan person", @@ -920,6 +920,7 @@ "unable_to_remove_reaction": "Kunde inte ta bort reaktion", "unable_to_repair_items": "kunde inte reparera objekt", "unable_to_reset_password": "Kunde inte ÃĨterställa lÃļsenord", + "unable_to_reset_pin_code": "Kunde inte ÃĨterställa pinkod", "unable_to_resolve_duplicate": "Det gÃĨr inte att lÃļsa dubbletter", "unable_to_restore_assets": "Det gÃĨr inte att ÃĨterställa tillgÃĨngar", "unable_to_restore_trash": "Det gick inte att ÃĨterställa papperskorgen", @@ -947,16 +948,15 @@ "unable_to_update_user": "Kunde inte uppdatera användare", "unable_to_upload_file": "Det gÃĨr inte att ladda upp filen" }, - "exif": "Exif", "exif_bottom_sheet_description": "Lägg till beskrivning...", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLATS", "exif_bottom_sheet_people": "PERSONER", "exif_bottom_sheet_person_add_person": "Lägg till namn", - "exif_bottom_sheet_person_age": "Ålder {}", - "exif_bottom_sheet_person_age_months": "Ålder {} mÃĨnader", - "exif_bottom_sheet_person_age_year_months": "Ålder 1 ÃĨr, {} mÃĨnader", - "exif_bottom_sheet_person_age_years": "Ålder {}", + "exif_bottom_sheet_person_age": "Ålder {age}", + "exif_bottom_sheet_person_age_months": "Ålder {months} mÃĨnader", + "exif_bottom_sheet_person_age_year_months": "Ålder 1 ÃĨr, {months} mÃĨnader", + "exif_bottom_sheet_person_age_years": "Ålder {years}", "exit_slideshow": "Avsluta bildspel", "expand_all": "Expandera alla", "experimental_settings_new_asset_list_subtitle": "Under uppbyggnad", @@ -974,9 +974,10 @@ "external": "Externt", "external_libraries": "Externa Bibliotek", "external_network": "Externt nätverk", - "external_network_sheet_info": "När appen inte är ansluten till det fÃļredragna WiFi-nätverket, kommer den att ansluta till servern via den fÃļrsta av fÃļljande URL:er den kan nÃĨ, frÃĨn toppen till botten", + "external_network_sheet_info": "När appen inte är ansluten till det fÃļredragna Wi-Fi-nätverket, kommer den att ansluta till servern via den fÃļrsta av fÃļljande URL:er den kan nÃĨ, frÃĨn toppen till botten", "face_unassigned": "Otilldelade", "failed": "Misslyckades", + "failed_to_authenticate": "Misslyckades med autentisering", "failed_to_load_assets": "Det gick inte att läsa in resurser", "failed_to_load_folder": "Kunde inte ladda mappen", "favorite": "Favorit", @@ -990,8 +991,8 @@ "file_name_or_extension": "Filnamn eller -tillägg", "filename": "Filnamn", "filetype": "Filtyp", - "filter": "Filter", "filter_people": "Filtrera personer", + "filter_places": "Filtrera platser", "find_them_fast": "Hitta dem snabbt efter namn med sÃļk", "fix_incorrect_match": "Fixa inkorrekt matchning", "folder": "Mapp", @@ -1062,7 +1063,6 @@ "image_viewer_page_state_provider_download_started": "Nedladdning PÃĨbÃļrjad", "image_viewer_page_state_provider_download_success": "Nedladdningen Lyckades", "image_viewer_page_state_provider_share_error": "Delningsfel", - "immich_logo": "Immich Logo", "immich_web_interface": "Immich Web gränssnitt", "import_from_json": "Importera frÃĨn JSON", "import_path": "ImportsÃļkväg", @@ -1136,7 +1136,6 @@ "login_form_back_button_text": "BakÃĨt", "login_form_email_hint": "din.email@email.com", "login_form_endpoint_hint": "http://din-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", "login_form_err_http": "Var god ange http:// eller https://", "login_form_err_invalid_email": "Ogiltig email", "login_form_err_invalid_url": "Ogiltig webbadress", @@ -1170,8 +1169,8 @@ "manage_your_devices": "Hantera dina inloggade enheter", "manage_your_oauth_connection": "Hantera din OAuth-anslutning", "map": "Karta", - "map_assets_in_bound": "{} foto", - "map_assets_in_bounds": "{} foton", + "map_assets_in_bound": "{count} foto", + "map_assets_in_bounds": "{count} foton", "map_cannot_get_user_location": "Kan inte hämta användarens plats", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Använd den här platsen", @@ -1185,9 +1184,9 @@ "map_settings": "Kartinställningar", "map_settings_dark_mode": "MÃļrkt tema", "map_settings_date_range_option_day": "Senaste 24 timmarna", - "map_settings_date_range_option_days": "Senaste {} dagarna", + "map_settings_date_range_option_days": "Senaste {days} dagarna", "map_settings_date_range_option_year": "Senaste ÃĨret", - "map_settings_date_range_option_years": "Senaste {} ÃĨren", + "map_settings_date_range_option_years": "Senaste {years} ÃĨren", "map_settings_dialog_title": "Kartinställningar", "map_settings_include_show_archived": "Inkludera Arkiverade", "map_settings_include_show_partners": "Inkludera Partners", @@ -1202,8 +1201,6 @@ "memories_setting_description": "Hantera det du ser i dina minnen", "memories_start_over": "BÃļrja Om", "memories_swipe_to_close": "Svep upp fÃļr att stänga", - "memories_year_ago": "Ett ÃĨr sedan", - "memories_years_ago": "{} ÃĨr sedan", "memory": "Minne", "memory_lane_title": "Återupplev {title}", "menu": "Meny", @@ -1218,7 +1215,6 @@ "missing": "Saknade", "model": "Modell", "month": "MÃĨnad", - "monthly_title_text_date_format": "MMMM y", "more": "Mer", "moved_to_trash": "Flyttad till papperskorgen", "multiselect_grid_edit_date_time_err_read_only": "Kan inte ändra datum pÃĨ skrivskyddade objekt, hoppar Ãļver", @@ -1267,7 +1263,6 @@ "notification_toggle_setting_description": "Aktivera e-postaviseringar", "notifications": "Notifikationer", "notifications_setting_description": "Hantera aviseringar", - "oauth": "OAuth", "official_immich_resources": "Officiella Immich-resurser", "offline": "FrÃĨnkopplad", "offline_paths": "Offlinevägar", @@ -1288,13 +1283,11 @@ "options": "Val", "or": "eller", "organize_your_library": "Organisera ditt bibliotek", - "original": "original", "other": "Övrigt", "other_devices": "Andra enheter", "other_variables": "Andra variabler", "owned": "Ägd", "owner": "Ägare", - "partner": "Partner", "partner_can_access": "{partner} har ÃĨtkomst", "partner_can_access_assets": "Alla dina foton och videoklipp fÃļrutom de i Arkiverade och Raderade", "partner_can_access_location": "Platsen där dina foton togs", @@ -1305,9 +1298,8 @@ "partner_page_partner_add_failed": "Misslyckades med att lägga till partner", "partner_page_select_partner": "Välj partner", "partner_page_shared_to_title": "Delad till", - "partner_page_stop_sharing_content": "{} kommer inte längre att komma ÃĨt dina foton.", + "partner_page_stop_sharing_content": "{partner} kommer inte längre att komma ÃĨt dina foton.", "partner_sharing": "Partnerdelning", - "partners": "Partners", "password": "LÃļsenord", "password_does_not_match": "LÃļsenorden stämmer inte Ãļverens", "password_required": "LÃļsenord krävs", @@ -1319,7 +1311,6 @@ }, "path": "SÃļkväg", "pattern": "MÃļnster", - "pause": "Pause", "pause_memories": "Pausa minnen", "paused": "Pausad", "pending": "Väntande", @@ -1342,7 +1333,6 @@ "permission_onboarding_permission_granted": "Rättigheten beviljad! Du är klar.", "permission_onboarding_permission_limited": "Rättighet begränsad. FÃļr att lÃĨta Immich säkerhetskopiera och hantera hela ditt galleri, tillÃĨt foto- och video-rättigheter i Inställningar.", "permission_onboarding_request": "Immich kräver tillstÃĨnd fÃļr att se dina foton och videor.", - "person": "Person", "person_birthdate": "FÃļdd {date}", "person_hidden": "{name}{hidden, select, true { (dold)} other {}}", "photo_shared_all_users": "Du har antingen delat dina foton med alla användare eller sÃĨ har du inga användare att dela dem med.", @@ -1358,7 +1348,6 @@ "play_memories": "Spela upp minnen", "play_motion_photo": "Spela upp rÃļrligt foto", "play_or_pause_video": "Spela upp eller pausa video", - "port": "Port", "preferences_settings_subtitle": "Hantera appens inställningar", "preferences_settings_title": "Inställningar", "preset": "FÃļrinställt värde", @@ -1372,16 +1361,14 @@ "profile_drawer_client_out_of_date_major": "Mobilappen är utdaterad. Uppdatera till senaste huvudversionen.", "profile_drawer_client_out_of_date_minor": "Mobilappen är utdaterad. Uppdatera till senaste mindre versionen.", "profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Servern är utdaterad. Uppdatera till senaste huvudversionen.", "profile_drawer_server_out_of_date_minor": "Servern är utdaterad. Uppdatera till senaste mindre versionen.", "profile_image_of_user": "{user} profilbild", "profile_picture_set": "Profilbild vald.", "public_album": "Publikt album", "public_share": "Offentlig delning", - "purchase_account_info": "Supporter", "purchase_activated_subtitle": "Tack fÃļr att du stÃļdjer Immich och open source-mjukvara", - "purchase_activated_time": "Aktiverad {date, date}", + "purchase_activated_time": "Aktiverad {date}", "purchase_activated_title": "Aktiveringan av din nyckel lyckades", "purchase_button_activate": "Aktivera", "purchase_button_buy": "KÃļp", @@ -1401,7 +1388,6 @@ "purchase_panel_info_1": "Att bygga Immich kräver mycket tid och engagemang och vÃĨra tekniker jobbar heltid fÃļr att gÃļra det sÃĨ bra som vi mÃļjligt kan. VÃĨrt mÃĨl är att open source-mjukvara och etiska affärsmetoder ska bli en hÃĨllbar inkomstkälla fÃļr utvecklare och att skapa ett ekosystem som repekterar personlig integritet med verkliga alternativ till exploaterande molntjänster.", "purchase_panel_info_2": "DÃĨ vi ÃĨtagit oss att inte ha betalväggar kommer detta kÃļp inte att ge dig nÃĨgra utÃļkade funktioner i Immich. Vi sätter vÃĨr tillit till användare som du som stÃļdjer Immichs fortsatta utveckling.", "purchase_panel_title": "StÃļd projektet", - "purchase_per_server": "Per server", "purchase_per_user": "Per användare", "purchase_remove_product_key": "Ta bort produktnyckel", "purchase_remove_product_key_prompt": "Vill du verkligen ta bort produktnyckeln?", @@ -1409,7 +1395,6 @@ "purchase_remove_server_product_key_prompt": "Är du säker pÃĨ att du vill ta bort serverns produktnyckel?", "purchase_server_description_1": "FÃļr hela servern", "purchase_server_description_2": "Supporterstatus", - "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnyckeln fÃļr servern hanteras av administratÃļren", "rating": "Antal stjärnor", "rating_clear": "Ta bort betyg", @@ -1529,7 +1514,6 @@ "search_page_no_places": "Ingen platsinformation finns tillgänglig", "search_page_screenshots": "Skärmdumpar", "search_page_search_photos_videos": "SÃļk efter dina foton och videor", - "search_page_selfies": "Selfies", "search_page_things": "Objekt", "search_page_view_all_button": "Visa alla", "search_page_your_activity": "Dina aktiviteter", @@ -1590,12 +1574,12 @@ "setting_languages_apply": "Verkställ", "setting_languages_subtitle": "Ändra appens sprÃĨk", "setting_languages_title": "SprÃĨk", - "setting_notifications_notify_failures_grace_period": "Rapportera säkerhetskopieringsfel i bakgrunden: {}", - "setting_notifications_notify_hours": "{} timmar", + "setting_notifications_notify_failures_grace_period": "Rapportera säkerhetskopieringsfel i bakgrunden: {duration}", + "setting_notifications_notify_hours": "{count} timmar", "setting_notifications_notify_immediately": "omedelbart", - "setting_notifications_notify_minutes": "{} minuter", + "setting_notifications_notify_minutes": "{count} minuter", "setting_notifications_notify_never": "aldrig", - "setting_notifications_notify_seconds": "{} sekunder", + "setting_notifications_notify_seconds": "{count} sekunder", "setting_notifications_single_progress_subtitle": "Detaljerad uppladdningsstatus per bild och video", "setting_notifications_single_progress_title": "Visa detaljerat uppladdningsfÃļrlopp", "setting_notifications_subtitle": "Anpassa dina notis-inställningar", @@ -1609,7 +1593,7 @@ "settings_saved": "Inställningar sparade", "share": "Dela", "share_add_photos": "Lägg till foton", - "share_assets_selected": "{} valda", + "share_assets_selected": "{count} valda", "share_dialog_preparing": "FÃļrbereder...", "shared": "Delad", "shared_album_activities_input_disable": "Kommentar är inaktiverad", @@ -1623,34 +1607,33 @@ "shared_by_user": "Delad av {user}", "shared_by_you": "Delad av dig", "shared_from_partner": "Foton frÃĨn {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uppladdat", + "shared_intent_upload_button_progress_text": "{current} / {total} Uppladdat", "shared_link_app_bar_title": "Delade Länkar", "shared_link_clipboard_copied_massage": "Kopierad till urklipp", - "shared_link_clipboard_text": "Länk: {}\nLÃļsenord: {}", + "shared_link_clipboard_text": "Länk: {link}\nLÃļsenord: {password}", "shared_link_create_error": "Fel vid skapande av delad länk", "shared_link_edit_description_hint": "Lägg till delnings-beskrivningen", "shared_link_edit_expire_after_option_day": "1 dag", - "shared_link_edit_expire_after_option_days": "{} dagar", + "shared_link_edit_expire_after_option_days": "{count} dagar", "shared_link_edit_expire_after_option_hour": "1 timme", - "shared_link_edit_expire_after_option_hours": "{} timmar", + "shared_link_edit_expire_after_option_hours": "{count} timmar", "shared_link_edit_expire_after_option_minute": "1 minut", - "shared_link_edit_expire_after_option_minutes": "{} minuter", - "shared_link_edit_expire_after_option_months": "{} mÃĨnader", - "shared_link_edit_expire_after_option_year": "{} ÃĨr", + "shared_link_edit_expire_after_option_minutes": "{count} minuter", + "shared_link_edit_expire_after_option_months": "{count} mÃĨnader", + "shared_link_edit_expire_after_option_year": "{count} ÃĨr", "shared_link_edit_password_hint": "Ange delningslÃļsenordet", "shared_link_edit_submit_button": "Uppdatera länk", "shared_link_error_server_url_fetch": "Kan inte hämta server-urlen", - "shared_link_expires_day": "GÃĨr ut om {} dag", - "shared_link_expires_days": "GÃĨr ut om {} dagar", - "shared_link_expires_hour": "GÃĨr ut om {} timme", - "shared_link_expires_hours": "GÃĨr ut om {} timmar", - "shared_link_expires_minute": "GÃĨr ut om {} minut", - "shared_link_expires_minutes": "GÃĨr ut om {} minuter", + "shared_link_expires_day": "GÃĨr ut om {count} dag", + "shared_link_expires_days": "GÃĨr ut om {count} dagar", + "shared_link_expires_hour": "GÃĨr ut om {count} timme", + "shared_link_expires_hours": "GÃĨr ut om {count} timmar", + "shared_link_expires_minute": "GÃĨr ut om {count} minut", + "shared_link_expires_minutes": "GÃĨr ut om {count} minuter", "shared_link_expires_never": "GÃĨr ut ∞", - "shared_link_expires_second": "GÃĨr ut om {} sekunder", - "shared_link_expires_seconds": "GÃĨr ut om {} sekunder", + "shared_link_expires_second": "GÃĨr ut om {count} sekunder", + "shared_link_expires_seconds": "GÃĨr ut om {count} sekunder", "shared_link_individual_shared": "Individdelad", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Hantera Delade länkar", "shared_link_options": "Alternativ fÃļr delad länk", "shared_links": "Delade Länkar", @@ -1716,7 +1699,6 @@ "start": "Starta", "start_date": "Startdatum", "state": "Stat", - "status": "Status", "stop_motion_photo": "Stanna rÃļrligt foto", "stop_photo_sharing": "Sluta dela dina foton?", "stop_photo_sharing_description": "{partner} kommer inte länga ha tillgÃĨng till dina foton.", @@ -1727,8 +1709,6 @@ "submit": "Skicka", "suggestions": "FÃļrslag", "sunrise_on_the_beach": "SoluppgÃĨng pÃĨ stranden", - "support": "Support", - "support_and_feedback": "Support & Feedback", "support_third_party_description": "Din Immich-installation paketerades av en tredje part. Problem som du upplever kan orsakas av det paketet, sÃĨ vänligen ta upp problem med dem i fÃļrsta hand med hjälp av länkarna nedan.", "swap_merge_direction": "Byt sammanfogningsriktning", "sync": "Synka", @@ -1749,7 +1729,7 @@ "theme_selection": "Val av tema", "theme_selection_description": "Ställ in temat automatiskt till ljust eller mÃļrkt baserat pÃĨ din webbläsares inställningar", "theme_setting_asset_list_storage_indicator_title": "Visa lagringsindikator pÃĨ filer", - "theme_setting_asset_list_tiles_per_row_title": "Antal bilder och videor per rad ({})", + "theme_setting_asset_list_tiles_per_row_title": "Antal bilder och videor per rad ({count})", "theme_setting_colorful_interface_subtitle": "Applicera primärfärgen pÃĨ bakgrundsytor.", "theme_setting_colorful_interface_title": "Färgglatt gränssnitt", "theme_setting_image_viewer_quality_subtitle": "Justera kvaliteten i bildvisaren", @@ -1774,7 +1754,6 @@ "to_trash": "Papperskorg", "toggle_settings": "Växla inställningar", "toggle_theme": "Växla tema", - "total": "Total", "total_usage": "Total användning", "trash": "Papperskorg", "trash_all": "Kasta alla", @@ -1784,11 +1763,11 @@ "trash_no_results_message": "Borttagna foton och videor kommer att visas här.", "trash_page_delete_all": "Ta Bort Alla", "trash_page_empty_trash_dialog_content": "Vill du ta bort dina slängda objekt? De kommer att tas bort permanent frÃĨn Immich", - "trash_page_info": "Objekt i papperskorgen tas bort permanent efter {} dagar", + "trash_page_info": "Objekt i papperskorgen tas bort permanent efter {days} dagar", "trash_page_no_assets": "Inga slängda objekt", "trash_page_restore_all": "Återställ Alla", "trash_page_select_assets_btn": "Välj objekt", - "trash_page_title": "Papperskorg ({})", + "trash_page_title": "Papperskorg ({count})", "trashed_items_will_be_permanently_deleted_after": "Objekt i papperskorgen raderas permanent efter {days, plural, one {# dag} other {# dagar}}.", "type": "Typ", "unarchive": "Ångra arkivering", @@ -1826,9 +1805,8 @@ "upload_status_errors": "Fel", "upload_status_uploaded": "Uppladdad", "upload_success": "Uppladdning lyckades, ladda om sidan fÃļr att se nya objekt.", - "upload_to_immich": "Ladda upp till Immich ({})", + "upload_to_immich": "Ladda upp till Immich ({count})", "uploading": "Laddar upp", - "url": "URL", "usage": "Användning", "use_current_connection": "Använd aktuell anslutning", "use_custom_date_range": "Använd anpassat datumintervall istället", @@ -1847,7 +1825,6 @@ "validate": "Validera", "validate_endpoint_error": "Ange en giltig URL", "variables": "Variabler", - "version": "Version", "version_announcement_closing": "Din vän, Alex", "version_announcement_message": "Hej där! En ny version av Immich är tillgänglig. Ta dig tid att läsa versionsfakta fÃļr att säkerställa att dina inställningar är uppdaterade fÃļr att fÃļrhindra eventuella felkonfigurationer, särskilt om du använder WatchTower eller nÃĨgon mekanism som hanterar uppdatering av din Immich instans automatiskt.", "version_announcement_overlay_release_notes": "versionsinformation", @@ -1857,7 +1834,6 @@ "version_announcement_overlay_title": "Ny server-version finns tillgänglig 🎉", "version_history": "Versionshistorik", "version_history_item": "Version {version} installerad {date}", - "video": "Video", "video_hover_setting": "Spela upp videotumnagel när muspekaren är Ãļver den", "video_hover_setting_description": "Spela upp videotumnagel när muspekaren är Ãļver den. Även när den är deaktiverad kan uppspelning startas när muspekaren är Ãļver play-ikonen.", "videos": "Videor", diff --git a/i18n/ta.json b/i18n/ta.json index 224277f68c..679d35ec46 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -180,20 +180,13 @@ "oauth_auto_register": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽĩ❁", "oauth_auto_register_description": "OAuth āŽ‰āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¨ā¯āŽ¤ āŽĒāŽŋāŽąāŽ•ā¯ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", "oauth_button_text": "āŽĒāŽŸā¯āŽŸāŽŠā¯ āŽ‰āŽ°ā¯ˆ", - "oauth_client_id": "āŽĩāŽžāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ āŽāŽŸāŽŋ", - "oauth_client_secret": "āŽĩāŽžāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ āŽ°āŽ•āŽšāŽŋāŽ¯āŽŽā¯", "oauth_enable_description": "OAuth āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ•", - "oauth_issuer_url": "āŽĩāŽ´āŽ™ā¯āŽ•ā¯āŽĒāŽĩāŽ°ā¯ URL", "oauth_mobile_redirect_uri": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI", "oauth_mobile_redirect_uri_override": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI āŽŽā¯‡āŽ˛ā¯†āŽ´ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯", "oauth_mobile_redirect_uri_override_description": "'app.immich:/' āŽ¤āŽĩāŽąāŽžāŽŠ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI āŽ†āŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", - "oauth_profile_signing_algorithm": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ° āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽŽā¯ āŽĩāŽ´āŽŋāŽŽā¯āŽąā¯ˆ", - "oauth_profile_signing_algorithm_description": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽĩāŽ´āŽŋāŽŽā¯āŽąā¯ˆ.", - "oauth_scope": "āŽĩāŽžāŽ¯ā¯āŽĒā¯āŽĒ❁", "oauth_settings": "Oauth", "oauth_settings_description": "OAuth āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "oauth_settings_more_details": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽąā¯āŽąāŽŋāŽ¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯, āŽŸāŽžāŽ•ā¯āŽ¸ā¯ āŽāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", - "oauth_signing_algorithm": "āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽŽā¯ āŽĩāŽ´āŽŋāŽŽā¯āŽąā¯ˆ", "oauth_storage_label_claim": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ˛ā¯‡āŽĒāŽŋāŽŗā¯ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯", "oauth_storage_label_claim_description": "āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ˛ā¯‡āŽĒāŽŋāŽŗā¯ˆ āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛āŽŋāŽŠā¯ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•ā¯āŽ•ā¯ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "oauth_storage_quota_claim": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯", @@ -308,7 +301,6 @@ "transcoding_reference_frames_description": "āŽ•ā¯ŠāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽŸā¯āŽŸāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸ āŽĩā¯‡āŽŖā¯āŽŸāŽŋāŽ¯ āŽĒāŽŋāŽ°ā¯‡āŽŽā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ. āŽ…āŽ¤āŽŋāŽ• āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯†āŽ¤ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ. 0 āŽ‡āŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❈ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_required_description": "āŽāŽąā¯āŽąā¯āŽ•ā¯āŽ•ā¯ŠāŽŗā¯āŽŗāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩāŽŸāŽŋāŽĩāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛āŽžāŽ¤ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", "transcoding_settings": "āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", - "transcoding_settings_description": "", "transcoding_target_resolution": "āŽ‡āŽ˛āŽ•ā¯āŽ•ā¯ āŽ¤ā¯€āŽ°ā¯āŽŽāŽžāŽŠāŽŽā¯", "transcoding_target_resolution_description": "āŽ…āŽ¤āŽŋāŽ• āŽ¤ā¯€āŽ°ā¯āŽŽāŽžāŽŠāŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽ¤āŽŋāŽ• āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽ…āŽ¤āŽŋāŽ• āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•ā¯āŽŽā¯, āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽŗāŽĩā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽŽāŽąā¯āŽŽā¯ŠāŽ´āŽŋāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯āŽąā¯ˆāŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "transcoding_temporal_aq": "āŽ¤āŽŽā¯āŽĒā¯‹āŽ°ā¯āŽ˛ā¯", @@ -321,7 +313,6 @@ "transcoding_transcode_policy_description": "āŽ’āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ•ā¯ŠāŽŗā¯āŽ•ā¯ˆ. āŽŽāŽšā¯.āŽŸāŽŋ.āŽ†āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ (āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ āŽ¤āŽĩāŽŋāŽ°).", "transcoding_two_pass_encoding": "āŽ‡āŽ°āŽŖā¯āŽŸā¯-āŽĒāŽžāŽšā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯", "transcoding_two_pass_encoding_setting_description": "āŽšāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽ‡āŽ°āŽŖā¯āŽŸā¯ āŽĒāŽžāŽšā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯. āŽŽā¯‡āŽ•ā¯āŽšā¯ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ (H.264 āŽŽāŽąā¯āŽąā¯āŽŽā¯ HEVC āŽ‰āŽŸāŽŠā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯ āŽ‡āŽ¤ā¯ āŽ¤ā¯‡āŽĩ❈āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯), āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯āŽŸā¯ˆ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽžāŽ•āŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽĩāŽ°āŽŽā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąāŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ CRF āŽ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. VP9 āŽāŽĒā¯ āŽĒā¯ŠāŽąā¯āŽ¤ā¯āŽ¤āŽĩāŽ°ā¯ˆ, āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ CRF āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛āŽžāŽŽā¯.", - "transcoding_video_codec": "", "transcoding_video_codec_description": "VP9 āŽ…āŽ¤āŽŋāŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩāŽ˛ā¯ˆ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¤āŽŠā¯āŽŽā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽąā¯āŽ•ā¯ āŽ…āŽ¤āŽŋāŽ• āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•ā¯āŽŽā¯. HEVC āŽ‡āŽ¤ā¯‡āŽĒā¯‹āŽ˛ā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤ āŽĩāŽ˛ā¯ˆ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¤āŽŠā¯āŽŽā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. H.264 āŽĒāŽ°āŽĩāŽ˛āŽžāŽ• āŽ‡āŽŖāŽ•ā¯āŽ•āŽŽāŽžāŽŠāŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽĩāŽŋāŽ°ā¯ˆāŽĩāŽžāŽŠāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŽāŽŋāŽ•āŽĒā¯ āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ.āŽĩāŽŋ 1 āŽŽāŽŋāŽ•āŽĩā¯āŽŽā¯ āŽ¤āŽŋāŽąāŽŽā¯ˆāŽ¯āŽžāŽŠ āŽ•ā¯‹āŽŸā¯†āŽ•ā¯ āŽ†āŽŠāŽžāŽ˛ā¯ āŽĒāŽ´ā¯ˆāŽ¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽ¤āŽĩāŽŋ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ.", "trash_enabled_description": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "trash_number_of_days": "āŽ¨āŽžāŽŸā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", @@ -719,7 +710,6 @@ "unable_to_update_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_upload_file": "āŽ•ā¯‹āŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ" }, - "exif": "Exif", "exit_slideshow": "āŽšā¯āŽ˛ā¯ˆāŽŸā¯āŽšā¯‹āŽĩāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąāŽĩā¯āŽŽā¯", "expand_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽŋāŽ°āŽŋāŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", "expire_after": "āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", @@ -991,7 +981,7 @@ "public_share": "āŽĒā¯ŠāŽ¤ā¯ āŽĒāŽ™ā¯āŽ•ā¯", "purchase_account_info": "āŽ†āŽ¤āŽ°āŽĩāŽžāŽŗāŽ°ā¯", "purchase_activated_subtitle": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¤āŽŋāŽąāŽ¨ā¯āŽ¤ āŽŽā¯‚āŽ˛ āŽŽā¯†āŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆ āŽ†āŽ¤āŽ°āŽŋāŽ¤ā¯āŽ¤āŽ¤āŽąā¯āŽ•ā¯ āŽ¨āŽŠā¯āŽąāŽŋ", - "purchase_activated_time": "{āŽ¤ā¯‡āŽ¤āŽŋ, āŽ¤ā¯‡āŽ¤āŽŋ} āŽ‡āŽ˛ā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "purchase_activated_time": "{date} āŽ‡āŽ˛ā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "purchase_activated_title": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽŋāŽąāŽĩā¯āŽ•ā¯‹āŽ˛ā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "purchase_button_activate": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯", "purchase_button_buy": "āŽĩāŽžāŽ™ā¯āŽ•", diff --git a/i18n/te.json b/i18n/te.json index a46773d36f..52b503cc6f 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -183,20 +183,13 @@ "oauth_auto_register": "ā°¸āąā°ĩāą€ā°¯ ā°¨ā°Žāą‹ā°Ļāą", "oauth_auto_register_description": "OAuth ā°¤āą‹ ā°¸āąˆā°¨āą ā°‡ā°¨āą ā°šāą‡ā°¸ā°ŋā°¨ ā°¤ā°°āąā°ĩā°žā°¤ ā°•āąŠā°¤āąā°¤ ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āąā°˛ā°¨āą ā°¸āąā°ĩā°¯ā°‚ā°šā°žā°˛ā°•ā°‚ā°—ā°ž ā°¨ā°Žāą‹ā°Ļāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "oauth_button_text": "ā°Ŧā°Ÿā°¨āą ā°ĩā°šā°¨ā°‚", - "oauth_client_id": "ā°•āąā°˛ā°¯ā°ŋā°‚ā°Ÿāą ID", - "oauth_client_secret": "ā°•āąā°˛ā°¯ā°ŋā°‚ā°Ÿāą ā°°ā°šā°¸āąā°¯ā°‚", "oauth_enable_description": "OAuth ā°¤āą‹ ā°˛ā°žā°—ā°ŋā°¨āą ā°…ā°ĩāąā°ĩā°‚ā°Ąā°ŋ", - "oauth_issuer_url": "ā°œā°žā°°āą€ā°Ļā°žā°°āą URL", "oauth_mobile_redirect_uri": "ā°ŽāąŠā°Ŧāąˆā°˛āą ā°Ļā°žā°°ā°ŋā°Žā°žā°°āąā°Ēāą URI", "oauth_mobile_redirect_uri_override": "ā°ŽāąŠā°Ŧāąˆā°˛āą ā°Ļā°žā°°ā°ŋā°Žā°žā°°āąā°Ēāą URI ā°“ā°ĩā°°āąâ€Œā°°āąˆā°Ąāą", "oauth_mobile_redirect_uri_override_description": "OAuth ā°Ēāąā°°āąŠā°ĩāąˆā°Ąā°°āą '{callback}'ā°ĩā°‚ā°Ÿā°ŋ ā°ŽāąŠā°Ŧāąˆā°˛āą URIā°¨ā°ŋ ā°…ā°¨āąā°Žā°¤ā°ŋā°‚ā°šā°¨ā°Ēāąā°Ēāąā°Ąāą ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", - "oauth_profile_signing_algorithm": "ā°Ēāąā°°āąŠā°Ģāąˆā°˛āą ⰏⰂⰤⰕⰂ ā°…ā°˛āąā°—āą‹ā°°ā°ŋā°Ĩā°‚", - "oauth_profile_signing_algorithm_description": "ā°ĩā°ŋā°¨ā°ŋā°¯āą‹ā°—ā°Ļā°žā°°āą ā°Ēāąā°°āąŠā°Ģāąˆā°˛āąâ€Œā°Ēāąˆ ⰏⰂⰤⰕⰂ ā°šāą‡ā°¯ā°Ąā°žā°¨ā°ŋā°•ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šāą‡ ā°…ā°˛āąā°—āą‹ā°°ā°ŋā°Ĩā°‚.", - "oauth_scope": "ā°Ēā°°ā°ŋā°§ā°ŋ", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth ā°˛ā°žā°—ā°ŋā°¨āą ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "oauth_settings_more_details": "Ⰸ ā°Ģāą€ā°šā°°āą ā°—āąā°°ā°ŋā°‚ā°šā°ŋ ā°Žā°°ā°ŋā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°ĩā°°ā°žā°˛ ā°•āą‹ā°¸ā°‚, ā°Ąā°žā°•āąā°¸āą ā°šāą‚ā°Ąā°‚ā°Ąā°ŋ.", - "oauth_signing_algorithm": "ⰏⰂⰤⰕⰂ ā°…ā°˛āąā°—āą‹ā°°ā°ŋā°Ĩā°‚", "oauth_storage_label_claim": "ā°¨ā°ŋā°˛āąā°ĩ ā°˛āą‡ā°Ŧāąā°˛āą ā°•āąā°˛āą†ā°¯ā°ŋā°Žāą", "oauth_storage_label_claim_description": "ā°¯āą‚ā°œā°°āą ā°¯āąŠā°•āąā°• ā°¨ā°ŋā°˛āąā°ĩ ā°˛āą‡ā°Ŧāąā°˛āąâ€Œā°¨āą Ⰸ ā°•āąā°˛āą†ā°¯ā°ŋā°Žāą ā°ĩā°ŋā°˛āąā°ĩā°•āą ā°¸āąā°ĩā°¯ā°‚ā°šā°žā°˛ā°•ā°‚ā°—ā°ž ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ.", "oauth_storage_quota_claim": "ā°¨ā°ŋā°˛āąā°ĩ ā°•āą‹ā°Ÿā°ž ā°•āąā°˛āą†ā°¯ā°ŋā°Žāą", @@ -926,7 +919,6 @@ "notification_toggle_setting_description": "ā°‡ā°Žāą†ā°¯ā°ŋā°˛āą ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āąâ€Œā°˛ā°¨āą ā°Ēāąā°°ā°žā°°ā°‚ā°­ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "notifications": "ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āąâ€Œā°˛āą", "notifications_setting_description": "ā°¨āą‹ā°Ÿā°ŋā°Ģā°ŋā°•āą‡ā°ˇā°¨āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", - "oauth": "OAuth", "official_immich_resources": "ā°…ā°§ā°ŋā°•ā°žā°°ā°ŋā°• ā°‡ā°Žāąā°Žā°ŋā°šāą ā°ĩā°¨ā°°āąā°˛āą", "offline": "ā°†ā°Ģāąâ€Œā°˛āąˆā°¨āą", "offline_paths": "ā°†ā°Ģāąâ€Œā°˛āąˆā°¨āą ā°Ēā°žā°¤āąâ€Œā°˛āą", @@ -1013,7 +1005,7 @@ "public_share": "ā°Ēā°Ŧāąā°˛ā°ŋā°•āą ā°ˇāą‡ā°°āą", "purchase_account_info": "ā°Žā°Ļāąā°Ļā°¤āąā°Ļā°žā°°āą", "purchase_activated_subtitle": "Immich ā°Žā°°ā°ŋā°¯āą ā°“ā°Ēāą†ā°¨āą ā°¸āą‹ā°°āąā°¸āą ā°¸ā°žā°Ģāąā°Ÿāąâ€Œā°ĩāą‡ā°°āąâ€Œā°˛ā°•āą ā°Žā°Ļāąā°Ļā°¤āą ā°‡ā°šāąā°šā°ŋⰍⰂā°Ļāąā°•āą ā°§ā°¨āąā°¯ā°ĩā°žā°Ļā°žā°˛āą", - "purchase_activated_time": "{date, date}ā°¨ ā°¯ā°žā°•āąā°Ÿā°ŋā°ĩāą‡ā°Ÿāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", + "purchase_activated_time": "{date}ā°¨ ā°¯ā°žā°•āąā°Ÿā°ŋā°ĩāą‡ā°Ÿāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "purchase_activated_title": "ā°Žāą€ ā°•āą€ ā°ĩā°ŋⰜⰝā°ĩā°‚ā°¤ā°‚ā°—ā°ž ā°¯ā°žā°•āąā°Ÿā°ŋā°ĩāą‡ā°Ÿāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "purchase_button_activate": "ā°¯ā°žā°•āąā°Ÿā°ŋā°ĩāą‡ā°Ÿāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "purchase_button_buy": "ā°•āąŠā°¨āą", @@ -1179,7 +1171,6 @@ "upload_status_errors": "ā°˛āą‹ā°Ēā°žā°˛āą", "upload_status_uploaded": "ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "upload_success": "ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą ā°ĩā°ŋⰜⰝā°ĩā°‚ā°¤ā°Žāąˆā°‚ā°Ļā°ŋ, ā°•āąŠā°¤āąā°¤ ā°…ā°Ēāąâ€Œā°˛āą‹ā°Ąāą ā°†ā°¸āąā°¤āąā°˛ā°¨āą ā°šāą‚ā°Ąā°Ÿā°žā°¨ā°ŋā°•ā°ŋ ā°Ēāą‡ā°œāą€ā°¨ā°ŋ ā°°ā°ŋā°Ģāąā°°āą†ā°ˇāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ.", - "url": "URL", "usage": "ā°ĩā°žā°Ąāąā°•", "use_custom_date_range": "ā°Ŧā°Ļāąā°˛āąā°—ā°ž ā°…ā°¨āąā°•āą‚ā°˛ ā°¤āą‡ā°Ļāą€ ā°Ēā°°ā°ŋā°§ā°ŋā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "user": "ā°ĩā°ŋā°¨āąā°¯āą‹ā°—ā°§ā°žā°°ā°ŋ", diff --git a/i18n/th.json b/i18n/th.json index d797dab583..36b5b97b5c 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,7 +14,6 @@ "add_a_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "add_a_name": "āš€ā¸žā¸´āšˆā¸Ąā¸Šā¸ˇāšˆā¸­", "add_a_title": "āš€ā¸žā¸´āšˆā¸Ąā¸Ģā¸ąā¸§ā¸‚āš‰ā¸­", - "add_endpoint": "Add endpoint", "add_exclusion_pattern": "āš€ā¸žā¸´āšˆā¸Ąā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "add_import_path": "āš€ā¸žā¸´āšˆā¸Ąāš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛", "add_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", @@ -191,20 +190,13 @@ "oauth_auto_register": "ā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸā¸™ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "oauth_auto_register_description": "ā¸Ĩā¸‡ā¸—ā¸°āš€ā¸šā¸ĩā¸ĸā¸™ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_button_text": "ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸¸āšˆā¸Ąā¸ā¸”", - "oauth_client_id": "ID āš„ā¸„ā¸Ĩāš€ā¸­ā¸™ā¸•āšŒ", - "oauth_client_secret": "Secret āš„ā¸„ā¸Ĩāš€ā¸­ā¸™ā¸•āšŒ", "oauth_enable_description": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", - "oauth_issuer_url": "ā¸œā¸šāš‰ā¸­ā¸­ā¸ URL", "oauth_mobile_redirect_uri": "URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", "oauth_mobile_redirect_uri_override": "āšā¸—ā¸™ā¸—ā¸ĩāšˆ URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", "oauth_mobile_redirect_uri_override_description": "āš€ā¸›ā¸´ā¸”āš€ā¸Ąā¸ˇāšˆā¸­ 'app.immich:/' āš€ā¸›āš‡ā¸™ URI ⏗ā¸ĩāšˆāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", - "oauth_profile_signing_algorithm": "ā¸­ā¸ąā¸Ĩ⏁⏭⏪⏴⏗ā¸ļā¸Ąā¸ā¸˛ā¸Ŗā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰", - "oauth_profile_signing_algorithm_description": "ā¸­ā¸ąā¸Ĩ⏁⏭⏪⏴⏗ā¸ļā¸Ąāšƒā¸Šāš‰āšƒā¸™ā¸ā¸˛ā¸Ŗā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰", - "oauth_scope": "ā¸‚ā¸­ā¸šāš€ā¸‚ā¸•", "oauth_settings": "OAuth", "oauth_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_settings_more_details": "ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą āšƒā¸Ģāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš€ā¸­ā¸ā¸Ē⏞⏪", - "oauth_signing_algorithm": "ā¸­ā¸ąā¸Ĩ⏁⏭⏪⏴⏗ā¸ļā¸Ąā¸ā¸˛ā¸Ŗā¸Ĩā¸‡ā¸™ā¸˛ā¸Ą", "oauth_storage_label_claim": "ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡ā¸›āš‰ā¸˛ā¸ĸā¸ā¸ŗā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š", "oauth_storage_label_claim_description": "ā¸•ā¸ąāš‰ā¸‡ā¸›āš‰ā¸˛ā¸ĸā¸ā¸ŗā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚ā¸­ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸•ā¸˛ā¸Ąā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "oauth_storage_quota_claim": "ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š", @@ -370,11 +362,9 @@ "admin_password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "administration": "ā¸ā¸˛ā¸Ŗā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "advanced": "ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡", - "advanced_settings_log_level_title": "ā¸Ŗā¸°ā¸”ā¸ąā¸šā¸ā¸˛ā¸Ŗ Log: {}", + "advanced_settings_log_level_title": "ā¸Ŗā¸°ā¸”ā¸ąā¸šā¸ā¸˛ā¸Ŗ Log: {level}", "advanced_settings_prefer_remote_subtitle": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸šā¸˛ā¸‡āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸Šāš‰ā¸˛ā¸Ąā¸˛ā¸ āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸™ā¸ĩāš‰āš€ā¸žā¸ˇāšˆā¸­āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸ˆā¸˛ā¸ā¸Ŗā¸ĩāš‚ā¸Ąā¸—āšā¸—ā¸™", "advanced_settings_prefer_remote_title": "āšƒā¸Ģāš‰ā¸„ā¸§ā¸˛ā¸Ąā¸Ēā¸ŗā¸„ā¸ąā¸ā¸ā¸ąā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ŗā¸ĩāš‚ā¸Ąā¸—", - "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", - "advanced_settings_proxy_headers_title": "Proxy Headers", "advanced_settings_self_signed_ssl_subtitle": "ā¸‚āš‰ā¸˛ā¸Ąā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡āšā¸šā¸š self-signed", "advanced_settings_self_signed_ssl_title": "ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL āšā¸šā¸š self-signed ", "advanced_settings_tile_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡", @@ -399,9 +389,9 @@ "album_remove_user_confirmation": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ĩā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ {user} ?", "album_share_no_users": "ā¸”ā¸šāš€ā¸Ģā¸Ąā¸ˇā¸­ā¸™ā¸§āšˆā¸˛ā¸„ā¸¸ā¸“āš„ā¸”āš‰āšā¸Šā¸ŖāšŒā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸™ā¸ĩāš‰ā¸ā¸ąā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšā¸Ĩāš‰ā¸§", "album_thumbnail_card_item": "1 ⏪⏞ā¸ĸ⏁⏞⏪", - "album_thumbnail_card_items": "{} ⏪⏞ā¸ĸ⏁⏞⏪", + "album_thumbnail_card_items": "{count} ⏪⏞ā¸ĸ⏁⏞⏪", "album_thumbnail_card_shared": " ¡ ā¸–ā¸šā¸āšā¸Šā¸ŖāšŒ", - "album_thumbnail_shared_by": "āšā¸Šā¸ŖāšŒāš‚ā¸”ā¸ĸ {}", + "album_thumbnail_shared_by": "āšā¸Šā¸ŖāšŒāš‚ā¸”ā¸ĸ {user}", "album_updated": "ā¸­ā¸ąā¸›āš€ā¸”ā¸—ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšā¸Ĩāš‰ā¸§", "album_updated_setting_description": "āšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™ā¸­ā¸ĩāš€ā¸Ąā¸Ĩāš€ā¸Ąā¸ˇāšˆā¸­ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒā¸ā¸ąā¸™ā¸Ąā¸ĩā¸Ēā¸ˇāšˆā¸­āšƒā¸Ģā¸Ąāšˆ", "album_user_left": "⏭⏭⏁⏈⏞⏁ {album}", @@ -439,10 +429,9 @@ "archive": "āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", "archive_or_unarchive_photo": "āš€ā¸āš‡ā¸š/āš„ā¸Ąāšˆāš€ā¸āš‡ā¸šā¸ ā¸˛ā¸žā¸–ā¸˛ā¸§ā¸Ŗ", "archive_page_no_archived_assets": "āš„ā¸Ąāšˆā¸žā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸™ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", - "archive_page_title": "āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ ({})", + "archive_page_title": "āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ ({count})", "archive_size": "ā¸‚ā¸™ā¸˛ā¸”āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", "archive_size_description": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸™ā¸˛ā¸”ā¸Ēā¸šā¸‡ā¸Ē⏏⏔ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ (GiB)", - "archived": "Archived", "are_these_the_same_person": "āš€ā¸›āš‡ā¸™ā¸„ā¸™āš€ā¸”ā¸ĩā¸ĸā¸§ā¸ā¸ąā¸™ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", "are_you_sure_to_do_this": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸ŗā¸Ēā¸´āšˆā¸‡ā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", "asset_action_delete_err_read_only": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸā¸§āš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", @@ -462,37 +451,25 @@ "asset_list_settings_title": "ā¸•ā¸˛ā¸Ŗā¸˛ā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "asset_offline": "ā¸Ēā¸ˇāšˆā¸­ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", "asset_offline_description": "āš„ā¸Ąāšˆā¸žā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏠⏞ā¸ĸ⏙⏭⏁⏙ā¸ĩāš‰āšƒā¸™ā¸”ā¸´ā¸Ēā¸āšŒā¸­ā¸ĩā¸ā¸•āšˆā¸­āš„ā¸› āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸´ā¸”ā¸•āšˆā¸­ā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚ Immich ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­ā¸‚ā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸Šāšˆā¸§ā¸ĸāš€ā¸Ģā¸Ĩ⏎⏭", - "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "ā¸‚āš‰ā¸˛ā¸Ąāšā¸Ĩāš‰ā¸§", "asset_skipped_in_trash": "āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "asset_uploaded": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āšā¸Ĩāš‰ā¸§", "asset_uploading": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔â€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", "asset_viewer_settings_title": "ā¸•ā¸ąā¸§ā¸”ā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪", "assets": "ā¸Ēā¸ˇāšˆā¸­", "assets_added_to_album_count": "āš€ā¸žā¸´āšˆā¸Ą {count, plural, one {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "assets_added_to_name_count": "āš€ā¸žā¸´āšˆā¸Ą {count, plural, one {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ {hasName, select, true {{name}} other {new album}}", - "assets_deleted_permanently": "{} asset(s) deleted permanently", - "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server", "assets_moved_to_trash_count": "ā¸ĸāš‰ā¸˛ā¸ĸ {count, plural, one {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āšā¸Ĩāš‰ā¸§", "assets_permanently_deleted_count": "ā¸Ĩ⏚ {count, plural, one {# asset} other {# assets}} ā¸—ā¸´āš‰ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ā¸–ā¸šā¸ā¸Ĩā¸šāšā¸Ĩāš‰ā¸§", - "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device", "assets_restore_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸šāš‰ā¸„ā¸ˇā¸™ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸—ā¸´āš‰ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”? ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰āš„ā¸”āš‰! āš‚ā¸›ā¸Ŗā¸”ā¸—ā¸Ŗā¸˛ā¸šā¸§āšˆā¸˛ā¸Ēā¸ˇāšˆā¸­ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒāšƒā¸”āš† āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ā¸šāš‰ā¸„ā¸ˇā¸™āš„ā¸”āš‰ā¸”āš‰ā¸§ā¸ĸ⏧⏴⏘ā¸ĩ⏙ā¸ĩāš‰", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} ā¸„ā¸ˇā¸™ā¸„āšˆā¸˛", - "assets_restored_successfully": "{} asset(s) restored successfully", - "assets_trashed": "{} asset(s) trashed", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ā¸–ā¸šā¸ā¸Ĩ⏚", - "assets_trashed_from_server": "{} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} ⏭ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸­ā¸ĸā¸šāšˆāšā¸Ĩāš‰ā¸§", "authorized_devices": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸—ā¸ĩāšˆāš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", "back": "⏁ā¸Ĩā¸ąā¸š", "back_close_deselect": "ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸š, ⏛⏴⏔, ā¸Ģ⏪⏎⏭ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸ā¸˛ā¸Ŗāš€ā¸Ĩ⏎⏭⏁", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸šā¸™āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ ({})", + "backup_album_selection_page_albums_device": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸šā¸™āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ ({count})", "backup_album_selection_page_albums_tap": "ā¸ā¸”āš€ā¸žā¸ˇāšˆā¸­ā¸Ŗā¸§ā¸Ą ⏁⏔ā¸Ēā¸­ā¸‡ā¸„ā¸Ŗā¸ąāš‰ā¸‡āš€ā¸žā¸ˇāšˆā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "backup_album_selection_page_assets_scatter": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏞⏪ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ā¸Ŗā¸°ā¸ˆā¸˛ā¸ĸāš„ā¸›āšƒā¸™ā¸Ģā¸Ĩ⏞ā¸ĸā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸–ā¸šā¸ā¸Ŗā¸§ā¸Ąā¸Ģ⏪⏎⏭ā¸ĸā¸āš€ā¸§āš‰ā¸™āšƒā¸™ā¸ā¸Ŗā¸°ā¸šā¸§ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_album_selection_page_select_albums": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", @@ -501,11 +478,11 @@ "backup_all": "ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "backup_background_service_backup_failed_message": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸Ĩā¸­ā¸‡āšƒā¸Ģā¸Ąāšˆ...", "backup_background_service_connection_failed_message": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸ā¸ąā¸šāš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸Ĩā¸­ā¸‡āšƒā¸Ģā¸Ąāšˆ...", - "backup_background_service_current_upload_notification": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔ {}", + "backup_background_service_current_upload_notification": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔ {filename}", "backup_background_service_default_notification": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚ā¸Ģā¸˛ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆ...", "backup_background_service_error_title": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "backup_background_service_in_progress_notification": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏂⏭⏇⏄⏏⏓...", - "backup_background_service_upload_failure_notification": "ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧ {}", + "backup_background_service_upload_failure_notification": "ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧ {filename}", "backup_controller_page_albums": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "backup_controller_page_background_app_refresh_disabled_content": "āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸”ā¸ļā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšā¸­ā¸žā¸­ā¸ĸā¸šāšˆāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡āš‚ā¸”ā¸ĸā¸ā¸˛ā¸Ŗāš„ā¸›ā¸—ā¸ĩāšˆ ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ > ā¸—ā¸ąāšˆā¸§āš„ā¸› > ⏔ā¸ļā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšā¸­ā¸›ā¸­ā¸ĸā¸šāšˆāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡ āš€ā¸žā¸ˇāšˆā¸­āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸”ā¸ļā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", "backup_controller_page_background_app_refresh_disabled_title": "⏁⏞⏪⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Šāšā¸­ā¸žāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸Ĩā¸ąā¸‡ā¸›ā¸´ā¸”", @@ -516,7 +493,7 @@ "backup_controller_page_background_battery_info_title": "⏛⏪⏰ā¸Ēā¸´ā¸—ā¸˜ā¸´ā¸ ā¸˛ā¸žāšā¸šā¸•āš€ā¸•ā¸­ā¸Ŗā¸ĩāšˆ", "backup_controller_page_background_charging": "ā¸‚ā¸“ā¸°ā¸Šā¸˛ā¸ŖāšŒā¸ˆā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸ⏧", "backup_controller_page_background_configure_error": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", - "backup_controller_page_background_delay": "ā¸Ĩāšˆā¸˛ā¸Šāš‰ā¸˛ā¸ā¸˛ā¸Ŗā¸Ĩā¸ŗā¸Ŗā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆ: {}", + "backup_controller_page_background_delay": "ā¸Ĩāšˆā¸˛ā¸Šāš‰ā¸˛ā¸ā¸˛ā¸Ŗā¸Ĩā¸ŗā¸Ŗā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆ: {duration}", "backup_controller_page_background_description": "āš€ā¸›ā¸´ā¸”ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡āš€ā¸žā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆāš‚ā¸”ā¸ĸ⏗ā¸ĩāšˆāš„ā¸Ąāšˆā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸•āš‰ā¸­ā¸‡āš€ā¸›ā¸´ā¸”āšā¸­ā¸›", "backup_controller_page_background_is_off": "⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ā¸›ā¸´ā¸”ā¸­ā¸ĸā¸šāšˆ", "backup_controller_page_background_is_on": "⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸›ā¸´ā¸”ā¸­ā¸ĸā¸šāšˆ", @@ -524,14 +501,13 @@ "backup_controller_page_background_turn_on": "āš€ā¸›ā¸´ā¸”ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", "backup_controller_page_background_wifi": "ā¸šā¸™ WiFi āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", "backup_controller_page_backup": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "backup_controller_page_backup_selected": "⏗ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁:", + "backup_controller_page_backup_selected": "⏗ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁: ", "backup_controller_page_backup_sub": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸Ēā¸ŗā¸Ŗā¸­ā¸‡āšā¸Ĩāš‰ā¸§", - "backup_controller_page_created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­: {}", + "backup_controller_page_created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­: {date}", "backup_controller_page_desc_backup": "āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛āš€ā¸žā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆāš„ā¸›ā¸ĸā¸ąā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒāš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”āšā¸­ā¸ž", - "backup_controller_page_excluded": "ā¸–ā¸šā¸ā¸ĸā¸āš€ā¸§āš‰ā¸™:", - "backup_controller_page_failed": "ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧ ({})", - "backup_controller_page_filename": "ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "ā¸–ā¸šā¸ā¸ĸā¸āš€ā¸§āš‰ā¸™: ", + "backup_controller_page_failed": "ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧ ({count})", + "backup_controller_page_filename": "ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ: {filename} [{size}]", "backup_controller_page_info": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_controller_page_none_selected": "āš„ā¸Ąāšˆā¸Ąā¸ĩ⏗ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁", "backup_controller_page_remainder": "⏗ā¸ĩāšˆāš€ā¸Ģā¸Ĩ⏎⏭", @@ -540,7 +516,7 @@ "backup_controller_page_start_backup": "āš€ā¸Ŗā¸´āšˆā¸Ąā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_controller_page_status_off": "⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛ā¸›ā¸´ā¸”ā¸­ā¸ĸā¸šāšˆ", "backup_controller_page_status_on": "⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛āš€ā¸›ā¸´ā¸”ā¸­ā¸ĸā¸šāšˆ", - "backup_controller_page_storage_format": "{} ⏈⏞⏁ {} ā¸–ā¸šā¸āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", + "backup_controller_page_storage_format": "{used} ⏈⏞⏁ {total} ā¸–ā¸šā¸āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "backup_controller_page_to_backup": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_controller_page_total_sub": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸‹āš‰ā¸ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸ˆā¸˛ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁", "backup_controller_page_turn_off": "⏛⏴⏔⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛", @@ -553,7 +529,6 @@ "backup_manual_success": "ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "backup_manual_title": "ā¸Ēā¸–ā¸˛ā¸™ā¸°ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔", "backup_options_page_title": "ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "⏁ā¸Ĩā¸ąā¸šā¸Ģā¸Ĩā¸ąā¸‡", "birthdate_saved": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸§ā¸ąā¸™āš€ā¸ā¸´ā¸”āšā¸Ĩāš‰ā¸§", "birthdate_set_description": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš€ā¸ā¸´ā¸”ā¸ˆā¸°ā¸™ā¸ŗā¸Ąā¸˛āšƒā¸Šāš‰āšƒā¸™ā¸ā¸˛ā¸Ŗā¸„ā¸ŗā¸™ā¸§ā¸“ā¸­ā¸˛ā¸ĸā¸¸ā¸‚ā¸­ā¸‡ā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏙ā¸ĩāš‰āšƒā¸™ā¸‚ā¸“ā¸°ā¸—ā¸ĩāšˆā¸–āšˆā¸˛ā¸ĸā¸Ŗā¸šā¸›", @@ -565,21 +540,21 @@ "bulk_keep_duplicates_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāš€ā¸āš‡ā¸š {count, plural, one {# duplicate asset} other {# duplicate asset}} āš„ā¸§āš‰ ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰ā¸ˆā¸°āšā¸āš‰āš„ā¸‚ā¸ā¸Ĩā¸¸āšˆā¸Ąā¸—ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āš‚ā¸”ā¸ĸāš„ā¸Ąāšˆā¸•āš‰ā¸­ā¸‡ā¸Ĩ⏚ā¸Ēā¸´āšˆā¸‡āšƒā¸”āš€ā¸Ĩā¸ĸ", "bulk_trash_duplicates_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸Ąā¸˛ā¸ {count, plural, one {# duplicate asset} other {# duplicate asset}} ā¸ā¸˛ā¸Ŗā¸—ā¸ŗāš€ā¸Šāšˆā¸™ā¸™ā¸ĩāš‰ā¸ˆā¸°āš€ā¸āš‡ā¸šā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāšƒā¸Ģā¸āšˆā¸—ā¸ĩāšˆā¸Ēā¸¸ā¸”ā¸‚ā¸­ā¸‡āšā¸•āšˆā¸Ĩ⏰⏁ā¸Ĩā¸¸āšˆā¸Ąāšā¸Ĩ⏰ā¸Ĩā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‹āš‰ā¸ŗā¸­ā¸ˇāšˆā¸™ āš† ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "buy": "ā¸‹ā¸ˇāš‰ā¸­ Immich", - "cache_settings_album_thumbnails": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž ({} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", + "cache_settings_album_thumbnails": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž ({count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", "cache_settings_clear_cache_button": "ā¸Ĩāš‰ā¸˛ā¸‡āšā¸„ā¸Š", "cache_settings_clear_cache_button_title": "ā¸Ĩāš‰ā¸˛ā¸‡āšā¸„ā¸Šā¸‚ā¸­ā¸‡āšā¸­ā¸ž ⏈⏰ā¸Ēāšˆā¸‡ā¸œā¸Ĩā¸ā¸Ŗā¸°ā¸—ā¸šā¸•āšˆā¸­ā¸›ā¸Ŗā¸°ā¸Ēā¸´ā¸—ā¸˜ā¸´ā¸ ā¸˛ā¸žāšā¸­ā¸žā¸ˆā¸™ā¸ā¸§āšˆā¸˛āšā¸„ā¸Šā¸ˆā¸°ā¸–ā¸šā¸ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšƒā¸Ģā¸Ąāšˆ", "cache_settings_duplicated_assets_clear_button": "ā¸Ĩāš‰ā¸˛ā¸‡", "cache_settings_duplicated_assets_subtitle": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛āšā¸šā¸Ĩāš‡ā¸ā¸Ĩ⏴ā¸Ēā¸•āšŒāš‚ā¸”ā¸ĸāšā¸­ā¸›", - "cache_settings_duplicated_assets_title": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏗ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ ({})", - "cache_settings_image_cache_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž ({} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", + "cache_settings_duplicated_assets_title": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏗ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ ({count})", + "cache_settings_image_cache_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž ({count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", "cache_settings_statistics_album": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", - "cache_settings_statistics_assets": "{} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ ({})", + "cache_settings_statistics_assets": "{count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ ({size})", "cache_settings_statistics_full": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāš€ā¸•āš‡ā¸Ą", "cache_settings_statistics_shared": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒ", "cache_settings_statistics_thumbnail": "ā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­", "cache_settings_statistics_title": "ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšā¸„ā¸Š", "cache_settings_subtitle": "ā¸„ā¸§ā¸šā¸„ā¸¸ā¸Ąā¸žā¸¤ā¸•ā¸´ā¸ā¸Ŗā¸Ŗā¸Ąā¸ā¸˛ā¸Ŗāšā¸„ā¸Šā¸‚ā¸­ā¸‡āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ Immich", - "cache_settings_thumbnail_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ ({} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", + "cache_settings_thumbnail_size": "ā¸‚ā¸™ā¸˛ā¸”āšā¸„ā¸Šā¸Ŗā¸šā¸›ā¸ĸāšˆā¸­ ({count} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪)", "cache_settings_tile_subtitle": "ā¸„ā¸§ā¸šā¸„ā¸¸ā¸Ąā¸žā¸¤ā¸•ā¸´ā¸ā¸Ŗā¸Ŗā¸Ąā¸‚ā¸­ā¸‡ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šāšƒā¸™ā¸•ā¸ąā¸§āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "cache_settings_tile_title": "⏗ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šāšƒā¸™ā¸•ā¸ąā¸§āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "cache_settings_title": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸„ā¸Š", @@ -588,12 +563,10 @@ "camera_model": "ā¸Ŗā¸¸āšˆā¸™ā¸ā¸Ĩāš‰ā¸­ā¸‡", "cancel": "ā¸ĸā¸āš€ā¸Ĩ⏴⏁", "cancel_search": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", - "canceled": "Canceled", "cannot_merge_people": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ŗā¸§ā¸Ąā¸ā¸Ĩā¸¸āšˆā¸Ąā¸„ā¸™āš„ā¸”āš‰", "cannot_undo_this_action": "⏁⏞⏪⏁⏪⏰⏗⏺⏙ā¸ĩāš‰āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šāš„ā¸”āš‰!", "cannot_update_the_description": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸žāš€ā¸”ā¸—ā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš„ā¸”āš‰", "change_date": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ", - "change_display_order": "Change display order", "change_expiration_time": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏", "change_location": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸•āšā¸˛āšā¸Ģā¸™āšˆā¸‡", "change_name": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸Šā¸ˇāšˆā¸­", @@ -605,12 +578,10 @@ "change_password_form_new_password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", "change_password_form_password_mismatch": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸Ąāšˆā¸•ā¸Ŗā¸‡ā¸ā¸ąā¸™", "change_password_form_reenter_new_password": "⏁⏪⏭⏁⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", + "change_pin_code": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "change_your_password": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "changed_visibility_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗā¸Ąā¸­ā¸‡āš€ā¸Ģāš‡ā¸™āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", "check_all": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", "check_logs": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸šā¸ąā¸™ā¸—ā¸ļ⏁", "choose_matching_people_to_merge": "āš€ā¸Ĩ⏎⏭⏁⏄⏙⏗ā¸ĩāšˆā¸•ā¸Ŗā¸‡ā¸ā¸ąā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸Ŗā¸§ā¸Ąāš€ā¸‚āš‰ā¸˛ā¸”āš‰ā¸§ā¸ĸā¸ā¸ąā¸™", "city": "āš€ā¸Ąā¸ˇā¸­ā¸‡", @@ -619,14 +590,6 @@ "clear_all_recent_searches": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸›ā¸Ŗā¸°ā¸§ā¸ąā¸•ā¸´ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", "clear_message": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ą", "clear_value": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸„āšˆā¸˛", - "client_cert_dialog_msg_confirm": "OK", - "client_cert_enter_password": "Enter Password", - "client_cert_import": "Import", - "client_cert_import_success_msg": "Client certificate is imported", - "client_cert_invalid_msg": "Invalid certificate file or wrong password", - "client_cert_remove_msg": "Client certificate is removed", - "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", - "client_cert_title": "SSL Client Certificate", "clockwise": "ā¸•ā¸˛ā¸Ąāš€ā¸‚āš‡ā¸Ąā¸™ā¸˛ā¸Ŧ⏴⏁⏞", "close": "⏛⏴⏔", "collapse": "ā¸ĸāšˆā¸­", @@ -645,17 +608,17 @@ "confirm_delete_face": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛{name}⏭⏭⏁ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", "confirm_delete_shared_link": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ĩ⏚ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāšƒā¸Šāšˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ ?", "confirm_keep_this_delete_others": "⏈⏰ā¸Ĩā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸™ā¸Ŗā¸˛ā¸ĸ⏁⏞⏪ āšā¸Ĩ⏰ā¸ĸā¸āš€ā¸§āš‰ā¸™ā¸Ēā¸ˇāšˆā¸­ā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆāšƒā¸Šāšˆāš„ā¸Ģā¸Ąā¸—ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•āšˆā¸­?", + "confirm_new_pin_code": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "confirm_password": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "contain": "ā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆ", "context": "ā¸šā¸Ŗā¸´ā¸šā¸—", "continue": "ā¸•āšˆā¸­āš„ā¸›", - "control_bottom_app_bar_album_info_shared": "{} ⏪⏞ā¸ĸ⏁⏞⏪ ¡ ā¸–ā¸šā¸āšā¸Šā¸ŖāšŒ", + "control_bottom_app_bar_album_info_shared": "{count} ⏪⏞ā¸ĸ⏁⏞⏪ ¡ ā¸–ā¸šā¸āšā¸Šā¸ŖāšŒ", "control_bottom_app_bar_create_new_album": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšƒā¸Ģā¸Ąāšˆ", "control_bottom_app_bar_delete_from_immich": "ā¸Ĩ⏚⏈⏞⏁ Immich", "control_bottom_app_bar_delete_from_local": "ā¸Ĩā¸šā¸ˆā¸˛ā¸āš€ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "control_bottom_app_bar_edit_location": "āšā¸āš‰āš„ā¸‚ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "control_bottom_app_bar_edit_time": "āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™āšā¸Ĩā¸°āš€ā¸§ā¸Ĩ⏞", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "āšā¸Šā¸ŖāšŒāšƒā¸Ģāš‰", "control_bottom_app_bar_trash_from_immich": "ā¸ĸāš‰ā¸˛ā¸ĸāš€ā¸‚āš‰ā¸˛ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "copied_image_to_clipboard": "ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸ā¸ ā¸˛ā¸žāš„ā¸›ā¸ĸā¸ąā¸‡ā¸„ā¸Ĩā¸´ā¸›ā¸šā¸­ā¸ŖāšŒā¸”āšā¸Ĩāš‰ā¸§", @@ -677,7 +640,6 @@ "create_link": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ĩā¸´ā¸‡ā¸āšŒ", "create_link_to_share": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ĩā¸´ā¸‡ā¸āšŒāš€ā¸žā¸ˇāšˆā¸­āšā¸Šā¸ŖāšŒ", "create_link_to_share_description": "ā¸œā¸šāš‰ā¸—ā¸ĩāšˆā¸Ąā¸ĩā¸Ĩā¸´ā¸‡ā¸āšŒ ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸”ā¸šā¸Ŗā¸šā¸›ā¸—ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āš„ā¸”āš‰", - "create_new": "CREATE NEW", "create_new_person": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", "create_new_person_hint": "⏁⏺ā¸Ģ⏙⏔ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āšƒā¸Ģāš‰ā¸ā¸ąā¸šā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", "create_new_user": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", @@ -687,10 +649,9 @@ "create_tag_description": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸—āš‡ā¸āšƒā¸Ģā¸Ąāšˆ ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāšā¸—āš‡ā¸ā¸—ā¸ĩāšˆā¸‹āš‰ā¸­ā¸™ā¸ā¸ąā¸™ āš‚ā¸›ā¸Ŗā¸”ā¸›āš‰ā¸­ā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸‚ā¸­ā¸‡āšā¸—āš‡ā¸ ā¸Ŗā¸§ā¸Ąā¸–ā¸ļā¸‡āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ģā¸Ąā¸˛ā¸ĸā¸—ā¸ąā¸š", "create_user": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰", "created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸Ĩāš‰ā¸§", - "crop": "Crop", "curated_object_page_title": "ā¸Ēā¸´āšˆā¸‡ā¸‚ā¸­ā¸‡", "current_device": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", - "current_server_address": "Current server address", + "current_pin_code": "⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", "custom_locale": "ā¸›ā¸Ŗā¸ąā¸šā¸ ā¸˛ā¸Šā¸˛ā¸—āš‰ā¸­ā¸‡ā¸–ā¸´āšˆā¸™āš€ā¸­ā¸‡", "custom_locale_description": "āšƒā¸Šāš‰ā¸Ŗā¸šā¸›āšā¸šā¸šā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāšā¸Ĩā¸°ā¸•ā¸ąā¸§āš€ā¸Ĩā¸‚ā¸ˆā¸˛ā¸ā¸ ā¸˛ā¸Šā¸˛āšā¸Ĩā¸°ā¸‚ā¸­ā¸šāš€ā¸‚ā¸•", "daily_title_text_date": "E dd MMM", @@ -699,7 +660,6 @@ "date_after": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸Ģā¸Ĩā¸ąā¸‡ā¸ˆā¸˛ā¸", "date_and_time": "ā¸§ā¸ąā¸™āšā¸Ĩā¸°āš€ā¸§ā¸Ĩ⏞", "date_before": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸āšˆā¸­ā¸™", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸§ā¸ąā¸™āš€ā¸ā¸´ā¸”āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", "date_range": "ā¸Šāšˆā¸§ā¸‡ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ", "day": "ā¸§ā¸ąā¸™", @@ -753,26 +713,12 @@ "documentation": "āš€ā¸­ā¸ā¸Ē⏞⏪", "done": "ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "download": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", - "download_canceled": "Download canceled", - "download_complete": "Download complete", - "download_enqueue": "Download enqueued", - "download_error": "Download Error", - "download_failed": "Download failed", - "download_filename": "file: {}", - "download_finished": "Download finished", "download_include_embedded_motion_videos": "ā¸Ŗā¸§ā¸Ąā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸ā¸ąā¸‡ā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸ ā¸˛ā¸žāš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", "download_include_embedded_motion_videos_description": "ā¸Ŗā¸§ā¸Ąā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸ā¸ąā¸‡ā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸ ā¸˛ā¸žāš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģā¸§āš€ā¸Ąā¸ˇāšˆā¸­ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", - "download_notfound": "Download not found", - "download_paused": "Download paused", "download_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", "download_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", - "download_started": "Download started", - "download_sucess": "Download success", - "download_sucess_android": "The media has been downloaded to DCIM/Immich", - "download_waiting_to_retry": "Waiting to retry", "downloading": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", "downloading_asset_filename": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ {filename}", - "downloading_media": "Downloading media", "drop_files_to_upload": "ā¸§ā¸˛ā¸‡āš„ā¸Ÿā¸ĨāšŒāšƒā¸™ā¸Šāšˆā¸­ā¸‡ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔", "duplicates": "⏪⏞ā¸ĸ⏁⏞⏪⏗ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™", "duplicates_description": "āšā¸āš‰āš„ā¸‚āšā¸•āšˆā¸Ĩ⏰⏁ā¸Ĩā¸¸āšˆā¸Ąāš‚ā¸”ā¸ĸā¸Ŗā¸°ā¸šā¸¸ā¸§āšˆā¸˛ā¸ā¸Ĩā¸¸āšˆā¸Ąāšƒā¸”ā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ā¸Ģā¸˛ā¸ā¸Ąā¸ĩ", @@ -802,19 +748,15 @@ "editor_crop_tool_h2_aspect_ratios": "ā¸­ā¸ąā¸•ā¸Ŗā¸˛ā¸Ēāšˆā¸§ā¸™ā¸ ā¸˛ā¸ž", "editor_crop_tool_h2_rotation": "⏁⏞⏪ā¸Ģā¸Ąā¸¸ā¸™", "email": "⏭ā¸ĩāš€ā¸Ąā¸Ĩ", - "empty_folder": "This folder is empty", "empty_trash": "ā¸—ā¸´āš‰ā¸‡ā¸ˆā¸˛ā¸ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "empty_trash_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩāš‰ā¸˛ā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰ ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸Ĩā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰⏭⏭⏁⏈⏞⏁ Immich ⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ\nā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰āš„ā¸”āš‰!", "enable": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "enabled": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "end_date": "ā¸§ā¸ąā¸™ā¸Ēā¸´āš‰ā¸™ā¸Ē⏏⏔", - "enqueued": "Enqueued", "enter_wifi_name": "Enter WiFi name", "error": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", - "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "āš€ā¸ā¸´ā¸”āš€ā¸­ā¸­āš€ā¸Ŗā¸­ā¸ŖāšŒ āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸­ā¸­ā¸āš„ā¸”āš‰", "error_loading_image": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔⏪⏰ā¸Ģā¸§āšˆā¸˛ā¸‡āš‚ā¸Ģā¸Ĩā¸”ā¸ ā¸˛ā¸ž", - "error_saving_image": "Error: {}", "error_title": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "errors": { "cannot_navigate_next_asset": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡āš„ā¸”āš‰", @@ -947,10 +889,6 @@ "exif_bottom_sheet_location": "ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "exif_bottom_sheet_people": "⏄⏙", "exif_bottom_sheet_person_add_person": "āš€ā¸žā¸´āšˆā¸Ąā¸Šā¸ˇāšˆā¸­", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸ā¸˛ā¸Ŗā¸™ā¸ŗāš€ā¸Ē⏙⏭", "expand_all": "⏂ā¸ĸ⏞ā¸ĸā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "experimental_settings_new_asset_list_subtitle": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸žā¸ąā¸’ā¸™ā¸˛", @@ -967,12 +905,9 @@ "extension": "ā¸Ēāšˆā¸§ā¸™ā¸•āšˆā¸­ā¸‚ā¸ĸ⏞ā¸ĸ", "external": "⏠⏞ā¸ĸ⏙⏭⏁", "external_libraries": "⏠⏞ā¸ĸ⏙⏭⏁⏄ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", - "external_network": "External network", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "face_unassigned": "āš„ā¸Ąāšˆā¸ā¸ŗā¸Ģā¸™ā¸”ā¸Ąā¸­ā¸šā¸Ģā¸Ąā¸˛ā¸ĸ", - "failed": "Failed", "failed_to_load_assets": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­", - "failed_to_load_folder": "Failed to load folder", "favorite": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", "favorite_or_unfavorite_photo": "āš‚ā¸›ā¸Ŗā¸”ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆāš‚ā¸›ā¸Ŗā¸”ā¸ ā¸˛ā¸ž", "favorites": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", @@ -984,23 +919,18 @@ "file_name_or_extension": "ā¸™ā¸˛ā¸Ąā¸Ē⏁⏏ā¸Ĩā¸Ģā¸Ŗā¸ˇā¸­ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ", "filename": "ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ", "filetype": "ā¸Šā¸™ā¸´ā¸”āš„ā¸Ÿā¸ĨāšŒ", - "filter": "Filter", "filter_people": "ā¸ā¸Ŗā¸­ā¸‡ā¸œā¸šāš‰ā¸„ā¸™", "find_them_fast": "ā¸„āš‰ā¸™ā¸Ģā¸˛āš‚ā¸”ā¸ĸā¸Šā¸ˇāšˆā¸­ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ŗā¸§ā¸”āš€ā¸Ŗāš‡ā¸§", "fix_incorrect_match": "āšā¸āš‰āš„ā¸‚ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸šā¸„ā¸šāšˆā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "āš‚ā¸Ÿā¸ĨāšŒāš€ā¸”ā¸­ā¸ŖāšŒ", "folders_feature_description": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸ĩā¸ĸā¸ā¸”ā¸šā¸Ąā¸¸ā¸Ąā¸Ąā¸­ā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ ā¸˛ā¸žā¸–āšˆā¸˛ā¸ĸāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­āšƒā¸™ā¸Ŗā¸°ā¸šā¸šāš„ā¸Ÿā¸ĨāšŒ", "forward": "āš„ā¸›ā¸‚āš‰ā¸˛ā¸‡ā¸Ģā¸™āš‰ā¸˛", "general": "ā¸—ā¸ąāšˆā¸§āš„ā¸›", "get_help": "ā¸‚ā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸Šāšˆā¸§ā¸ĸāš€ā¸Ģā¸Ĩ⏎⏭", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "go_back": "⏁ā¸Ĩā¸ąā¸š", "go_to_folder": "āš„ā¸›ā¸—ā¸ĩāšˆāš‚ā¸Ÿā¸ĨāšŒāš€ā¸”ā¸­ā¸ŖāšŒ", "go_to_search": "⏁ā¸Ĩā¸ąā¸šāš„ā¸›ā¸ĸā¸ąā¸‡ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", - "grant_permission": "Grant permission", "group_albums_by": "ā¸ˆā¸ąā¸”ā¸ā¸Ĩā¸¸āšˆā¸Ąā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸•ā¸˛ā¸Ą", "group_country": "ā¸ˆā¸ąā¸”āš€ā¸Ŗā¸ĩā¸ĸ⏇⏁ā¸Ĩā¸¸āšˆā¸Ąā¸•ā¸˛ā¸Ąā¸›ā¸Ŗā¸°āš€ā¸—ā¸¨", "group_no": "āš„ā¸Ąāšˆā¸ˆā¸ąā¸”ā¸ā¸Ĩā¸¸āšˆā¸Ą", @@ -1010,12 +940,6 @@ "haptic_feedback_switch": "āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸­ā¸šā¸Ēā¸™ā¸­ā¸‡āšā¸šā¸šā¸Ēā¸ąā¸Ąā¸œā¸ąā¸Ē", "haptic_feedback_title": "ā¸ā¸˛ā¸Ŗā¸•ā¸­ā¸šā¸Ēā¸™ā¸­ā¸‡āšā¸šā¸šā¸Ēā¸ąā¸Ąā¸œā¸ąā¸Ē", "has_quota": "āš€ā¸Ģā¸Ĩā¸ˇā¸­ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆ", - "header_settings_add_header_tip": "Add Header", - "header_settings_field_validator_msg": "Value cannot be empty", - "header_settings_header_name_input": "Header name", - "header_settings_header_value_input": "Header value", - "headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request", - "headers_settings_tile_title": "Custom proxy headers", "hi_user": "ā¸Ēā¸§ā¸ąā¸Ē⏔ā¸ĩ⏄⏏⏓ {name} {email}", "hide_all_people": "ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "hide_gallery": "ā¸‹āšˆā¸­ā¸™ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", @@ -1039,8 +963,6 @@ "home_page_upload_err_limit": "ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”āš„ā¸”āš‰ā¸Ąā¸˛ā¸ā¸Ēā¸¸ā¸”ā¸„ā¸Ŗā¸ąāš‰ā¸‡ā¸Ĩ⏰ 30 ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "host": "āš‚ā¸Žā¸Ēā¸•āšŒ", "hour": "ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸā¸ā¸ąā¸š {person1} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸā¸ā¸ąā¸š {person1} āšā¸Ĩ⏰ {person2} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", @@ -1051,7 +973,6 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1} āšā¸Ĩ⏰ {person2} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1}, {person2},āšā¸Ĩ⏰ {person3} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1}, {person2}, āšā¸Ĩ⏰ {additionalCount, number} āšƒā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", - "image_saved_successfully": "Image saved", "image_viewer_page_state_provider_download_started": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™", "image_viewer_page_state_provider_download_success": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "image_viewer_page_state_provider_share_error": "āšā¸Šā¸ŖāšŒā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", @@ -1072,8 +993,6 @@ "night_at_midnight": "ā¸—ā¸¸ā¸āš€ā¸—ā¸ĩāšˆā¸ĸ⏇⏄⏎⏙", "night_at_twoam": "ā¸—ā¸¸ā¸ā¸§ā¸ąā¸™āš€ā¸§ā¸Ĩ⏞⏕ā¸ĩ 2" }, - "invalid_date": "Invalid date", - "invalid_date_format": "Invalid date format", "invite_people": "āš€ā¸Šā¸´ā¸ā¸œā¸šāš‰ā¸„ā¸™", "invite_to_album": "āš€ā¸Šā¸´ā¸āš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "items_count": "{count, plural, one {# ⏪⏞ā¸ĸ⏁⏞⏪} other {#⏪⏞ā¸ĸ⏁⏞⏪}}", @@ -1109,9 +1028,6 @@ "list": "⏪⏞ā¸ĸ⏁⏞⏪", "loading": "⏁⏺ā¸Ĩā¸ąā¸‡āš‚ā¸Ģā¸Ĩ⏔", "loading_search_results_failed": "āš‚ā¸Ģā¸Ĩā¸”ā¸œā¸Ĩā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", "location_picker_choose_on_map": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸šā¸™āšā¸œā¸™ā¸—ā¸ĩāšˆ", "location_picker_latitude_error": "ā¸ā¸Ŗā¸¸ā¸“ā¸˛āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸°ā¸•ā¸´ā¸ˆā¸šā¸•ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", @@ -1162,8 +1078,8 @@ "manage_your_devices": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "manage_your_oauth_connection": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ OAuth ⏂⏭⏇⏄⏏⏓", "map": "āšā¸œā¸™ā¸—ā¸ĩāšˆ", - "map_assets_in_bound": "{} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", - "map_assets_in_bounds": "{} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", + "map_assets_in_bound": "{count} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", + "map_assets_in_bounds": "{count} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "map_cannot_get_user_location": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ģā¸˛ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰", "map_location_dialog_yes": "āšƒā¸Šāšˆ", "map_location_picker_page_use_location": "āšƒā¸Šāš‰ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸™ā¸ĩāš‰", @@ -1177,9 +1093,9 @@ "map_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸œā¸™ā¸—ā¸ĩāšˆ", "map_settings_dark_mode": "āš‚ā¸Ģā¸Ąā¸”ā¸Ąā¸ˇā¸”", "map_settings_date_range_option_day": "24 ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡ā¸—ā¸ĩāšˆā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", - "map_settings_date_range_option_days": "{} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", + "map_settings_date_range_option_days": "{days} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", "map_settings_date_range_option_year": "⏛ā¸ĩ⏗ā¸ĩāšˆā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", - "map_settings_date_range_option_years": "{} ⏛ā¸ĩā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", + "map_settings_date_range_option_years": "{years} ⏛ā¸ĩā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", "map_settings_dialog_title": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸œā¸™ā¸—ā¸ĩāšˆ", "map_settings_include_show_archived": "ā¸Ŗā¸§ā¸Ąāš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", "map_settings_include_show_partners": "ā¸Ŗā¸˛ā¸Ąā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", @@ -1194,8 +1110,6 @@ "memories_setting_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ēā¸´āšˆā¸‡ā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“āš€ā¸Ģāš‡ā¸™āšƒā¸™ā¸„ā¸§ā¸˛ā¸Ąā¸—ā¸Ŗā¸‡ā¸ˆāšā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "memories_start_over": "āš€ā¸Ŗā¸´āšˆā¸Ąāšƒā¸Ģā¸Ąāšˆ", "memories_swipe_to_close": "ā¸›ā¸ąā¸”ā¸‚ā¸ļāš‰ā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸›ā¸´ā¸”", - "memories_year_ago": "A year ago", - "memories_years_ago": "{} years ago", "memory": "ā¸„ā¸§ā¸˛ā¸Ąā¸—ā¸Ŗā¸‡ā¸ˆā¸ŗ", "memory_lane_title": "ā¸„ā¸§ā¸˛ā¸Ąā¸—ā¸Ŗā¸‡ā¸ˆā¸ŗ {title}", "menu": "āš€ā¸Ąā¸™ā¸š", @@ -1210,7 +1124,6 @@ "missing": "⏂⏞⏔ā¸Ģ⏞ā¸ĸ", "model": "āš‚ā¸Ąāš€ā¸”ā¸Ĩ", "month": "āš€ā¸”ā¸ˇā¸­ā¸™", - "monthly_title_text_date_format": "MMMM y", "more": "āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą", "moved_to_trash": "ā¸—ā¸´āš‰ā¸‡ā¸Ĩā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āšā¸Ĩāš‰ā¸§", "multiselect_grid_edit_date_time_err_read_only": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸ⏧ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", @@ -1218,13 +1131,12 @@ "my_albums": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸‚ā¸­ā¸‡ā¸‰ā¸ąā¸™", "name": "ā¸Šā¸ˇāšˆā¸­", "name_or_nickname": "ā¸Šā¸ˇāšˆā¸­ā¸Ģā¸Ŗā¸ˇā¸­ā¸Šā¸ˇāšˆā¸­āš€ā¸Ĩāšˆā¸™", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", "never": "āš„ā¸Ąāšˆāš€ā¸„ā¸ĸ", "new_album": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšƒā¸Ģā¸Ąāšˆ", "new_api_key": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ API ⏄ā¸ĩā¸ĸāšŒāšƒā¸Ģā¸Ąāšˆ", "new_password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", "new_person": "ā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", + "new_pin_code": "⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) āšƒā¸Ģā¸Ąāšˆ", "new_user_created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰āšƒā¸Ģā¸Ąāšˆāšā¸Ĩāš‰ā¸§", "new_version_available": "ā¸Ąā¸ĩāš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšƒā¸Ģā¸Ąāšˆāšƒā¸Ģāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "newest_first": "āšƒā¸Ģā¸Ąāšˆā¸Ēā¸¸ā¸”ā¸āšˆā¸­ā¸™", @@ -1248,7 +1160,6 @@ "no_results_description": "ā¸Ĩā¸­ā¸‡āšƒā¸Šāš‰ā¸„ā¸ŗā¸žāš‰ā¸­ā¸‡ā¸Ģ⏪⏎⏭⏄⏺ā¸Ģā¸Ĩā¸ąā¸ā¸—ā¸ĩāšˆā¸ā¸§āš‰ā¸˛ā¸‡ā¸ā¸§āšˆā¸˛ā¸™ā¸ĩāš‰", "no_shared_albums_message": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš€ā¸žā¸ˇāšˆā¸­āšā¸Šā¸ŖāšŒā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸ā¸ąā¸šā¸„ā¸™āšƒā¸™āš€ā¸„ā¸Ŗā¸ˇā¸­ā¸‚āšˆā¸˛ā¸ĸ⏂⏭⏇⏄⏏⏓", "not_in_any_album": "āš„ā¸Ąāšˆā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšƒā¸” āš†", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ģ⏕⏏: ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸›āš‰ā¸˛ā¸ĸā¸ā¸ŗā¸ā¸ąā¸šā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸ā¸ąā¸šāš€ā¸™ā¸ˇāš‰ā¸­ā¸Ģ⏞⏗ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ āšƒā¸Ģāš‰āš€ā¸Ŗā¸ĩā¸ĸā¸āšƒā¸Šāš‰", "notes": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ģ⏕⏏", "notification_permission_dialog_content": "āš€ā¸žā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™ āš€ā¸‚āš‰ā¸˛ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸Ĩāš‰ā¸§ā¸ā¸”ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•", @@ -1258,14 +1169,12 @@ "notification_toggle_setting_description": "āš€ā¸›ā¸´ā¸”/⏛⏴⏔ ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™ā¸­ā¸ĩāš€ā¸Ąā¸Ĩ", "notifications": "ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", "notifications_setting_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", - "oauth": "OAuth", "official_immich_resources": "āšā¸Ģā¸Ĩāšˆā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ Immich ⏭ā¸ĸāšˆā¸˛ā¸‡āš€ā¸›āš‡ā¸™ā¸—ā¸˛ā¸‡ā¸ā¸˛ā¸Ŗ", "offline": "ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", "offline_paths": "āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸—ā¸ĩāšˆā¸•ā¸ąāš‰ā¸‡ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", "offline_paths_description": "⏜ā¸Ĩā¸Ĩā¸ąā¸žā¸˜āšŒāš€ā¸Ģā¸Ĩāšˆā¸˛ā¸™ā¸ĩāš‰ā¸­ā¸˛ā¸ˆāš€ā¸ā¸´ā¸”ā¸ˆā¸˛ā¸ā¸ā¸˛ā¸Ŗā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆāš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸Ģ⏙ā¸ļāšˆā¸‡ā¸‚ā¸­ā¸‡āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏠⏞ā¸ĸā¸™ā¸­ā¸ā¸”āš‰ā¸§ā¸ĸā¸•ā¸™āš€ā¸­ā¸‡", "ok": "⏕⏁ā¸Ĩ⏇", "oldest_first": "āš€ā¸Ŗā¸ĩā¸ĸā¸‡āš€ā¸āšˆā¸˛ā¸Ēā¸¸ā¸”ā¸āšˆā¸­ā¸™", - "on_this_device": "On this device", "onboarding": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "onboarding_privacy_description": "⏄⏏⏓ā¸Ĩā¸ąā¸ā¸Šā¸“ā¸° (āš„ā¸Ąāšˆā¸ˆā¸ŗāš€ā¸›āš‡ā¸™) ā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸•āš‰ā¸­ā¸‡ā¸­ā¸˛ā¸¨ā¸ąā¸ĸ⏚⏪⏴⏁⏞⏪⏠⏞ā¸ĸ⏙⏭⏁ āšā¸Ĩ⏰ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰ā¸•ā¸Ĩā¸­ā¸”āš€ā¸§ā¸Ĩā¸˛āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", "onboarding_theme_description": "āš€ā¸Ĩ⏎⏭⏁⏘ā¸ĩā¸Ąā¸Ēā¸ĩ ⏄⏏⏓ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩā¸‡āš„ā¸”āš‰āšƒā¸™ā¸ ā¸˛ā¸ĸā¸Ģā¸Ĩā¸ąā¸‡āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", @@ -1296,7 +1205,7 @@ "partner_page_partner_add_failed": "ā¸ā¸˛ā¸Ŗāš€ā¸žā¸´āšˆā¸Ąā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", "partner_page_select_partner": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", "partner_page_shared_to_title": "āšā¸Šā¸ŖāšŒā¸ā¸ąā¸š", - "partner_page_stop_sharing_content": "{} ā¸ˆā¸°āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", + "partner_page_stop_sharing_content": "{partner} ā¸ˆā¸°āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "partner_sharing": "āšā¸Šā¸ŖāšŒā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸žā¸˛ā¸ŖāšŒā¸—āš€ā¸™ā¸­ā¸ŖāšŒ", "partners": "ā¸žā¸˛ā¸ŖāšŒā¸—āš€ā¸™ā¸­ā¸ŖāšŒ", "password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", @@ -1341,6 +1250,9 @@ "photos_count": "{count, plural, one {{count, number} ā¸Ŗā¸šā¸›} other {{count, number} ā¸Ŗā¸šā¸›}}", "photos_from_previous_years": "ā¸ ā¸˛ā¸žā¸–āšˆā¸˛ā¸ĸā¸ˆā¸˛ā¸ā¸›ā¸ĩā¸āšˆā¸­ā¸™", "pick_a_location": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸•āšā¸˛āšā¸Ģā¸™āšˆā¸‡", + "pin_code_changed_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "pin_code_reset_successfully": "ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) āšƒā¸Ģā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "pin_code_setup_successfully": "ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "place": "ā¸Ē⏖⏞⏙⏗ā¸ĩāšˆ", "places": "ā¸Ē⏖⏞⏙⏗ā¸ĩāšˆ", "play": "āš€ā¸Ĩāšˆā¸™", @@ -1348,7 +1260,6 @@ "play_motion_photo": "āš€ā¸Ĩāšˆā¸™ā¸ ā¸˛ā¸žā¸§ā¸ąā¸•ā¸–ā¸¸āš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", "play_or_pause_video": "āš€ā¸Ĩāšˆā¸™ā¸Ģ⏪⏎⏭ā¸Ģā¸ĸ⏏⏔⏧⏴⏔ā¸ĩāš‚ā¸­", "port": "ā¸žā¸­ā¸ŖāšŒā¸•", - "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛", "preset": "ā¸žā¸Ŗā¸ĩāš€ā¸‹āš‡ā¸•", "preview": "ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡", @@ -1361,7 +1272,6 @@ "profile_drawer_client_out_of_date_major": "āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ā¸Ąā¸ĩā¸­ā¸ąā¸žāš€ā¸”ā¸• āš‚ā¸›ā¸Ŗā¸”ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸›āš‡ā¸™āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸Ģā¸Ĩā¸ąā¸ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "profile_drawer_client_out_of_date_minor": "āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ā¸Ąā¸ĩā¸­ā¸ąā¸žāš€ā¸”ā¸• āš‚ā¸›ā¸Ŗā¸”ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸›āš‡ā¸™āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸Ŗā¸­ā¸‡ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "profile_drawer_client_server_up_to_date": "āš„ā¸„ā¸Ĩāš€ā¸­ā¸™ā¸•āšŒāšā¸Ĩā¸°āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒāš€ā¸›āš‡ā¸™ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸Ąā¸ĩā¸­ā¸ąā¸žāš€ā¸”ā¸• āš‚ā¸›ā¸Ŗā¸”ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸›āš‡ā¸™āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸Ģā¸Ĩā¸ąā¸ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "profile_drawer_server_out_of_date_minor": "āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸Ąā¸ĩā¸­ā¸ąā¸žāš€ā¸”ā¸• āš‚ā¸›ā¸Ŗā¸”ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸›āš‡ā¸™āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸Ŗā¸­ā¸‡ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "profile_image_of_user": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāš‚ā¸›ā¸Ŗāš„ā¸Ÿā¸ĨāšŒā¸‚ā¸­ā¸‡ {user}", @@ -1370,7 +1280,7 @@ "public_share": "āšā¸Šā¸ŖāšŒāšā¸šā¸šā¸Ēā¸˛ā¸˜ā¸˛ā¸Ŗā¸“ā¸°", "purchase_account_info": "ā¸œā¸šāš‰ā¸Ēā¸™ā¸ąā¸šā¸Ē⏙⏏⏙", "purchase_activated_subtitle": "ā¸‚ā¸­ā¸šā¸„ā¸¸ā¸“ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ēā¸™ā¸ąā¸šā¸Ē⏙⏏⏙ Immich āšā¸Ĩā¸°ā¸‹ā¸­ā¸Ÿā¸•āšŒāšā¸§ā¸ŖāšŒāš€ā¸Ē⏪ā¸ĩ (Open source software)", - "purchase_activated_time": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date, date}", + "purchase_activated_time": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "purchase_activated_title": "⏪ā¸Ģā¸ąā¸Ēā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸–ā¸šā¸āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", "purchase_button_activate": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "purchase_button_buy": "ā¸‹ā¸ˇāš‰ā¸­", @@ -1413,7 +1323,6 @@ "recent": "ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "recent-albums": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "recent_searches": "ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", - "recently_added": "Recently added", "recently_added_page_title": "āš€ā¸žā¸´āšˆā¸Ąā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "refresh": "⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Š", "refresh_encoded_videos": "āš‚ā¸Ģā¸Ĩ⏔⏁⏞⏪ encoded ⏧⏴⏔ā¸ĩāš‚ā¸­āšƒā¸Ģā¸Ąāšˆ", @@ -1456,6 +1365,7 @@ "reset": "⏪ā¸ĩāš€ā¸‹āš‡ā¸•", "reset_password": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", "reset_people_visibility": "ā¸›ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ąā¸­ā¸‡āš€ā¸Ģāš‡ā¸™āšƒā¸Ģā¸Ąāšˆ", + "reset_pin_code": "ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) āšƒā¸Ģā¸Ąāšˆ", "reset_to_default": "⏁ā¸Ĩā¸ąā¸šāš„ā¸›ā¸„āšˆā¸˛āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™", "resolve_duplicates": "āšā¸āš‰āš„ā¸‚ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‹āš‰ā¸ŗā¸‹āš‰ā¸­ā¸™", "resolved_all_duplicates": "āšā¸āš‰āš„ā¸‚ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‹āš‰ā¸ŗā¸‹āš‰ā¸­ā¸™ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", @@ -1470,7 +1380,6 @@ "role_editor": "āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ąā¸ˇā¸­āšā¸āš‰āš„ā¸‚", "role_viewer": "ā¸”ā¸š", "save": "ā¸šā¸ąā¸™ā¸—ā¸ļ⏁", - "save_to_gallery": "Save to gallery", "saved_api_key": "ā¸šā¸ąā¸™ā¸—ā¸ļ⏁ API ⏄ā¸ĩā¸ĸāšŒ āšā¸Ĩāš‰ā¸§", "saved_profile": "āšā¸āš‰āš„ā¸‚āš‚ā¸›ā¸Ŗāš„ā¸Ÿā¸ĨāšŒā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "saved_settings": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", @@ -1492,31 +1401,17 @@ "search_city": "ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸•ā¸˛ā¸Ąāš€ā¸Ąā¸ˇā¸­ā¸‡", "search_country": "ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸•ā¸˛ā¸Ąā¸›ā¸Ŗā¸°āš€ā¸—ā¸¨", "search_filter_apply": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸•ā¸ąā¸§ā¸ā¸Ŗā¸­ā¸‡", - "search_filter_camera_title": "Select camera type", - "search_filter_date": "Date", - "search_filter_date_interval": "{start} to {end}", - "search_filter_date_title": "Select a date range", "search_filter_display_option_not_in_album": "āš„ā¸Ąāšˆā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", - "search_filter_display_options": "Display Options", - "search_filter_filename": "Search by file name", - "search_filter_location": "Location", - "search_filter_location_title": "Select location", - "search_filter_media_type": "Media Type", - "search_filter_media_type_title": "Select media type", - "search_filter_people_title": "Select people", "search_for": "ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸š", "search_for_existing_person": "ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆ", - "search_no_more_result": "No more results", "search_no_people": "āš„ā¸Ąāšˆā¸žā¸šā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏄⏙", "search_no_people_named": "āš„ā¸Ąāšˆā¸žā¸š \"{name}\"", - "search_no_result": "No results found, try a different search term or combination", "search_options": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", "search_page_categories": "ā¸Ģā¸Ąā¸§ā¸”ā¸Ģā¸Ąā¸šāšˆ", "search_page_motion_photos": "ā¸ ā¸˛ā¸žāš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", "search_page_no_objects": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ⏪⏞ā¸ĸ⏁⏞⏪", "search_page_no_places": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ē⏖⏞⏙⏗ā¸ĩāšˆ", "search_page_screenshots": "āšā¸„ā¸›ā¸Ģā¸™āš‰ā¸˛ā¸ˆā¸­", - "search_page_search_photos_videos": "Search for your photos and videos", "search_page_selfies": "āš€ā¸‹ā¸Ĩ⏟ā¸ĩāšˆ", "search_page_things": "ā¸Ēā¸´āšˆā¸‡ā¸‚ā¸­ā¸‡", "search_page_view_all_button": "ā¸”ā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", @@ -1555,7 +1450,6 @@ "selected_count": "{count, plural, other {# āš€ā¸Ĩā¸ˇā¸­ā¸āšā¸Ĩāš‰ā¸§}}", "send_message": "ā¸Ēāšˆā¸‡ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ą", "send_welcome_email": "ā¸Ēāšˆā¸‡ā¸­ā¸ĩāš€ā¸Ąā¸Ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸š", - "server_endpoint": "Server Endpoint", "server_info_box_app_version": "āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšā¸­ā¸ž", "server_info_box_server_url": "URL āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "server_offline": "Server ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", @@ -1576,28 +1470,26 @@ "setting_image_viewer_preview_title": "āš‚ā¸Ģā¸Ĩā¸”ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡", "setting_image_viewer_title": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "setting_languages_apply": "ā¸šā¸ąā¸™ā¸—ā¸ļ⏁", - "setting_languages_subtitle": "Change the app's language", "setting_languages_title": "ā¸ ā¸˛ā¸Šā¸˛", - "setting_notifications_notify_failures_grace_period": "āšā¸ˆāš‰ā¸‡ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧: {}", - "setting_notifications_notify_hours": "{} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", + "setting_notifications_notify_failures_grace_period": "āšā¸ˆāš‰ā¸‡ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧: {duration}", + "setting_notifications_notify_hours": "{count} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", "setting_notifications_notify_immediately": "āš‚ā¸”ā¸ĸā¸—ā¸ąā¸™ā¸—ā¸ĩ", - "setting_notifications_notify_minutes": "{} ⏙⏞⏗ā¸ĩ", + "setting_notifications_notify_minutes": "{count} ⏙⏞⏗ā¸ĩ", "setting_notifications_notify_never": "āš„ā¸Ąāšˆāš€ā¸„ā¸ĸ", - "setting_notifications_notify_seconds": "{} ⏧⏴⏙⏞⏗ā¸ĩ", + "setting_notifications_notify_seconds": "{count} ⏧⏴⏙⏞⏗ā¸ĩ", "setting_notifications_single_progress_subtitle": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸„ā¸§ā¸˛ā¸Ąā¸„ā¸ˇā¸šā¸Ģā¸™āš‰ā¸˛ā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āš‚ā¸”ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”ā¸•āšˆā¸­ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪", "setting_notifications_single_progress_title": "āšā¸Ē⏔⏇⏪⏞ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔ā¸Ē⏖⏞⏙⏰⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", "setting_notifications_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", "setting_notifications_total_progress_subtitle": "ā¸„ā¸§ā¸˛ā¸Ąā¸„ā¸ˇā¸šā¸Ģā¸™āš‰ā¸˛ā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āš‚ā¸”ā¸ĸā¸Ŗā¸§ā¸Ą (āš€ā¸Ēā¸Ŗāš‡ā¸ˆā¸Ēā¸´āš‰ā¸™/ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”)", "setting_notifications_total_progress_title": "āšā¸Ē⏔⏇ā¸Ē⏖⏞⏙⏰⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™āš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "setting_video_viewer_looping_title": "⏧⏙ā¸Ĩā¸šā¸›", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛", "settings_require_restart": "⏁⏪⏏⏓⏞⏪ā¸ĩā¸Ēā¸•ā¸˛ā¸ŖāšŒā¸— Immmich āš€ā¸žā¸ˇāšˆā¸­āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛", "settings_saved": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸Ĩāš‰ā¸§", + "setup_pin_code": "ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "share": "āšā¸Šā¸ŖāšŒ", "share_add_photos": "āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", - "share_assets_selected": "{} ā¸–ā¸šā¸āš€ā¸Ĩ⏎⏭⏁", + "share_assets_selected": "{count} ā¸–ā¸šā¸āš€ā¸Ĩ⏎⏭⏁", "share_dialog_preparing": "⏁⏺ā¸Ĩā¸ąā¸‡āš€ā¸•ā¸Ŗā¸ĩā¸ĸā¸Ą...", "shared": "āšā¸Šā¸ŖāšŒ", "shared_album_activities_input_disable": "ā¸„ā¸­ā¸Ąāš€ā¸Ąā¸™ā¸•āšŒā¸–ā¸šā¸ā¸›ā¸´ā¸”", @@ -1611,39 +1503,36 @@ "shared_by_user": "āšā¸Šā¸ŖāšŒāš‚ā¸”ā¸ĸ {user}", "shared_by_you": "āšā¸Šā¸ŖāšŒāš‚ā¸”ā¸ĸ⏄⏏⏓", "shared_from_partner": "ā¸Ŗā¸šā¸›ā¸ˆā¸˛ā¸ {partner}", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒ", "shared_link_clipboard_copied_massage": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁ā¸Ĩ⏇⏄ā¸Ĩā¸´ā¸›ā¸šā¸­ā¸ŖāšŒā¸”", - "shared_link_clipboard_text": "ā¸Ĩā¸´ā¸‡ā¸āšŒ: {}\n⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™: {}", + "shared_link_clipboard_text": "ā¸Ĩā¸´ā¸‡ā¸āšŒ: {link}\n⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™: {password}", "shared_link_create_error": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔⏂⏓⏰ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ĩā¸´ā¸‡ā¸āšŒāšā¸Šā¸ŖāšŒ", "shared_link_edit_description_hint": "āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”ā¸ā¸˛ā¸Ŗāšā¸Šā¸ŖāšŒ", "shared_link_edit_expire_after_option_day": "1 ā¸§ā¸ąā¸™", - "shared_link_edit_expire_after_option_days": "{} ā¸§ā¸ąā¸™", + "shared_link_edit_expire_after_option_days": "{count} ā¸§ā¸ąā¸™", "shared_link_edit_expire_after_option_hour": "1 ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", - "shared_link_edit_expire_after_option_hours": "{} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", + "shared_link_edit_expire_after_option_hours": "{count} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", "shared_link_edit_expire_after_option_minute": "1 ⏙⏞⏗ā¸ĩ", - "shared_link_edit_expire_after_option_minutes": "{} ⏙⏞⏗ā¸ĩ", - "shared_link_edit_expire_after_option_months": "{} āš€ā¸”ā¸ˇā¸­ā¸™", - "shared_link_edit_expire_after_option_year": "{} ⏛ā¸ĩ", + "shared_link_edit_expire_after_option_minutes": "{count} ⏙⏞⏗ā¸ĩ", + "shared_link_edit_expire_after_option_months": "{count} āš€ā¸”ā¸ˇā¸­ā¸™", + "shared_link_edit_expire_after_option_year": "{count} ⏛ā¸ĩ", "shared_link_edit_password_hint": "⏁⏪⏭⏁⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšā¸Šā¸ŖāšŒ", "shared_link_edit_submit_button": "ā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸Ĩā¸´ā¸‡ā¸āšŒ", "shared_link_error_server_url_fetch": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ŗā¸ąā¸š URL ā¸ˆā¸˛ā¸āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", - "shared_link_expires_day": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ā¸§ā¸ąā¸™", - "shared_link_expires_days": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ā¸§ā¸ąā¸™", - "shared_link_expires_hour": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", - "shared_link_expires_hours": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", - "shared_link_expires_minute": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ⏙⏞⏗ā¸ĩ", - "shared_link_expires_minutes": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ⏙⏞⏗ā¸ĩ", + "shared_link_expires_day": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ā¸§ā¸ąā¸™", + "shared_link_expires_days": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ā¸§ā¸ąā¸™", + "shared_link_expires_hour": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", + "shared_link_expires_hours": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", + "shared_link_expires_minute": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ⏙⏞⏗ā¸ĩ", + "shared_link_expires_minutes": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ⏙⏞⏗ā¸ĩ", "shared_link_expires_never": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸§ā¸ąā¸™ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏", - "shared_link_expires_second": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ⏧⏴⏙⏞⏗ā¸ĩ", - "shared_link_expires_seconds": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {} ⏧⏴⏙⏞⏗ā¸ĩ", + "shared_link_expires_second": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ⏧⏴⏙⏞⏗ā¸ĩ", + "shared_link_expires_seconds": "ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸā¸¸āšƒā¸™ā¸­ā¸ĩ⏁ {count} ⏧⏴⏙⏞⏗ā¸ĩ", "shared_link_individual_shared": "āšā¸Šā¸ŖāšŒā¸Ŗā¸˛ā¸ĸā¸šā¸¸ā¸„ā¸„ā¸Ĩ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "⏚⏪⏴ā¸Ģ⏞⏪ā¸Ĩā¸´ā¸‡ā¸āšŒ", "shared_link_options": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒ", "shared_links": "ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒ", "shared_links_description": "āšā¸šāšˆā¸‡ā¸›ā¸ąā¸™ā¸Ŗā¸šā¸›āšā¸Ĩ⏰⏧ā¸ĩ⏔ā¸ĩāš‚ā¸­ā¸”āš‰ā¸§ā¸ĸā¸Ĩā¸´āš‰ā¸‡ā¸„āšŒ", - "shared_with_me": "Shared with me", "shared_with_partner": "āšā¸Šā¸ŖāšŒā¸ā¸ąā¸š {partner}", "sharing": "ā¸ā¸˛ā¸Ŗāšā¸Šā¸ŖāšŒ", "sharing_enter_password": "āš‚ā¸›ā¸Ŗā¸”ā¸›āš‰ā¸­ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāš€ā¸›ā¸´ā¸”ā¸”ā¸šā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰", @@ -1698,7 +1587,6 @@ "stack_duplicates": "⏙⏺ā¸Ēā¸´āšˆā¸‡ā¸—ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸Ąā¸˛ā¸‹āš‰ā¸­ā¸™ā¸­ā¸ĸā¸šāšˆā¸”āš‰ā¸§ā¸ĸā¸ā¸ąā¸™", "stack_select_one_photo": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸Ŗā¸šā¸›ā¸Ģā¸Ĩā¸ąā¸ā¸Ģ⏙ā¸ļāšˆā¸‡ā¸Ŗā¸šā¸›ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸šā¸›ā¸—ā¸ĩāšˆā¸‹āš‰ā¸­ā¸™ā¸ā¸ąā¸™ā¸™ā¸ĩāš‰", "stack_selected_photos": "ā¸‹āš‰ā¸­ā¸™ā¸Ŗā¸šā¸›ā¸—ā¸ĩāšˆā¸–ā¸šā¸āš€ā¸Ĩ⏎⏭⏁", - "stacktrace": "", "start": "āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™", "start_date": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš€ā¸Ŗā¸´āšˆā¸Ą", "state": "ā¸Ŗā¸ąā¸", @@ -1718,9 +1606,6 @@ "support_third_party_description": "ā¸ā¸˛ā¸Ŗā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ Immich ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸–ā¸šā¸ā¸ˆā¸ąā¸”ā¸—ā¸ŗāšā¸žāš‡ā¸āš€ā¸ā¸ˆāš‚ā¸”ā¸ĸā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ēā¸˛ā¸Ą ā¸›ā¸ąā¸ā¸Ģ⏞⏗ā¸ĩāšˆā¸„ā¸¸ā¸“ā¸žā¸šā¸­ā¸˛ā¸ˆāš€ā¸ā¸´ā¸”ā¸ˆā¸˛ā¸āšā¸žāš‡ā¸āš€ā¸ā¸ˆā¸”ā¸ąā¸‡ā¸ā¸Ĩāšˆā¸˛ā¸§ ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™āš‚ā¸›ā¸Ŗā¸”āšā¸ˆāš‰ā¸‡ā¸›ā¸ąā¸ā¸Ģ⏞⏗ā¸ĩāšˆāš€ā¸ā¸´ā¸”ā¸‚ā¸ļāš‰ā¸™ā¸ā¸ąā¸šā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ēā¸˛ā¸Ąā¸āšˆā¸­ā¸™āš‚ā¸”ā¸ĸāšƒā¸Šāš‰ā¸Ĩā¸´ā¸‡ā¸āšŒā¸”āš‰ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸‡", "swap_merge_direction": "ā¸Ēā¸Ĩā¸ąā¸šā¸”āš‰ā¸˛ā¸™ā¸Ŗā¸§ā¸Ą", "sync": "ā¸‹ā¸´ā¸‡ā¸„āšŒ", - "sync_albums": "Sync albums", - "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", - "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "āšā¸—āš‡ā¸", "tag_created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸—āš‡ā¸: {tag}", "tag_not_found_question": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ģā¸˛āšā¸—āš‡ā¸āš„ā¸”āš‰āšƒā¸Šāšˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸—āš‡ā¸āšƒā¸Ģā¸Ąāšˆ", @@ -1732,14 +1617,9 @@ "theme_selection": "ā¸ā¸˛ā¸Ŗāš€ā¸Ĩ⏎⏭⏁⏘ā¸ĩā¸Ą", "theme_selection_description": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸˜ā¸ĩā¸Ąāšƒā¸Ģāš‰ā¸Ēā¸§āšˆā¸˛ā¸‡ā¸Ģā¸Ŗā¸ˇā¸­ā¸Ąā¸ˇā¸”āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ ā¸­ā¸´ā¸‡ā¸ˆā¸˛ā¸ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡āš€ā¸šā¸Ŗā¸˛ā¸§āšŒāš€ā¸‹ā¸­ā¸ŖāšŒā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "theme_setting_asset_list_storage_indicator_title": "āšā¸Ēā¸”ā¸‡ā¸•ā¸ąā¸§ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸šā¸™ā¸•ā¸˛ā¸Ŗā¸˛ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪", - "theme_setting_asset_list_tiles_per_row_title": "ā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸•āšˆā¸­āšā¸–ā¸§ ({})", - "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.", - "theme_setting_colorful_interface_title": "Colorful interface", + "theme_setting_asset_list_tiles_per_row_title": "ā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸•āšˆā¸­āšā¸–ā¸§ ({count})", "theme_setting_image_viewer_quality_subtitle": "ā¸›ā¸Ŗā¸ąā¸šā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸•ā¸ąā¸§ā¸”ā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔", "theme_setting_image_viewer_quality_title": "ā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸•ā¸ąā¸‡ā¸”ā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", - "theme_setting_primary_color_subtitle": "Pick a color for primary actions and accents.", - "theme_setting_primary_color_title": "Primary color", - "theme_setting_system_primary_color_title": "Use system color", "theme_setting_system_theme_switch": "ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ (ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ŗā¸°ā¸šā¸š)", "theme_setting_theme_subtitle": "āš€ā¸Ĩ⏎⏭⏁⏘ā¸ĩā¸Ąā¸‚ā¸­ā¸‡āšā¸­ā¸ž", "theme_setting_three_stage_loading_subtitle": "ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩā¸”āšā¸šā¸šā¸Ēā¸˛ā¸Ąā¸‚ā¸ąāš‰ā¸™ā¸•ā¸­ā¸™ā¸­ā¸˛ā¸ˆāš€ā¸žā¸´āšˆā¸Ąā¸›ā¸Ŗā¸°ā¸Ēā¸´ā¸—ā¸˜ā¸´ā¸ ā¸˛ā¸žāšƒā¸™ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩā¸”āšā¸•āšˆā¸ˆā¸°ā¸—ā¸ŗāšƒā¸Ģāš‰āš‚ā¸Ģā¸Ĩā¸”āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‚āšˆā¸˛ā¸ĸāš€ā¸žā¸´āšˆā¸Ąā¸‚ā¸ļāš‰ā¸™ā¸Ąā¸˛ā¸", @@ -1762,17 +1642,18 @@ "trash": "ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "trash_all": "ā¸—ā¸´āš‰ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "trash_count": "{count, number} āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", - "trash_emptied": "Emptied trash", "trash_no_results_message": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ģ⏪⏎⏭⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸Ĩ⏚⏈⏰⏭ā¸ĸā¸šāšˆā¸—ā¸ĩāšˆā¸™ā¸ĩāšˆ", "trash_page_delete_all": "ā¸Ĩā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "trash_page_empty_trash_dialog_content": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸´āš‰ā¸‡ā¸‚ā¸ĸā¸°ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸” ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸žā¸§ā¸ā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸–ā¸šā¸ā¸Ĩ⏚⏈⏞⏁ Immich ⏖⏞⏧⏪", - "trash_page_info": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏗ā¸ĩāšˆā¸—ā¸´āš‰ā¸‡ā¸ˆā¸°ā¸–ā¸šā¸ā¸Ĩā¸šā¸–ā¸˛ā¸§ā¸Ŗā¸Ģā¸Ĩā¸ąā¸‡ {} ā¸§ā¸ąā¸™", + "trash_page_info": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏗ā¸ĩāšˆā¸—ā¸´āš‰ā¸‡ā¸ˆā¸°ā¸–ā¸šā¸ā¸Ĩā¸šā¸–ā¸˛ā¸§ā¸Ŗā¸Ģā¸Ĩā¸ąā¸‡ {days} ā¸§ā¸ąā¸™", "trash_page_no_assets": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸™ā¸‚ā¸ĸ⏰", "trash_page_restore_all": "ā¸ā¸šāš‰ā¸„ā¸ˇā¸™ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "trash_page_select_assets_btn": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪", - "trash_page_title": "⏂ā¸ĸ⏰ ({})", + "trash_page_title": "⏂ā¸ĸ⏰ ({count})", "trashed_items_will_be_permanently_deleted_after": "⏪⏞ā¸ĸ⏁⏞⏪⏗ā¸ĩāšˆā¸–ā¸šā¸ā¸Ĩā¸šā¸ˆā¸°ā¸–ā¸šā¸ā¸Ĩā¸šā¸—ā¸´āš‰ā¸‡ā¸ ā¸˛ā¸ĸāšƒā¸™ {days, plural, one {# ā¸§ā¸ąā¸™} other {# ā¸§ā¸ąā¸™}}.", "type": "ā¸›ā¸Ŗā¸°āš€ā¸ ā¸—", + "unable_to_change_pin_code": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", + "unable_to_setup_pin_code": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "unarchive": "ā¸™ā¸ŗā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", "unfavorite": "ā¸™ā¸ŗā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", "unhide_person": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", @@ -1796,14 +1677,12 @@ "upload_status_errors": "ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "upload_status_uploaded": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āšā¸Ĩāš‰ā¸§", "upload_success": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ, ⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Šā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰āšƒā¸Ģā¸Ąāšˆā¸„ā¸¸ā¸“ā¸ˆā¸°āš€ā¸Ģāš‡ā¸™ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš€ā¸žā¸´āšˆā¸Ąā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", "usage": "ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", - "use_current_connection": "use current connection", "use_custom_date_range": "āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡ā¸Šāšˆā¸§ā¸‡āš€ā¸§ā¸Ĩ⏞", "user": "ā¸œā¸šāš‰āšƒā¸Šāš‰", "user_id": "āš„ā¸­ā¸”ā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰", + "user_pin_code_settings": "⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", + "user_pin_code_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "user_purchase_settings": "ā¸‹ā¸ˇāš‰ā¸­", "user_purchase_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸‹ā¸ˇāš‰ā¸­", "user_role_set": "ā¸•ā¸ąāš‰ā¸‡ {role} āšƒā¸Ģāš‰ā¸ā¸ąā¸š {user}", @@ -1814,7 +1693,6 @@ "users": "ā¸œā¸šāš‰āšƒā¸Šāš‰", "utilities": "āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ąā¸ˇā¸­", "validate": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚", - "validate_endpoint_error": "Please enter a valid URL", "variables": "ā¸•ā¸ąā¸§āšā¸›ā¸Ŗ", "version": "ā¸Ŗā¸¸āšˆā¸™", "version_announcement_message": "ā¸Ēā¸§ā¸ąā¸Ē⏔ā¸ĩ! Immich āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšƒā¸Ģā¸Ąāšˆā¸žā¸Ŗāš‰ā¸­ā¸Ąāšƒā¸Ģāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšā¸Ĩāš‰ā¸§ āš‚ā¸›ā¸Ŗā¸”āšƒā¸Šāš‰āš€ā¸§ā¸Ĩ⏞ā¸Ēā¸ąā¸ā¸„ā¸Ŗā¸šāšˆāš€ā¸žā¸ˇāšˆā¸­ā¸­āšˆā¸˛ā¸™ ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ģā¸•ā¸¸ā¸ā¸˛ā¸Ŗāš€ā¸œā¸ĸāšā¸žā¸Ŗāšˆ āš€ā¸žā¸ˇāšˆā¸­āšƒā¸Ģāš‰āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš€ā¸”ā¸•āšā¸Ĩāš‰ā¸§ āš€ā¸žā¸ˇāšˆā¸­ā¸›āš‰ā¸­ā¸‡ā¸ā¸ąā¸™ā¸ā¸˛ā¸Ŗā¸ā¸ŗā¸Ģā¸™ā¸”ā¸„āšˆā¸˛ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ āš‚ā¸”ā¸ĸāš€ā¸‰ā¸žā¸˛ā¸°ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸ĸā¸´āšˆā¸‡ā¸Ģā¸˛ā¸ā¸„ā¸¸ā¸“āšƒā¸Šāš‰ WatchTower ā¸Ģ⏪⏎⏭⏁ā¸Ĩāš„ā¸ā¸­ā¸ˇāšˆā¸™āš† ⏗ā¸ĩāšˆā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸­ā¸´ā¸™ā¸Ēāšā¸•ā¸™ā¸‹āšŒ Immich ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", diff --git a/i18n/tr.json b/i18n/tr.json index 35c90b7f90..96032cb9a8 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -53,6 +53,7 @@ "confirm_email_below": "Onaylamak için aşağıya {email} yazÄąn", "confirm_reprocess_all_faces": "TÃŧm yÃŧzleri tekrardan işlemek istediğinize emin misiniz? Bu işlem isimlendirilmiş insanlarÄą da silecek.", "confirm_user_password_reset": "{user} adlÄą kullanÄącÄąnÄąn şifresini sÄąfÄąrlamak istediğinize emin misiniz?", + "confirm_user_pin_code_reset": "{user} adlÄą kullanÄącÄąnÄąn PIN kodunu sÄąfÄąrlamak istediğinize emin misiniz?", "create_job": "GÃļrev oluştur", "cron_expression": "Cron İfadesi", "cron_expression_description": "Cron formatÄąnÄą kullanarak tarama aralığınÄą belirle. Daha fazla bilgi için Ãļrneğin Crontab Guru’ya bakÄąn", @@ -187,20 +188,13 @@ "oauth_auto_register": "Otomatik kayÄąt", "oauth_auto_register_description": "OAuth ile giriş yapan yeni kullanÄącÄąlarÄą otomatik kaydet", "oauth_button_text": "Buton yazÄąsÄą", - "oauth_client_id": "İstemci ID", - "oauth_client_secret": "Gizli İstemci AnahtarÄą", "oauth_enable_description": "OAuth ile giriş yap", - "oauth_issuer_url": "YayÄąnlayÄącÄą URL", "oauth_mobile_redirect_uri": "Mobil yÃļnlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanÄąlacak YÃļnlendirme Adresi", "oauth_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayÄącÄąsÄą olduğunda etkinleştir, Ãļrneğin '{callback}'", - "oauth_profile_signing_algorithm": "Profil imzalama algoritmasÄą", - "oauth_profile_signing_algorithm_description": "KullanÄącÄąnÄąn profilini imzalarken kullanÄąlacak gÃŧvenlik algoritmasÄą.", - "oauth_scope": "Kapsam", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth giriş ayarlarÄąnÄą yÃļnet", "oauth_settings_more_details": "Bu Ãļzellik hakkÄąnda daha fazla bilgi için bu sayfayÄą ziyaret edin DÃļkÃŧmanlar.", - "oauth_signing_algorithm": "İmzalama algoritmasÄą", "oauth_storage_label_claim": "Depolama etiketi talebi", "oauth_storage_label_claim_description": "KullanÄącÄąnÄąn dosyalarÄąnÄą depolarken kullanÄąlan alt klasÃļrÃŧn adÄąnÄą belirlerken kulanÄąlacak değer (en: OAuth claim).", "oauth_storage_quota_claim": "Depolama kotasÄą talebi", @@ -366,7 +360,7 @@ "admin_password": "YÃļnetici Şifresi", "administration": "YÃļnetim", "advanced": "Gelişmiş", - "advanced_settings_log_level_title": "GÃŧnlÃŧk dÃŧzeyi: {}", + "advanced_settings_log_level_title": "GÃŧnlÃŧk dÃŧzeyi: {level}", "advanced_settings_prefer_remote_subtitle": "BazÄą cihazlar, cihazdaki Ãļğelerin kÃŧçÃŧk resimlerini gÃļstermekte çok yavaştÄąr. Bunun yerine sunucudaki kÃŧçÃŧk resimleri gÃļstermek için bu ayarÄą etkinleştirin.", "advanced_settings_prefer_remote_title": "Uzak gÃļrÃŧntÃŧleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte gÃļndermesi gereken proxy header'larÄą tanÄąmlayÄąn", @@ -395,9 +389,9 @@ "album_remove_user_confirmation": "{user} kullanÄącÄąsÄąnÄą kaldÄąrmak istediğinize emin misiniz?", "album_share_no_users": "GÃļrÃŧnÃŧşe gÃļre bu albÃŧmÃŧ tÃŧm kullanÄącÄąlarla paylaştÄąnÄąz veya paylaşacak herhangi bir başka kullanÄącÄąnÄąz yok.", "album_thumbnail_card_item": "1 Ãļğe", - "album_thumbnail_card_items": "{} Ãļğe", + "album_thumbnail_card_items": "{count} Ãļğe", "album_thumbnail_card_shared": " ¡ PaylaÅŸÄąldÄą", - "album_thumbnail_shared_by": "{} tarafÄąndan paylaÅŸÄąldÄą", + "album_thumbnail_shared_by": "{user} tarafÄąndan paylaÅŸÄąldÄą", "album_updated": "AlbÃŧm gÃŧncellendi", "album_updated_setting_description": "PaylaÅŸÄąlan bir albÃŧme yeni bir varlÄąk eklendiğinde email bildirimi alÄąn", "album_user_left": "{album}den ayrÄąldÄąnÄąz", @@ -435,7 +429,7 @@ "archive": "Arşiv", "archive_or_unarchive_photo": "FotoğrafÄą arşivle/arşivden Ã§Äąkar", "archive_page_no_archived_assets": "Arşivlenmiş Ãļğe bulunamadÄą", - "archive_page_title": "Arşiv ({})", + "archive_page_title": "Arşiv ({count})", "archive_size": "Arşiv boyutu", "archive_size_description": "İndirmeler için arşiv boyutunu yapÄąlandÄąrÄąn (GiB cinsinden)", "archived": "Arşivlenen", @@ -472,18 +466,18 @@ "assets_added_to_album_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} albÃŧme eklendi", "assets_added_to_name_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} {hasName, select, true {{name}} other {yeni albÃŧm}} içine eklendi", "assets_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}}", - "assets_deleted_permanently": "{} Ãļğe kalÄącÄą olarak silindi", - "assets_deleted_permanently_from_server": "{} Ãļğe kalÄącÄą olarak Immich sunucusundan silindi", + "assets_deleted_permanently": "{count} Ãļğe kalÄącÄą olarak silindi", + "assets_deleted_permanently_from_server": "{count} Ãļğe kalÄącÄą olarak Immich sunucusundan silindi", "assets_moved_to_trash_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} çÃļpe taÅŸÄąndÄą", "assets_permanently_deleted_count": "KalÄącÄą olarak silindi {count, plural, one {# varlÄąk} other {# varlÄąklar}}", "assets_removed_count": "KaldÄąrÄąldÄą {count, plural, one {# varlÄąk} other {# varlÄąklar}}", - "assets_removed_permanently_from_device": "{} Ãļğe cihazÄąnÄązdan kalÄącÄą olarak silindi", + "assets_removed_permanently_from_device": "{count} Ãļğe cihazÄąnÄązdan kalÄącÄą olarak silindi", "assets_restore_confirmation": "TÃŧm çÃļp kutusundaki varlÄąklarÄąnÄązÄą geri yÃŧklemek istediğinizden emin misiniz? Bu işlemi geri alamazsÄąnÄąz! AyrÄąca, çevrim dÄąÅŸÄą olan varlÄąklarÄąn bu şekilde geri yÃŧklenemeyeceğini unutmayÄąn.", "assets_restored_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}} geri yÃŧklendi", - "assets_restored_successfully": "{} Ãļğe geri yÃŧklendi", - "assets_trashed": "{} Ãļğe çÃļpe atÄąldÄą", + "assets_restored_successfully": "{count} Ãļğe geri yÃŧklendi", + "assets_trashed": "{count} Ãļğe çÃļpe atÄąldÄą", "assets_trashed_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}} çÃļp kutusuna taÅŸÄąndÄą", - "assets_trashed_from_server": "{} Ãļğe Immich sunucusunda çÃļpe atÄąldÄą", + "assets_trashed_from_server": "{count} Ãļğe Immich sunucusunda çÃļpe atÄąldÄą", "assets_were_part_of_album_count": "{count, plural, one {VarlÄąk zaten} other {VarlÄąklar zaten}} albÃŧmÃŧn parçasÄąydÄą", "authorized_devices": "Yetki Verilmiş Cihazlar", "automatic_endpoint_switching_subtitle": "Belirlenmiş Wi-Fi ağına bağlÄąyken yerel olarak bağlanÄąp başka yerlerde alternatif bağlantÄąyÄą kullan", @@ -492,7 +486,7 @@ "back_close_deselect": "Geri, kapat veya seçimi kaldÄąr", "background_location_permission": "Arka plan konum izni", "background_location_permission_content": "Arka planda çalÄąÅŸÄąrken ağ değiştirmek için Immich'in *her zaman* tam konum erişimine sahip olmasÄą gerekir, bÃļylece uygulama Wi-Fi ağınÄąn adÄąnÄą okuyabilir", - "backup_album_selection_page_albums_device": "Cihazdaki albÃŧmler ({})", + "backup_album_selection_page_albums_device": "Cihazdaki albÃŧmler ({count})", "backup_album_selection_page_albums_tap": "Seçmek için dokunun, hariç tutmak için çift dokunun", "backup_album_selection_page_assets_scatter": "VarlÄąklar birden fazla albÃŧme dağılabilir. Bu nedenle, yedekleme işlemi sÄąrasÄąnda albÃŧmler dahil edilebilir veya hariç tutulabilir.", "backup_album_selection_page_select_albums": "AlbÃŧm seç", @@ -501,11 +495,11 @@ "backup_all": "TÃŧmÃŧ", "backup_background_service_backup_failed_message": "Yedekleme başarÄąsÄąz. Tekrar deneniyor...", "backup_background_service_connection_failed_message": "Sunucuya bağlanÄąlamadÄą. Tekrar deneniyor...", - "backup_background_service_current_upload_notification": "{} yÃŧkleniyor", + "backup_background_service_current_upload_notification": "{filename} yÃŧkleniyor", "backup_background_service_default_notification": "Yeni Ãļğeler kontrol ediliyorâ€Ļ", "backup_background_service_error_title": "Yedekleme hatasÄą", "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor...", - "backup_background_service_upload_failure_notification": "{} yÃŧklemesi başarÄąsÄąz oldu", + "backup_background_service_upload_failure_notification": "{filename} yÃŧklemesi başarÄąsÄąz oldu", "backup_controller_page_albums": "Yedekleme AlbÃŧmleri", "backup_controller_page_background_app_refresh_disabled_content": "Arka planda yedeklemeyi kullanabilmek için Ayarlar > Genel > Arka Planda Uygulama Yenileme bÃļlÃŧmÃŧnden arka planda uygulama yenilemeyi etkinleştirin.", "backup_controller_page_background_app_refresh_disabled_title": "Arka planda uygulama yenileme devre dÄąÅŸÄą bÄąrakÄąldÄą", @@ -516,7 +510,7 @@ "backup_controller_page_background_battery_info_title": "Pil optimizasyonlarÄą", "backup_controller_page_background_charging": "Sadece şarjda", "backup_controller_page_background_configure_error": "Arka plan hizmeti yapÄąlandÄąrÄąlamadÄą", - "backup_controller_page_background_delay": "Yeni Ãļğelerin yedeklemesini geciktir: {}", + "backup_controller_page_background_delay": "Yeni Ãļğelerin yedeklemesini geciktir: {duration}", "backup_controller_page_background_description": "UygulamayÄą açmaya gerek kalmadan yeni Ãļğeleri otomatik olarak yedeklemek için arka plan hizmetini aÃ§Äąn", "backup_controller_page_background_is_off": "Otomatik arka planda yedekleme kapalÄą", "backup_controller_page_background_is_on": "Otomatik arka planda yedekleme aÃ§Äąk", @@ -524,14 +518,13 @@ "backup_controller_page_background_turn_on": "Arka plan hizmetini aç", "backup_controller_page_background_wifi": "Sadece Wi-Fi", "backup_controller_page_backup": "Yedekle", - "backup_controller_page_backup_selected": "Seçili:", + "backup_controller_page_backup_selected": "Seçili: ", "backup_controller_page_backup_sub": "Yedeklenen Ãļğeler", - "backup_controller_page_created": "Oluşturma tarihi: {}", + "backup_controller_page_created": "Oluşturma tarihi: {date}", "backup_controller_page_desc_backup": "UygulamayÄą açtığınÄązda yeni Ãļğelerin sunucuya otomatik olarak yÃŧklenmesi için Ãļn planda yedeklemeyi aÃ§Äąn.", - "backup_controller_page_excluded": "Hariç tutuldu:", - "backup_controller_page_failed": "BaşarÄąsÄąz ({})", - "backup_controller_page_filename": "Dosya adÄą: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "Hariç tutuldu: ", + "backup_controller_page_failed": "BaşarÄąsÄąz ({count})", + "backup_controller_page_filename": "Dosya adÄą: {filename} [{size}]", "backup_controller_page_info": "Yedekleme bilgileri", "backup_controller_page_none_selected": "Hiçbiri seçilmedi", "backup_controller_page_remainder": "Kalan", @@ -540,7 +533,7 @@ "backup_controller_page_start_backup": "Yedeklemeye Başla", "backup_controller_page_status_off": "Otomatik Ãļn planda yedekleme kapalÄą", "backup_controller_page_status_on": "Otomatik Ãļn planda yedekleme aÃ§Äąk", - "backup_controller_page_storage_format": "{}/{} kullanÄąlÄąyor", + "backup_controller_page_storage_format": "{used}/{total} kullanÄąlÄąyor", "backup_controller_page_to_backup": "Yedeklenecek albÃŧmler", "backup_controller_page_total_sub": "Seçili albÃŧmlerden tÃŧm benzersiz Ãļğeler", "backup_controller_page_turn_off": "Ön planda yedeklemeyi kapat", @@ -565,21 +558,21 @@ "bulk_keep_duplicates_confirmation": "{count, plural, one {# kopya Ãļğeyi} other {# kopya Ãļğeleri}} tutmak istediğinizden emin misiniz? Bu işlem, hiçbir şeyi silmeden tÃŧm kopya gruplarÄąnÄą çÃļzecektir.", "bulk_trash_duplicates_confirmation": "{count, plural, one {# kopya Ãļğeyi} other {# kopya Ãļğeleri}} toplu olarak çÃļp kutusuna taÅŸÄąmak istediğinizden emin misiniz? Bu işlem, her grubun en bÃŧyÃŧk Ãļğesini tutacak ve diğer tÃŧm kopyalarÄą çÃļp kutusuna taÅŸÄąyacaktÄąr.", "buy": "Immich'i SatÄąn AlÄąn", - "cache_settings_album_thumbnails": "KÃŧtÃŧphane sayfasÄą kÃŧçÃŧk resimleri ({} Ãļğe)", + "cache_settings_album_thumbnails": "KÃŧtÃŧphane sayfasÄą kÃŧçÃŧk resimleri ({count} Ãļğe)", "cache_settings_clear_cache_button": "Önbelleği temizle", "cache_settings_clear_cache_button_title": "UygulamanÄąn Ãļnbelleğini temizleyin. Önbellek yeniden oluşturulana kadar uygulamanÄąn performansÄąnÄą Ãļnemli ÃļlçÃŧde etkileyecektir.", "cache_settings_duplicated_assets_clear_button": "TEMİZLE", "cache_settings_duplicated_assets_subtitle": "Uygulama tarafÄąndan kara listeye alÄąnan Ãļğeler", - "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({})", - "cache_settings_image_cache_size": "GÃļrÃŧntÃŧ Ãļnbellek boyutu ({} Ãļğe)", + "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({count})", + "cache_settings_image_cache_size": "GÃļrÃŧntÃŧ Ãļnbellek boyutu ({count} Ãļğe)", "cache_settings_statistics_album": "KÃŧtÃŧphane kÃŧçÃŧk resimleri", - "cache_settings_statistics_assets": "{} Ãļğe ({})", + "cache_settings_statistics_assets": "{count} Ãļğe ({size})", "cache_settings_statistics_full": "Tam çÃļzÃŧnÃŧrlÃŧkte resimler", "cache_settings_statistics_shared": "PaylaÅŸÄąlan albÃŧm kÃŧçÃŧk resimleri", "cache_settings_statistics_thumbnail": "KÃŧçÃŧk resimler", "cache_settings_statistics_title": "Önbellek kullanÄąmÄą", "cache_settings_subtitle": "Immich mobil uygulamasÄąnÄąn Ãļnbelleğe alma davranÄąÅŸÄąnÄą kontrol edin", - "cache_settings_thumbnail_size": "KÃŧçÃŧk resim Ãļnbellek boyutu ({} Ãļğe)", + "cache_settings_thumbnail_size": "KÃŧçÃŧk resim Ãļnbellek boyutu ({count} Ãļğe)", "cache_settings_tile_subtitle": "Yerel depolama davranÄąÅŸÄąnÄą kontrol et", "cache_settings_tile_title": "Yerel Depolama", "cache_settings_title": "Önbellek AyarlarÄą", @@ -588,7 +581,6 @@ "camera_model": "Kamera modeli", "cancel": "İptal", "cancel_search": "AramayÄą iptal et", - "canceled": "Canceled", "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alÄąnamaz!", "cannot_update_the_description": "AÃ§Äąklama gÃŧncellenemiyor", @@ -605,6 +597,7 @@ "change_password_form_new_password": "Yeni Parola", "change_password_form_password_mismatch": "Parolalar eşleşmiyor", "change_password_form_reenter_new_password": "Tekrar Yeni Parola", + "change_pin_code": "PIN kodunu değiştirin", "change_your_password": "Şifreni değiştir", "changed_visibility_successfully": "GÃļrÃŧnÃŧrlÃŧk başarÄąyla değiştirildi", "check_all": "TÃŧmÃŧnÃŧ Seç", @@ -639,23 +632,22 @@ "comments_are_disabled": "Yorumlar devre dÄąÅŸÄą", "common_create_new_album": "Yeni AlbÃŧm", "common_server_error": "LÃŧtfen ağ bağlantÄąnÄązÄą kontrol edin, sunucunun erişilebilir olduğundan ve uygulama/sunucu sÃŧrÃŧmlerinin uyumlu olduğundan emin olun.", - "completed": "Completed", "confirm": "Onayla", "confirm_admin_password": "YÃļnetici Şifresini Onayla", "confirm_delete_face": "VarlÄąktan {name} yÃŧzÃŧnÃŧ silmek istediğinizden emin misiniz?", "confirm_delete_shared_link": "Bu paylaÅŸÄąlan bağlantÄąyÄą silmek istediğinizden emin misiniz?", "confirm_keep_this_delete_others": "Yığındaki diğer tÃŧm Ãļğeler bu varlÄąk haricinde silinecektir. Devam etmek istediğinizden emin misiniz?", + "confirm_new_pin_code": "Yeni PIN kodunu onaylayÄąn", "confirm_password": "Şifreyi onayla", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", - "control_bottom_app_bar_album_info_shared": "{} Ãļğe ¡ PaylaÅŸÄąlan", + "control_bottom_app_bar_album_info_shared": "{count} Ãļğe ¡ PaylaÅŸÄąlan", "control_bottom_app_bar_create_new_album": "Yeni albÃŧm", "control_bottom_app_bar_delete_from_immich": "Immich'ten sil", "control_bottom_app_bar_delete_from_local": "Cihazdan sil", "control_bottom_app_bar_edit_location": "Konumu DÃŧzenle", "control_bottom_app_bar_edit_time": "Tarih ve Saati DÃŧzenle", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "Paylaş:", "control_bottom_app_bar_trash_from_immich": "ÇÃļp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandÄą.", @@ -690,6 +682,7 @@ "crop": "Kes", "curated_object_page_title": "Nesneler", "current_device": "Mevcut cihaz", + "current_pin_code": "Mevcut PIN kodu", "current_server_address": "Mevcut sunucu adresi", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayÄąlarÄą dile ve bÃļlgeye gÃļre biçimlendirin", @@ -741,7 +734,6 @@ "direction": "YÃļn", "disabled": "Devre dÄąÅŸÄą bÄąrakÄąldÄą", "disallow_edits": "Değişikliklere izin verme", - "discord": "Discord", "discover": "Keşfet", "dismiss_all_errors": "TÃŧm hatalarÄą yoksay", "dismiss_error": "HatayÄą yoksay", @@ -758,7 +750,7 @@ "download_enqueue": "İndirme sÄąraya alÄąndÄą", "download_error": "İndirme HatasÄą", "download_failed": "İndirme başarÄąsÄąz oldu", - "download_filename": "dosya: {}", + "download_filename": "dosya: {filename}", "download_finished": "İndirme tamamlandÄą", "download_include_embedded_motion_videos": "GÃļmÃŧlÃŧ videolar", "download_include_embedded_motion_videos_description": "GÃļrsel hareketli fotoğraflarda yer alan gÃļmÃŧlÃŧ videolarÄą ayrÄą bir dosya olarak dahil et", @@ -802,19 +794,17 @@ "editor_crop_tool_h2_aspect_ratios": "En boy oranlarÄą", "editor_crop_tool_h2_rotation": "Rotasyon", "email": "E-posta", - "empty_folder": "This folder is empty", "empty_trash": "ÇÃļpÃŧ boşalt", "empty_trash_confirmation": "ÇÃļp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çÃļp kutusundaki tÃŧm varlÄąklarÄą kalÄącÄą olarak silecektir.\nBu işlemi geri alamazsÄąnÄąz!", "enable": "Etkinleştir", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", - "enqueued": "Enqueued", "enter_wifi_name": "Wi-Fi adÄąnÄą girin", "error": "Hata", "error_change_sort_album": "AlbÃŧm sÄąralama dÃŧzeni değiştirilemedi", "error_delete_face": "YÃŧzÃŧ varlÄąktan silme hatasÄą", "error_loading_image": "Resim yÃŧklenirken hata oluştu", - "error_saving_image": "Hata: {}", + "error_saving_image": "Hata: {error}", "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapÄąlamÄąyor", @@ -948,10 +938,6 @@ "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", "exif_bottom_sheet_person_add_person": "İsim ekle", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "Slayt gÃļsterisinden Ã§Äąk", "expand_all": "Hepsini genişlet", "experimental_settings_new_asset_list_subtitle": "ÇalÄąÅŸmalar devam ediyor", @@ -971,9 +957,7 @@ "external_network": "Harici ağlar", "external_network_sheet_info": "Belirlenmiş WiFi ağına bağlÄą olmadığında uygulama, yukarÄądan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracÄąlığıyla sunucuya bağlanacaktÄąr", "face_unassigned": "YÃŧz atanmadÄą", - "failed": "Failed", "failed_to_load_assets": "VarlÄąklar yÃŧklenemedi", - "failed_to_load_folder": "Failed to load folder", "favorite": "Favori", "favorite_or_unfavorite_photo": "Favoriye ekle veya Ã§Äąkar", "favorites": "Favoriler", @@ -989,8 +973,6 @@ "filter_people": "Kişileri filtrele", "find_them_fast": "AdlarÄąna gÃļre hÄązlÄąca bul", "fix_incorrect_match": "YanlÄąÅŸ eşleştirmeyi dÃŧzelt", - "folder": "Folder", - "folder_not_found": "Folder not found", "folders": "KlasÃļrler", "folders_feature_description": "Dosya sistemindeki fotoğraf ve videolarÄą klasÃļr gÃļrÃŧnÃŧmÃŧyle keşfedin", "forward": "İleri", @@ -1038,7 +1020,6 @@ "home_page_first_time_notice": "UygulamayÄą ilk kez kullanÄąyorsanÄąz, zaman çizelgesinin albÃŧmlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lÃŧtfen yedekleme için albÃŧm(ler) seçtiğinizden emin olun.", "home_page_share_err_local": "Yerel Ãļğeler bağlantÄą ile paylaÅŸÄąlamaz, atlanÄąyor", "home_page_upload_err_limit": "AynÄą anda en fazla 30 Ãļğe yÃŧklenebilir, atlanabilir", - "host": "Host", "hour": "Saat", "ignore_icloud_photos": "iCloud FotoğraflarÄąnÄą Yok Say", "ignore_icloud_photos_description": "iCloud'a yÃŧklenmiş fotoğraflar Immich sunucusuna yÃŧklenmesin", @@ -1164,8 +1145,8 @@ "manage_your_devices": "CihazlarÄąnÄązÄą yÃļnetin", "manage_your_oauth_connection": "OAuth bağlantÄąnÄązÄą yÃļnetin", "map": "Harita", - "map_assets_in_bound": "{} fotoğraf", - "map_assets_in_bounds": "{} fotoğraf", + "map_assets_in_bound": "{count} fotoğraf", + "map_assets_in_bounds": "{count} fotoğraf", "map_cannot_get_user_location": "KullanÄącÄąnÄąn konumu alÄąnamÄąyor", "map_location_dialog_yes": "Evet", "map_location_picker_page_use_location": "Bu konumu kullan", @@ -1179,9 +1160,9 @@ "map_settings": "Harita ayarlarÄą", "map_settings_dark_mode": "Koyu tema", "map_settings_date_range_option_day": "Son 24 saat", - "map_settings_date_range_option_days": "Son {} gÃŧn", + "map_settings_date_range_option_days": "Son {days} gÃŧn", "map_settings_date_range_option_year": "Son yÄąl", - "map_settings_date_range_option_years": "Son {} yÄąl", + "map_settings_date_range_option_years": "Son {years} yÄąl", "map_settings_dialog_title": "Harita AyarlarÄą", "map_settings_include_show_archived": "Arşivdekileri dahil et", "map_settings_include_show_partners": "Partnerleri Dahil Et", @@ -1196,8 +1177,6 @@ "memories_setting_description": "AnÄąlarÄąnÄązda gÃļrmek istediklerinizi yÃļnetin", "memories_start_over": "Baştan Başla", "memories_swipe_to_close": "Kapatmak için yukarÄą kaydÄąrÄąn", - "memories_year_ago": "Bir yÄąl Ãļnce", - "memories_years_ago": "{} yÄąl Ãļnce", "memory": "AnÄą", "memory_lane_title": "AnÄąlara Yolculuk {title}", "menu": "MenÃŧ", @@ -1210,9 +1189,7 @@ "minimize": "KÃŧçÃŧlt", "minute": "Dakika", "missing": "Eksik", - "model": "Model", "month": "Ay", - "monthly_title_text_date_format": "MMMM y", "more": "Daha fazla", "moved_to_trash": "ÇÃļp kutusuna taÅŸÄąndÄą", "multiselect_grid_edit_date_time_err_read_only": "Salt okunur Ãļğelerin tarihi dÃŧzenlenemedi, atlanÄąyor", @@ -1228,6 +1205,7 @@ "new_api_key": "Yeni API AnahtarÄą", "new_password": "Yeni şifre", "new_person": "Yeni kişi", + "new_pin_code": "Yeni PIN kodu", "new_user_created": "Yeni kullanÄącÄą oluşturuldu", "new_version_available": "YENİ VERSİYON MEVCUT", "newest_first": "Önce en yeniler", @@ -1251,7 +1229,6 @@ "no_results_description": "Eş anlamlÄą ya da daha genel anlamlÄą bir kelime deneyin", "no_shared_albums_message": "FotoğraflarÄą ve videolarÄą ağınÄązdaki kişilerle paylaşmak için bir albÃŧm oluşturun", "not_in_any_album": "Hiçbir albÃŧmde değil", - "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha Ãļnce yÃŧklenen varlÄąklar için bir depolama yolu etiketi uygulamak Ãŧzere şunu başlatÄąn", "notes": "Notlar", "notification_permission_dialog_content": "Bildirimleri etkinleştirmek için cihaz ayarlarÄąna gidin ve izin verin.", @@ -1261,7 +1238,6 @@ "notification_toggle_setting_description": "E-posta bildirimlerine izin ver", "notifications": "Bildirimler", "notifications_setting_description": "Bildirimleri yÃļnetin", - "oauth": "OAuth", "official_immich_resources": "Resmi Immich KaynaklarÄą", "offline": "Çevrim dÄąÅŸÄą", "offline_paths": "Çevrim dÄąÅŸÄą yollar", @@ -1299,7 +1275,7 @@ "partner_page_partner_add_failed": "Partner eklenemedi", "partner_page_select_partner": "Partner seç", "partner_page_shared_to_title": "PaylaÅŸÄąldÄą:", - "partner_page_stop_sharing_content": "{} artÄąk fotoğraflarÄąnÄąza erişemeyecek.", + "partner_page_stop_sharing_content": "{partner} artÄąk fotoğraflarÄąnÄąza erişemeyecek.", "partner_sharing": "Ortak paylaÅŸÄąmÄą", "partners": "Ortaklar", "password": "Şifre", @@ -1344,6 +1320,9 @@ "photos_count": "{count, plural, one {{count, number} fotoğraf} other {{count, number} fotoğraf}}", "photos_from_previous_years": "Önceki yÄąllardan fotoğraflar", "pick_a_location": "Bir konum seçin", + "pin_code_changed_successfully": "PIN kodu başarÄąyla değiştirildi", + "pin_code_reset_successfully": "PIN kodu başarÄąyla sÄąfÄąrlandÄą", + "pin_code_setup_successfully": "PIN kodu başarÄąyla ayarlandÄą", "place": "Konum", "places": "Konumlar", "places_count": "{count, plural, one {{count, number} yer} other {{count, number} yer}}", @@ -1351,7 +1330,6 @@ "play_memories": "AnÄąlarÄą oynat", "play_motion_photo": "Hareketli fotoğrafÄą oynat", "play_or_pause_video": "Videoyu oynat ya da durdur", - "port": "Port", "preferences_settings_subtitle": "Uygulama tercihlerini dÃŧzenle", "preferences_settings_title": "Tercihler", "preset": "Ön ayar", @@ -1365,7 +1343,6 @@ "profile_drawer_client_out_of_date_major": "Mobil uygulama gÃŧncel değil. LÃŧtfen en son ana sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_client_out_of_date_minor": "Mobil uygulama gÃŧncel değil. LÃŧtfen en son sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_client_server_up_to_date": "Uygulama ve sunucu gÃŧncel", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Sunucu gÃŧncel değil. LÃŧtfen en son ana sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_server_out_of_date_minor": "Sunucu gÃŧncel değil. LÃŧtfen en son sÃŧrÃŧme gÃŧncelleyin.", "profile_image_of_user": "{user} kullanÄącÄąsÄąnÄąn profil resmi", @@ -1374,7 +1351,7 @@ "public_share": "Genel paylaÅŸÄąm", "purchase_account_info": "Destekçi", "purchase_activated_subtitle": "Immich ve aÃ§Äąk kaynak yazÄąlÄąma destek olduğunuz için teşekkÃŧr ederiz", - "purchase_activated_time": "{date, date} tarihinde etkinleştirildi", + "purchase_activated_time": "{date} tarihinde etkinleştirildi", "purchase_activated_title": "AnahtarÄąnÄąz başarÄąyla etkinleştirildi", "purchase_button_activate": "Aktifleştir", "purchase_button_buy": "SatÄąn al", @@ -1459,6 +1436,7 @@ "reset": "SÄąfÄąrla", "reset_password": "Şifreyi sÄąfÄąrla", "reset_people_visibility": "Kişilerin gÃļrÃŧnÃŧrlÃŧğÃŧnÃŧ sÄąfÄąrla", + "reset_pin_code": "PIN kodunu sÄąfÄąrlayÄąn", "reset_to_default": "VarsayÄąlana sÄąfÄąrla", "resolve_duplicates": "Çiftleri çÃļz", "resolved_all_duplicates": "TÃŧm çiftler çÃļzÃŧldÃŧ", @@ -1501,7 +1479,6 @@ "search_filter_date_title": "Tarih aralığı seç", "search_filter_display_option_not_in_album": "AlbÃŧmde değil", "search_filter_display_options": "GÃļrÃŧntÃŧ Seçenekleri", - "search_filter_filename": "Search by file name", "search_filter_location": "Konum", "search_filter_location_title": "Konum seç", "search_filter_media_type": "Medya TÃŧrÃŧ", @@ -1509,10 +1486,8 @@ "search_filter_people_title": "Kişi seç", "search_for": "AraştÄąr", "search_for_existing_person": "Mevcut bir kişiyi ara", - "search_no_more_result": "No more results", "search_no_people": "Kişi yok", "search_no_people_named": "\"{name}\" isimli bir kişi yok", - "search_no_result": "No results found, try a different search term or combination", "search_options": "Arama seçenekleri", "search_page_categories": "Kategoriler", "search_page_motion_photos": "CanlÄą Fotoğraflar", @@ -1580,26 +1555,25 @@ "setting_languages_apply": "Uygula", "setting_languages_subtitle": "Uygulama dilini değiştir", "setting_languages_title": "Diller", - "setting_notifications_notify_failures_grace_period": "Arka plan yedekleme hatalarÄąnÄą bildir: {}", - "setting_notifications_notify_hours": "{} saat", + "setting_notifications_notify_failures_grace_period": "Arka plan yedekleme hatalarÄąnÄą bildir: {duration}", + "setting_notifications_notify_hours": "{count} saat", "setting_notifications_notify_immediately": "hemen", - "setting_notifications_notify_minutes": "{} dakika", + "setting_notifications_notify_minutes": "{count} dakika", "setting_notifications_notify_never": "hiçbir zaman", - "setting_notifications_notify_seconds": "{} saniye", + "setting_notifications_notify_seconds": "{count} saniye", "setting_notifications_single_progress_subtitle": "Öğe baÅŸÄąna ayrÄąntÄąlÄą yÃŧkleme ilerleme bilgisi", "setting_notifications_single_progress_title": "Arkaplan yedeklemesi ayrÄąntÄąlÄą ilerlemesini gÃļster", "setting_notifications_subtitle": "Bildirim tercihlerinizi dÃŧzenleyin", "setting_notifications_total_progress_subtitle": "Toplam yÃŧkleme ilerlemesi (tamamlanan/toplam)", "setting_notifications_total_progress_title": "Arkaplan yedeklemesi toplam ilerlemesini gÃļster", "setting_video_viewer_looping_title": "DÃļngÃŧ", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", "settings": "Ayarlar", "settings_require_restart": "Bu ayarÄą uygulamak için lÃŧtfen Immich'i yeniden başlatÄąn", "settings_saved": "Ayarlar kaydedildi", + "setup_pin_code": "PIN kodunu ayarlayÄąn", "share": "Paylaş", "share_add_photos": "Fotoğraf ekle", - "share_assets_selected": "{} seçili", + "share_assets_selected": "{count} seçili", "share_dialog_preparing": "HazÄąrlanÄąyor...", "shared": "PaylaÅŸÄąlan", "shared_album_activities_input_disable": "Yoruma kapalÄą", @@ -1613,34 +1587,32 @@ "shared_by_user": "{user} tarafÄąndan paylaÅŸÄąldÄą", "shared_by_you": "Senin tarafÄąndan paylaÅŸÄąldÄą", "shared_from_partner": "{partner} tarafÄąndan paylaÅŸÄąlan fotoğraflar", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", "shared_link_app_bar_title": "PaylaÅŸÄąlan BağlantÄąlar", "shared_link_clipboard_copied_massage": "Panoya kopyalandÄą", - "shared_link_clipboard_text": "BağlantÄą: {}\nParola: {}", + "shared_link_clipboard_text": "BağlantÄą: {link}\nParola: {password}", "shared_link_create_error": "PaylaÅŸÄąm bağlantÄąsÄą oluşturulurken hata oluştu", "shared_link_edit_description_hint": "AÃ§Äąklama yazÄąn", "shared_link_edit_expire_after_option_day": "1 gÃŧn", - "shared_link_edit_expire_after_option_days": "{} gÃŧn", + "shared_link_edit_expire_after_option_days": "{count} gÃŧn", "shared_link_edit_expire_after_option_hour": "1 saat", - "shared_link_edit_expire_after_option_hours": "{} saat", + "shared_link_edit_expire_after_option_hours": "{count} saat", "shared_link_edit_expire_after_option_minute": "1 dakika", - "shared_link_edit_expire_after_option_minutes": "{} dakika", - "shared_link_edit_expire_after_option_months": "{} ay", - "shared_link_edit_expire_after_option_year": "{} yÄąl", + "shared_link_edit_expire_after_option_minutes": "{count} dakika", + "shared_link_edit_expire_after_option_months": "{count} ay", + "shared_link_edit_expire_after_option_year": "{count} yÄąl", "shared_link_edit_password_hint": "PaylaÅŸÄąm parolasÄąnÄą girin", "shared_link_edit_submit_button": "BağlantÄąyÄą gÃŧncelle", "shared_link_error_server_url_fetch": "Sunucu URL'si alÄąnamadÄą", - "shared_link_expires_day": "SÃŧresi {} gÃŧn içinde doluyor", - "shared_link_expires_days": "SÃŧresi {} gÃŧn içinde doluyor", - "shared_link_expires_hour": "SÃŧresi {} saat içinde doluyor", - "shared_link_expires_hours": "SÃŧresi {} saat içinde doluyor", - "shared_link_expires_minute": "SÃŧresi {} dakika içinde doluyor", - "shared_link_expires_minutes": "{} dakika içinde sÃŧresi doluyor", + "shared_link_expires_day": "SÃŧresi {count} gÃŧn içinde doluyor", + "shared_link_expires_days": "SÃŧresi {count} gÃŧn içinde doluyor", + "shared_link_expires_hour": "SÃŧresi {count} saat içinde doluyor", + "shared_link_expires_hours": "SÃŧresi {count} saat içinde doluyor", + "shared_link_expires_minute": "SÃŧresi {count} dakika içinde doluyor", + "shared_link_expires_minutes": "{count} dakika içinde sÃŧresi doluyor", "shared_link_expires_never": "SÃŧresiz", - "shared_link_expires_second": "SÃŧresi {} saniye içinde doluyor", - "shared_link_expires_seconds": "{} sanyei içinde sÃŧresi doluyor", + "shared_link_expires_second": "SÃŧresi {count} saniye içinde doluyor", + "shared_link_expires_seconds": "{count} sanyei içinde sÃŧresi doluyor", "shared_link_individual_shared": "Bireysel paylaÅŸÄąmlÄą", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "PaylaÅŸÄąlan BağlantÄąlarÄą YÃļnet", "shared_link_options": "PaylaÅŸÄąlan bağlantÄą seçenekleri", "shared_links": "PaylaÅŸÄąlan bağlantÄąlar", @@ -1739,7 +1711,7 @@ "theme_selection": "Tema seçimi", "theme_selection_description": "TemayÄą otomatik olarak tarayÄącÄąnÄązÄąn sistem tercihine gÃļre aÃ§Äąk veya koyu ayarlayÄąn", "theme_setting_asset_list_storage_indicator_title": "Öğelerin kÃŧçÃŧk resimlerinde depolama gÃļstergesini gÃļster", - "theme_setting_asset_list_tiles_per_row_title": "SatÄąr baÅŸÄąna Ãļğe sayÄąsÄą ({})", + "theme_setting_asset_list_tiles_per_row_title": "SatÄąr baÅŸÄąna Ãļğe sayÄąsÄą ({count})", "theme_setting_colorful_interface_subtitle": "Birincil rengi arka plan yÃŧzeylerine uygulayÄąn.", "theme_setting_colorful_interface_title": "Renkli arayÃŧz", "theme_setting_image_viewer_quality_subtitle": "AyrÄąntÄąlÄą gÃļrÃŧntÃŧleyicinin kalitesini ayarla", @@ -1774,13 +1746,15 @@ "trash_no_results_message": "Silinen fotoğraf ve videolar burada listelenecektir.", "trash_page_delete_all": "TÃŧmÃŧnÃŧ Sil", "trash_page_empty_trash_dialog_content": "ÇÃļp kutusuna atÄąlmÄąÅŸ Ãļğeleri silmek istediğinize emin misiniz? Bu Ãļğeler Immich'ten kalÄącÄą olarak silinecek", - "trash_page_info": "ÇÃļp kutusuna atÄąlan Ãļğeler {} gÃŧn sonra kalÄącÄą olarak silinecektir", + "trash_page_info": "ÇÃļp kutusuna atÄąlan Ãļğeler {days} gÃŧn sonra kalÄącÄą olarak silinecektir", "trash_page_no_assets": "ÇÃļp kutusu boş", "trash_page_restore_all": "TÃŧmÃŧnÃŧ geri yÃŧkle", "trash_page_select_assets_btn": "İçerik seç", - "trash_page_title": "ÇÃļp Kutusu ({})", + "trash_page_title": "ÇÃļp Kutusu ({count})", "trashed_items_will_be_permanently_deleted_after": "Silinen Ãļğeler {days, plural, one {# gÃŧn} other {# gÃŧn}} sonra kalÄącÄą olarak silinecek.", "type": "TÃŧr", + "unable_to_change_pin_code": "PIN kodu değiştirilemedi", + "unable_to_setup_pin_code": "PIN kodu ayarlanamadÄą", "unarchive": "Arşivden Ã§Äąkar", "unarchived_count": "{count, plural, other {# arşivden Ã§ÄąkarÄąldÄą}}", "unfavorite": "Favorilerden kaldÄąr", @@ -1816,15 +1790,14 @@ "upload_status_errors": "Hatalar", "upload_status_uploaded": "YÃŧklendi", "upload_success": "YÃŧkleme başarÄąlÄą, yÃŧklenen yeni Ãļgeleri gÃļrebilmek için sayfayÄą yenileyin.", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", - "url": "URL", "usage": "KullanÄąm", "use_current_connection": "mevcut bağlantÄąyÄą kullan", "use_custom_date_range": "Bunun yerine Ãļzel tarih aralığınÄą kullan", "user": "KullanÄącÄą", "user_id": "KullanÄącÄą ID", "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu dosya} other {Bu}} {user} tarafÄąndan beğenildi", + "user_pin_code_settings": "PIN Kodu", + "user_pin_code_settings_description": "PIN kodunuzu yÃļnetin", "user_purchase_settings": "SatÄąn Alma", "user_purchase_settings_description": "SatÄąn alma işlemlerini yÃļnet", "user_role_set": "{user}, {role} olarak ayarlandÄą", @@ -1842,12 +1815,11 @@ "version_announcement_message": "Merhaba! Immich'in yeni bir sÃŧrÃŧmÃŧ mevcut. LÃŧtfen yapÄąlandÄąrmanÄązÄąn gÃŧncel olduğundan emin olmak için sÃŧrÃŧm notlarÄąnÄą okumak için biraz zaman ayÄąrÄąn, Ãļzellikle WatchTower veya Immich kurulumunuzu otomatik olarak gÃŧncelleyen bir mekanizma kullanÄąyorsanÄąz yanlÄąÅŸ yapÄąlandÄąrmalarÄąn ÃļnÃŧne geçmek adÄąna bu Ãļnemlidir.", "version_announcement_overlay_release_notes": "sÃŧrÃŧm notlarÄą", "version_announcement_overlay_text_1": "Merhaba arkadaÅŸÄąm, yeni bir sÃŧrÃŧm mevcut", - "version_announcement_overlay_text_2": "lÃŧtfen biraz zaman ayÄąrÄąn ve inceleyin:", + "version_announcement_overlay_text_2": "lÃŧtfen biraz zaman ayÄąrÄąn ve inceleyin: ", "version_announcement_overlay_text_3": "ve Ãļzellikle WatchTower veya sunucu uygulamanÄązÄą otomatik olarak gÃŧncelleyen herhangi bir mekanizma kullanÄąyorsanÄąz, herhangi bir yanlÄąÅŸ yapÄąlandÄąrmayÄą Ãļnlemek için docker-compose ve .env kurulumunuzun gÃŧncel olduğundan emin olun.", "version_announcement_overlay_title": "Yeni Sunucu SÃŧrÃŧmÃŧ Mevcut 🎉", "version_history": "Versiyon geçmişi", "version_history_item": "{version}, {date} tarihinde kuruldu", - "video": "Video", "video_hover_setting": "Üzerinde durulduğunda video Ãļnizlemesi oynat", "video_hover_setting_description": "Öğe Ãŧzerinde fareyle durulduğunda video kÃŧçÃŧk resmini oynatÄąr. Bu Ãļzellik devre dÄąÅŸÄąyken, oynatma simgesine fareyle gidilerek oynatma başlatÄąlabilir.", "videos": "Videolar", diff --git a/i18n/uk.json b/i18n/uk.json index 5c6a809f26..1862e82d8e 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -26,6 +26,7 @@ "add_to_album": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃ аĐģŅŒĐąĐžĐŧ", "add_to_album_bottom_sheet_added": "ДодаĐŊĐž Đ´Đž {album}", "add_to_album_bottom_sheet_already_exists": "ВĐļĐĩ Ņ” в {album}", + "add_to_locked_folder": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи", "add_to_shared_album": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃ ҁĐŋŅ–ĐģҌĐŊиК аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ URL", "added_to_archive": "ДодаĐŊĐž Đ´Đž Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐŧĐēĐŊŅƒŅ‚Đ¸ Đ˛ŅŅ– ĐŧĐĩŅ‚ĐžĐ´Đ¸ Đ˛Ņ…ĐžĐ´Ņƒ? Đ’Ņ…Ņ–Đ´ ĐąŅƒĐ´Đĩ ĐŋОвĐŊŅ–ŅŅ‚ŅŽ виĐŧĐēĐŊĐĩĐŊиК.", "authentication_settings_reenable": "ДĐģŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐžĐŗĐž Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐŊŅ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ КоĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "background_task_job": "ФОĐŊĐžĐ˛Ņ– ЗавдаĐŊĐŊŅ", - "backup_database": "Đ ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋŅ–Ņ йаСи даĐŊĐ¸Ņ…", - "backup_database_enable_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ йаСи даĐŊĐ¸Ņ…", - "backup_keep_last_amount": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋŅ–Đš Đ´ĐģŅ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ", - "backup_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", - "backup_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ йаСи даĐŊĐ¸Ņ…", + "backup_database": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ даĐŧĐŋ йаСи даĐŊĐ¸Ņ…", + "backup_database_enable_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ даĐŧĐŋи йаСи даĐŊĐ¸Ņ…", + "backup_keep_last_amount": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Ņ… даĐŧĐŋŅ–Đ˛, ŅĐēŅ– СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸", + "backup_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ даĐŧĐŋа йаСи даĐŊĐ¸Ņ…", + "backup_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи даĐŧĐŋа йаСи даĐŊĐ¸Ņ…. ĐŸŅ€Đ¸ĐŧŅ–Ņ‚Đēа: ҆Җ СавдаĐŊĐŊŅ ĐŊĐĩ ĐēĐžĐŊŅ‚Ņ€ĐžĐģŅŽŅŽŅ‚ŅŒŅŅ, Ņ– ви ĐŊĐĩ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ”Ņ‚Đĩ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŋĐžĐŧиĐģĐēи.", "check_all": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸ Đ˛ŅĐĩ", "cleanup": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐŊŅ", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ– СавдаĐŊĐŊŅ Đ´ĐģŅ: {job}", @@ -53,6 +54,7 @@ "confirm_email_below": "ДĐģŅ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐŊŅ ввĐĩĐ´Ņ–Ņ‚ŅŒ \"{email}\" ĐŊиĐļ҇Đĩ", "confirm_reprocess_all_faces": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž виСĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ? ĐĻĐĩ Ņ‚Đ°ĐēĐžĐļ ĐŋŅ€Đ¸ĐˇĐ˛ĐĩĐ´Đĩ Đ´Đž видаĐģĐĩĐŊĐŊŅ Ņ–ĐŧĐĩĐŊ С ŅƒŅŅ–Ņ… ОйĐģĐ¸Ņ‡.", "confirm_user_password_reset": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ҁĐēиĐŊŅƒŅ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° {user}?", + "confirm_user_pin_code_reset": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ҁĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд {user}?", "create_job": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ СавдаĐŊĐŊŅ", "cron_expression": "Cron Đ˛Đ¸Ņ€Đ°Đˇ", "cron_expression_description": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Ņ–ĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҁĐēаĐŊŅƒĐ˛Đ°ĐŊĐŊŅ, виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅŽŅ‡Đ¸ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ cron. ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛ĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž ĐŊаĐŋŅ€. Crontab Guru", @@ -192,26 +194,22 @@ "oauth_auto_register": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊа Ņ€ĐĩŅ”ŅŅ‚Ņ€Đ°Ņ†Ņ–Ņ", "oauth_auto_register_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Ņ€ĐĩŅ”ŅŅ‚Ņ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Đ¸Ņ… ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛ ĐŋҖҁĐģŅ Đ˛Ņ…ĐžĐ´Ņƒ ҇ĐĩŅ€ĐĩС OAuth", "oauth_button_text": "ĐĸĐĩĐēҁ҂ ĐēĐŊĐžĐŋĐēи", - "oauth_client_id": "ID КĐģŅ–Ņ”ĐŊŅ‚Đ°", - "oauth_client_secret": "ĐĄĐĩĐēŅ€ĐĩŅ‚ ĐēĐģŅ–Ņ”ĐŊŅ‚Đ°", + "oauth_client_secret_description": "ĐŸĐžŅ‚Ņ€Ņ–ĐąĐŊĐž, ŅĐēŅ‰Đž ĐŋĐžŅŅ‚Đ°Ņ‡Đ°ĐģҌĐŊиĐē OAuth ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ OAuth", - "oauth_issuer_url": "URL Đ˛Đ¸Đ´Đ°Ņ‡Ņ–", "oauth_mobile_redirect_uri": "URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩвиСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "oauth_mobile_redirect_uri_override_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸, ŅĐēŅ‰Đž OAuth-ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” ĐŧĐžĐąŅ–ĐģҌĐŊиК URI, ŅĐē '{callback}'", - "oauth_profile_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ ĐŋŅ–Đ´ĐŋĐ¸ŅĐ°ĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ", - "oauth_profile_signing_algorithm_description": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ, ŅĐēиК виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ Đ´ĐģŅ ĐŋŅ–Đ´ĐŋĐ¸ŅŅƒ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°.", - "oauth_scope": "ĐœĐ°ŅŅˆŅ‚Đ°Đą", "oauth_settings": "OAuth", "oauth_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ˛Ņ…ĐžĐ´Ņƒ ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛ĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ—.", - "oauth_signing_algorithm": "АĐģĐŗĐžŅ€Đ¸Ņ‚Đŧ ĐŋŅ–Đ´ĐŋĐ¸ŅŅƒ", "oauth_storage_label_claim": "ĐĸĐĩĐŗ Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Ņ–Ņ— ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "oauth_storage_label_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ˛ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐŧŅ–Ņ‚Đē҃ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŊа СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ҆ҖҔҗ виĐŧĐžĐŗĐ¸.", "oauth_storage_quota_claim": "Đ—Đ°ŅĐ˛Đēа ĐŊа ĐēĐ˛ĐžŅ‚Ņƒ ĐŊа СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ", "oauth_storage_quota_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ˛ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐēĐ˛ĐžŅ‚Ņƒ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŊа СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ҆ҖҔҗ виĐŧĐžĐŗĐ¸.", "oauth_storage_quota_default": "ĐšĐ˛ĐžŅ‚Đ° Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ (GiB)", "oauth_storage_quota_default_description": "ĐšĐ˛ĐžŅ‚Đ° в GiB, Ņ‰Đž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ, ĐēĐžĐģи ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐĩ ĐŊадаĐŊĐž (ввĐĩĐ´Ņ–Ņ‚ŅŒ 0 Đ´ĐģŅ ĐŊĐĩОйĐŧĐĩĐļĐĩĐŊĐžŅ— ĐēĐ˛ĐžŅ‚Đ¸).", + "oauth_timeout": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ Đ´ĐģŅ СаĐŋĐ¸Ņ‚Ņ–Đ˛", + "oauth_timeout_description": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Ņ– в ĐŧŅ–ĐģҖҁĐĩĐē҃ĐŊĐ´Đ°Ņ…", "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ШĐģŅŅ…Đ¸", "offline_paths_description": "ĐĻŅ– Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đ¸ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ ĐŋОв'ŅĐˇĐ°ĐŊŅ– С Ņ€ŅƒŅ‡ĐŊиĐŧ видаĐģĐĩĐŊĐŊŅĐŧ Ņ„Đ°ĐšĐģŅ–Đ˛, ŅĐēŅ– ĐŊĐĩ Đ˛Ņ…ĐžĐ´ŅŅ‚ŅŒ Đ´Đž СОвĐŊŅ–ŅˆĐŊŅŒĐžŅ— ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи.", "password_enable_description": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ Са ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐžŅŽ ĐŋĐžŅˆŅ‚ĐžŅŽ Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ Đ´ĐŊŅ–Đ˛ ĐŋҖҁĐģŅ видаĐģĐĩĐŊĐŊŅ Đ´ĐģŅ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐžĐŗĐž видаĐģĐĩĐŊĐŊŅ аĐēĐ°ŅƒĐŊŅ‚Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° Ņ‚Đ° ĐšĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛. Đ—Đ°Đ´Đ°Ņ‡Đ° видаĐģĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° СаĐŋ҃ҁĐēĐ°Ņ”Ņ‚ŅŒŅŅ ĐžĐŋŅ–Đ˛ĐŊĐžŅ‡Ņ– Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛, ĐŗĐžŅ‚ĐžĐ˛Đ¸Ņ… Đ´Đž видаĐģĐĩĐŊĐŊŅ. ЗĐŧŅ–ĐŊи Ņ†ŅŒĐžĐŗĐž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅ†Ņ–ĐŊĐĩĐŊŅ– ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐžĐŗĐž виĐēĐžĐŊаĐŊĐŊŅ.", "user_delete_immediately": "АĐēĐ°ŅƒĐŊŅ‚ Ņ‚Đ° Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° {user} ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐŊĐĩĐŗĐ°ĐšĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊŅ– в ҇ĐĩŅ€ĐŗŅƒ ĐŊа ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐĩ видаĐģĐĩĐŊĐŊŅ.", "user_delete_immediately_checkbox": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° Ņ‚Đ° Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҇ĐĩŅ€ĐŗŅƒ Đ´ĐģŅ ĐŊĐĩĐŗĐ°ĐšĐŊĐžĐŗĐž видаĐģĐĩĐŊĐŊŅ", + "user_details": "ДаĐŊĐŊŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "user_management": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи", "user_password_has_been_reset": "ĐŸĐ°Ņ€ĐžĐģҌ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐąŅƒĐģĐž ҁĐēиĐŊŅƒŅ‚Đž:", "user_password_reset_description": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡ĐĩĐ˛Ņ– Ņ‚Đ¸ĐŧŅ‡Đ°ŅĐžĐ˛Đ¸Đš ĐŋĐ°Ņ€ĐžĐģҌ Ņ– ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧŅ‚Đĩ КОĐŧ҃, Ņ‰Đž Đ˛Ņ–ĐŊ ĐŋОвиĐŊĐĩĐŊ ĐąŅƒĐ´Đĩ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€Đ¸ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐžĐŧ҃ Đ˛Ņ…ĐžĐ´Ņ–.", @@ -371,13 +370,17 @@ "admin_password": "ĐŸĐ°Ņ€ĐžĐģҌ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "administration": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ", "advanced": "Đ ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊŅ–", - "advanced_settings_log_level_title": "Đ Ņ–Đ˛ĐĩĐŊҌ ĐģĐžĐŗŅƒĐ˛Đ°ĐŊĐŊŅ: {}", + "advanced_settings_enable_alternate_media_filter_subtitle": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš Đ˛Đ°Ņ€Ņ–Đ°ĐŊŅ‚ Đ´ĐģŅ ҄ҖĐģŅŒŅ‚Ņ€Đ°Ņ†Ņ–Ņ— ĐŧĐĩĐ´Ņ–Đ°Ņ„Đ°ĐšĐģŅ–Đ˛ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— Са аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊиĐŧи ĐēŅ€Đ¸Ņ‚ĐĩŅ€Ņ–ŅĐŧи. ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ҆Đĩ, ŅĐēŅ‰Đž ҃ Đ˛Đ°Ņ виĐŊиĐēĐ°ŅŽŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧи С Ņ‚Đ¸Đŧ, Ņ‰Đž Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŊĐĩ Đ˛Đ¸ŅĐ˛ĐģŅŅ” Đ˛ŅŅ– аĐģŅŒĐąĐžĐŧи.", + "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНĐĸАЛĐŦНИЙ] ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊиК ҄ҖĐģŅŒŅ‚Ņ€ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— аĐģŅŒĐąĐžĐŧŅ–Đ˛ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", + "advanced_settings_log_level_title": "Đ Ņ–Đ˛ĐĩĐŊҌ ĐģĐžĐŗŅƒĐ˛Đ°ĐŊĐŊŅ: {level}", "advanced_settings_prefer_remote_subtitle": "ДĐĩŅĐēŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— вĐĩĐģҌĐŧи ĐŋĐžĐ˛Ņ–ĐģҌĐŊĐž СаваĐŊŅ‚Đ°ĐļŅƒŅŽŅ‚ŅŒ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Ņ–Đˇ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—. АĐēŅ‚Đ¸Đ˛ŅƒĐšŅ‚Đĩ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊĐ¸Ņ… ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ ĐŊĐ°Ņ‚ĐžĐŧŅ–ŅŅ‚ŅŒ.", "advanced_settings_prefer_remote_title": "ПĐĩŅ€ĐĩĐ˛Đ°ĐŗĐ° Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊиĐŧ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅĐŧ", - "advanced_settings_proxy_headers_subtitle": "ВизĐŊĐ°Ņ‡Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ŅĐēŅ– Immich ĐŧĐ°Ņ” ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅ€ĐĩĐļĐĩвиĐŧ СаĐŋĐ¸Ņ‚ĐžĐŧ.", + "advanced_settings_proxy_headers_subtitle": "ВизĐŊĐ°Ņ‡Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ŅĐēŅ– Immich ĐŧĐ°Ņ” ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅ€ĐĩĐļĐĩвиĐŧ СаĐŋĐ¸Ņ‚ĐžĐŧ", "advanced_settings_proxy_headers_title": "ĐŸŅ€ĐžĐēҁҖ-ĐˇĐ°ĐŗĐžĐģОвĐēи", "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ” ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°. ĐŸĐžŅ‚Ņ€Ņ–ĐąĐŊĐĩ Đ´ĐģŅ ŅĐ°ĐŧĐžĐŋŅ–Đ´ĐŋĐ¸ŅĐ°ĐŊĐ¸Ņ… ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Ņ–Đ˛.", "advanced_settings_self_signed_ssl_title": "ДозвоĐģĐ¸Ņ‚Đ¸ ŅĐ°ĐŧĐžĐŋŅ–Đ´ĐŋĐ¸ŅĐ°ĐŊŅ– SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ¸", + "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž видаĐģŅŅ‚Đ¸ айО Đ˛Ņ–Đ´ĐŊОвĐģŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ ĐŊа Ņ†ŅŒĐžĐŧ҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—, ĐēĐžĐģи Ņ†Ņ Đ´Ņ–Ņ виĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ в вĐĩĐą-Ņ–ĐŊŅ‚ĐĩҀ҄ĐĩĐšŅŅ–", + "advanced_settings_sync_remote_deletions_title": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊĐ¸Ņ… видаĐģĐĩĐŊҌ [ЕКСПЕРИМЕНĐĸАЛĐŦНО]", "advanced_settings_tile_subtitle": "Đ ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "advanced_settings_troubleshooting_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Ņ– Ņ„ŅƒĐŊĐē҆Җҗ Đ´ĐģŅ ҃ҁ҃ĐŊĐĩĐŊĐŊŅ ĐŊĐĩҁĐŋŅ€Đ°Đ˛ĐŊĐžŅŅ‚ĐĩĐš", "advanced_settings_troubleshooting_title": "ĐŖŅŅƒĐŊĐĩĐŊĐŊŅ ĐŊĐĩҁĐŋŅ€Đ°Đ˛ĐŊĐžŅŅ‚ĐĩĐš", @@ -400,9 +403,9 @@ "album_remove_user_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ {user}?", "album_share_no_users": "ĐĄŅ…ĐžĐļĐĩ, ви ĐŋĐžĐ´Ņ–ĐģиĐģĐ¸ŅŅ Ņ†Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ С ŅƒŅŅ–Đŧа ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи айО ҃ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ĐļОдĐŊĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°, С ŅĐēиĐŧ ĐŧĐžĐļĐŊа ĐąŅƒĐģĐž Đą ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ.", "album_thumbnail_card_item": "1 ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", - "album_thumbnail_card_items": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", + "album_thumbnail_card_items": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", "album_thumbnail_card_shared": " ¡ ĐĄĐŋŅ–ĐģҌĐŊиК", - "album_thumbnail_shared_by": "ĐŸĐžĐ´Ņ–ĐģĐ¸Đ˛ŅŅ {}", + "album_thumbnail_shared_by": "ĐŸĐžĐ´Ņ–ĐģĐ¸Đ˛ŅŅ {user}", "album_updated": "АĐģŅŒĐąĐžĐŧ ĐžĐŊОвĐģĐĩĐŊĐž", "album_updated_setting_description": "ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐšŅ‚Đĩ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐŊа ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊ҃ ĐŋĐžŅˆŅ‚Ņƒ, ĐēĐžĐģи ҃ ҁĐŋŅ–ĐģҌĐŊĐžĐŧ҃ аĐģŅŒĐąĐžĐŧŅ– С'ŅĐ˛ĐģŅŅŽŅ‚ŅŒŅŅ ĐŊĐžĐ˛Ņ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "album_user_left": "Ви ĐŋĐžĐēиĐŊ҃Đģи {album}", @@ -440,7 +443,7 @@ "archive": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", "archive_or_unarchive_photo": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ айО Ņ€ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "archive_page_no_archived_assets": "НĐĩĐŧĐ°Ņ” Đ°Ņ€Ņ…Ņ–Đ˛ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", - "archive_page_title": "ĐŅ€Ņ…Ņ–Đ˛ ({})", + "archive_page_title": "ĐŅ€Ņ…Ņ–Đ˛ ({count})", "archive_size": "РОСĐŧŅ–Ņ€ Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", "archive_size_description": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ€ĐžĐˇĐŧŅ–Ņ€ Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ (҃ GiB)", "archived": "ĐŅ€Ņ…Ņ–Đ˛", @@ -477,18 +480,18 @@ "assets_added_to_album_count": "ДодаĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "assets_added_to_name_count": "ДодаĐŊĐž {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}} Đ´Đž {hasName, select, true {{name}} other {ĐŊĐžĐ˛ĐžĐŗĐž аĐģŅŒĐąĐžĐŧ҃}}", "assets_count": "{count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", - "assets_deleted_permanently": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž", - "assets_deleted_permanently_from_server": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊĐž ĐŊаСавĐļди С ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", + "assets_deleted_permanently": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž", + "assets_deleted_permanently_from_server": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊĐž ĐŊаСавĐļди С ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", "assets_moved_to_trash_count": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ҃ ҁĐŧŅ–Ņ‚ĐŊиĐē", "assets_permanently_deleted_count": "ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_removed_count": "ВиĐģŅƒŅ‡ĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", - "assets_removed_permanently_from_device": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊŅ– ĐŊаСавĐļди С Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", + "assets_removed_permanently_from_device": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊŅ– ĐŊаСавĐļди С Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "assets_restore_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ŅĐ˛ĐžŅ— аĐēŅ‚Đ¸Đ˛Đ¸ С ҁĐŧŅ–Ņ‚ĐŊиĐēа? ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸! ЗвĐĩŅ€ĐŊŅ–Ņ‚ŅŒ ŅƒĐ˛Đ°ĐŗŅƒ, Ņ‰Đž ĐąŅƒĐ´ŅŒ-ŅĐēŅ– ĐžŅ„ĐģаКĐŊ-аĐēŅ‚Đ¸Đ˛Đ¸ ĐŊĐĩ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊŅ– Ņ‚Đ°ĐēиĐŧ Ņ‡Đ¸ĐŊĐžĐŧ.", "assets_restored_count": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", - "assets_restored_successfully": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ҃ҁĐŋŅ–ŅˆĐŊĐž Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž", - "assets_trashed": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа", + "assets_restored_successfully": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ҃ҁĐŋŅ–ŅˆĐŊĐž Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž", + "assets_trashed": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа", "assets_trashed_count": "ПоĐŧҖ҉ĐĩĐŊĐž в ҁĐŧŅ–Ņ‚ĐŊиĐē {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", - "assets_trashed_from_server": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich", + "assets_trashed_from_server": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich", "assets_were_part_of_album_count": "{count, plural, one {Đ ĐĩŅŅƒŅ€Ņ ĐąŅƒĐ˛} few {Đ ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐģи} other {Đ ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐģи}} вĐļĐĩ Ņ‡Đ°ŅŅ‚Đ¸ĐŊĐžŅŽ аĐģŅŒĐąĐžĐŧ҃", "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "automatic_endpoint_switching_subtitle": "ĐŸŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ŅŅ ĐģĐžĐēаĐģҌĐŊĐž ҇ĐĩŅ€ĐĩС СаСĐŊĐ°Ņ‡ĐĩĐŊ҃ Wi-Fi ĐŧĐĩŅ€ĐĩĐļ҃, ĐēĐžĐģи ҆Đĩ ĐŧĐžĐļĐģивО, Ņ– виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ– С'Ņ”Đ´ĐŊаĐŊĐŊŅ в Ņ–ĐŊŅˆĐ¸Ņ… виĐŋадĐēĐ°Ņ…", @@ -497,20 +500,20 @@ "back_close_deselect": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅ, СаĐēŅ€Đ¸Ņ‚Đ¸ айО ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€", "background_location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ Ņ„ĐžĐŊŅ–", "background_location_permission_content": "ЊОй ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ‚Đ¸ ĐŧĐĩŅ€ĐĩĐļŅ– ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–, Immich ĐŧĐ°Ņ” *СавĐļди* ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ— ĐŗĐĩĐžĐģĐžĐēĐ°Ņ†Ņ–Ņ—, Ņ‰ĐžĐą ĐˇŅ‡Đ¸Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", - "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧи ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ({})", - "backup_album_selection_page_albums_tap": "ĐĸĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ, Ņ‰ĐžĐą вĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸,\nŅ‚ĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đ˛Ņ–Ņ‡Ņ–, Ņ‰ĐžĐą виĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸", + "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧи ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ({count})", + "backup_album_selection_page_albums_tap": "ĐĸĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ, Ņ‰ĐžĐą вĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸, Đ´Đ˛Ņ–Ņ‡Ņ–, Ņ‰ĐžĐą виĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸", "backup_album_selection_page_assets_scatter": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐŊаĐģĐĩĐļĐ°Ņ‚Đ¸ Đ´Đž ĐēŅ–ĐģҌĐēĐžŅ… аĐģŅŒĐąĐžĐŧŅ–Đ˛ вОдĐŊĐžŅ‡Đ°Ņ. ĐĸаĐēиĐŧ Ņ‡Đ¸ĐŊĐžĐŧ, аĐģŅŒĐąĐžĐŧи ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊŅ– айО виĐģŅƒŅ‡ĐĩĐŊŅ– ĐŋŅ–Đ´ Ņ‡Đ°Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ.", "backup_album_selection_page_select_albums": "ОбĐĩŅ€Ņ–Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧи", "backup_album_selection_page_selection_info": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž ĐžĐąŅ€Đ°ĐŊĐĩ", "backup_album_selection_page_total_assets": "Đ—Đ°ĐŗĐ°ĐģҌĐŊа ĐēŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ҃ĐŊŅ–ĐēаĐģҌĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", "backup_all": "ĐŖŅŅ–", - "backup_background_service_backup_failed_message": "НĐĩ вдаĐģĐžŅŅ ĐˇŅ€ĐžĐąĐ¸Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽ...", - "backup_background_service_connection_failed_message": "НĐĩ вдаĐģĐžŅŅ Св'ŅĐˇĐ°Ņ‚Đ¸ŅŅ Ņ–Đˇ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽ...", - "backup_background_service_current_upload_notification": "ЗаваĐŊŅ‚Đ°ĐļŅƒŅ”Ņ‚ŅŒŅŅ {}", + "backup_background_service_backup_failed_message": "НĐĩ вдаĐģĐžŅŅ ĐˇŅ€ĐžĐąĐ¸Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽâ€Ļ", + "backup_background_service_connection_failed_message": "НĐĩ вдаĐģĐžŅŅ Св'ŅĐˇĐ°Ņ‚Đ¸ŅŅ Ņ–Đˇ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽâ€Ļ", + "backup_background_service_current_upload_notification": "ЗаваĐŊŅ‚Đ°ĐļŅƒŅ”Ņ‚ŅŒŅŅ {filename}", "backup_background_service_default_notification": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€ŅŅŽ ĐŊĐ°ŅĐ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŊĐžĐ˛Đ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛â€Ļ", "backup_background_service_error_title": "ПоĐŧиĐģĐēа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", - "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛...", - "backup_background_service_upload_failure_notification": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ {}", + "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛â€Ļ", + "backup_background_service_upload_failure_notification": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ {filename}", "backup_controller_page_albums": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧŅ–Đ˛", "backup_controller_page_background_app_refresh_disabled_content": "ДĐģŅ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ŅƒĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ„ĐžĐŊОвĐĩ ĐžĐŊОвĐģĐĩĐŊĐŊŅ в ĐŧĐĩĐŊŅŽ \"НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ > Đ—Đ°ĐŗĐ°ĐģҌĐŊŅ– > ФОĐŊОвĐĩ ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи\".", "backup_controller_page_background_app_refresh_disabled_title": "ФОĐŊОвĐĩ ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи виĐŧĐēĐŊĐĩĐŊĐĩ", @@ -521,22 +524,21 @@ "backup_controller_page_background_battery_info_title": "ОĐŋŅ‚Đ¸ĐŧŅ–ĐˇĐ°Ņ†Ņ–Ņ ĐąĐ°Ņ‚Đ°Ņ€ĐĩŅ—", "backup_controller_page_background_charging": "Đ›Đ¸ŅˆĐĩ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐˇĐ°Ņ€ŅĐ´ĐļаĐŊĐŊŅ", "backup_controller_page_background_configure_error": "НĐĩ вдаĐģĐžŅŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„ĐžĐŊОвиК ҁĐĩŅ€Đ˛Ņ–Ņ", - "backup_controller_page_background_delay": "Đ—Đ°Ņ‚Ņ€Đ¸ĐŧĐēа ĐŋĐĩŅ€ĐĩĐ´ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊиĐŧ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅĐŧ ĐŊĐžĐ˛Đ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛: {}", + "backup_controller_page_background_delay": "Đ—Đ°Ņ‚Ņ€Đ¸ĐŧĐēа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŊĐžĐ˛Đ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛: {duration}", "backup_controller_page_background_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛Ņƒ ҁĐģ҃ĐļĐąŅƒ, Ņ‰ĐžĐą Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ŅŅ‚Đ˛ĐžŅ€ŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— ĐąŅƒĐ´ŅŒ-ŅĐēĐ¸Ņ… ĐŊĐžĐ˛Đ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐąĐĩС ĐŊĐĩĐžĐąŅ…Ņ–Đ´ĐŊĐžŅŅ‚Ņ– Đ˛Ņ–Đ´ĐēŅ€Đ¸Đ˛Đ°Ņ‚Đ¸ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧ҃", "backup_controller_page_background_is_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ„ĐžĐŊОвĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ виĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_background_is_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ„ĐžĐŊОвĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_background_turn_off": "ВиĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ„ĐžĐŊОвиК ҁĐĩŅ€Đ˛Ņ–Ņ", "backup_controller_page_background_turn_on": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ„ĐžĐŊОвиК ҁĐĩŅ€Đ˛Ņ–Ņ", - "backup_controller_page_background_wifi": "Đ›Đ¸ŅˆĐĩ ĐŊа WiFi", + "backup_controller_page_background_wifi": "Đ›Đ¸ŅˆĐĩ ĐŊа Wi-Fi", "backup_controller_page_backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", - "backup_controller_page_backup_selected": "ĐžĐąŅ€Đ°ĐŊĐž:", + "backup_controller_page_backup_selected": "ĐžĐąŅ€Đ°ĐŊĐž: ", "backup_controller_page_backup_sub": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", - "backup_controller_page_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž: {}", + "backup_controller_page_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž: {date}", "backup_controller_page_desc_backup": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŊа ĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŧ҃ ĐŋĐģаĐŊŅ–, Ņ‰ĐžĐą Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€ ĐŋŅ–Đ´ Ņ‡Đ°Ņ Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Ņ‚Ņ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи.", - "backup_controller_page_excluded": "ВиĐģŅƒŅ‡ĐĩĐŊĐž:", - "backup_controller_page_failed": "НĐĩвдаĐģŅ– ({})", - "backup_controller_page_filename": "Назва Ņ„Đ°ĐšĐģ҃: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "ВиĐģŅƒŅ‡ĐĩĐŊĐž: ", + "backup_controller_page_failed": "НĐĩвдаĐģŅ– ({count})", + "backup_controller_page_filename": "Назва Ņ„Đ°ĐšĐģ҃: {filename} [{size}]", "backup_controller_page_info": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ", "backup_controller_page_none_selected": "ĐŅ–Ņ‡ĐžĐŗĐž ĐŊĐĩ ĐžĐąŅ€Đ°ĐŊĐž", "backup_controller_page_remainder": "ЗаĐģĐ¸ŅˆĐžĐē", @@ -545,7 +547,7 @@ "backup_controller_page_start_backup": "ĐŸĐžŅ‡Đ°Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_controller_page_status_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ– виĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_status_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ– Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", - "backup_controller_page_storage_format": "{} Ņ–Đˇ {} ҁĐŋĐžĐļĐ¸Ņ‚Đž", + "backup_controller_page_storage_format": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐž: {used} С {total}", "backup_controller_page_to_backup": "АĐģŅŒĐąĐžĐŧи Đ´Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_controller_page_total_sub": "ĐŖŅŅ– ҃ĐŊŅ–ĐēаĐģҌĐŊŅ– СĐŊŅ–ĐŧĐēи Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž С Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛", "backup_controller_page_turn_off": "ВиĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–", @@ -570,21 +572,21 @@ "bulk_keep_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ СаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}? ĐĻĐĩ дОСвОĐģĐ¸Ņ‚ŅŒ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŗŅ€ŅƒĐŋи Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛ ĐąĐĩС видаĐģĐĩĐŊĐŊŅ Ņ‡ĐžĐŗĐž-ĐŊĐĩĐąŅƒĐ´ŅŒ.", "bulk_trash_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐēиĐŊŅƒŅ‚Đ¸ в ҁĐŧŅ–Ņ‚ĐŊиĐē {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ĐŧĐ°ŅĐžĐ˛Đž? ĐĻĐĩ СаĐģĐ¸ŅˆĐ¸Ņ‚ŅŒ ĐŊĐ°ĐšĐąŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ ҃ ĐēĐžĐļĐŊŅ–Đš ĐŗŅ€ŅƒĐŋŅ– Ņ– виĐēиĐŊĐĩ в ҁĐŧŅ–Ņ‚ĐŊиĐē Đ˛ŅŅ– Ņ–ĐŊŅˆŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸.", "buy": "ĐŸŅ€Đ¸Đ´ĐąĐ°ĐšŅ‚Đĩ Immich", - "cache_settings_album_thumbnails": "ĐœŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ ŅŅ‚ĐžŅ€Ņ–ĐŊĐžĐē ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи ({} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", + "cache_settings_album_thumbnails": "ĐœŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ ŅŅ‚ĐžŅ€Ņ–ĐŊĐžĐē ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи ({count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", "cache_settings_clear_cache_button": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐĩ҈", "cache_settings_clear_cache_button_title": "ĐžŅ‡Đ¸Ņ‰Đ°Ņ” ĐēĐĩ҈ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи. ĐĻĐĩ ŅŅƒŅ‚Ņ‚Ņ”Đ˛Đž СĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи, Đ´ĐžĐēи ĐēĐĩ҈ ĐŊĐĩ ĐąŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩĐąŅƒĐ´ĐžĐ˛Đ°ĐŊĐž.", "cache_settings_duplicated_assets_clear_button": "ОЧИСĐĸИĐĸИ", "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž, СаĐŊĐĩҁĐĩĐŊŅ– Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐŧ ҃ Ņ‡ĐžŅ€ĐŊиК ҁĐŋĐ¸ŅĐžĐē", - "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({})", - "cache_settings_image_cache_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ ({} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", + "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({count})", + "cache_settings_image_cache_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ ({count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", "cache_settings_statistics_album": "Đ‘Ņ–ĐąĐģŅ–ĐžŅ‚Đĩ҇ĐŊŅ– ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸", - "cache_settings_statistics_assets": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({})", + "cache_settings_statistics_assets": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({size})", "cache_settings_statistics_full": "ПовĐŊĐžŅ€ĐˇĐžĐŧŅ–Ņ€ĐŊŅ– ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "cache_settings_statistics_shared": "ĐœŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛", "cache_settings_statistics_thumbnail": "ĐœŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸", "cache_settings_statistics_title": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ ĐēĐĩ҈҃", "cache_settings_subtitle": "КоĐŊŅ‚Ņ€ĐžĐģŅŽŅ” ĐēĐĩŅˆŅƒĐ˛Đ°ĐŊĐŊŅ ҃ ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŧ҃ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", - "cache_settings_thumbnail_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ ({} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", + "cache_settings_thumbnail_size": "РОСĐŧŅ–Ņ€ ĐēĐĩŅˆĐžĐ˛Đ°ĐŊĐ¸Ņ… ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ ({count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸)", "cache_settings_tile_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋОвĐĩĐ´Ņ–ĐŊĐēĐžŅŽ ĐģĐžĐēаĐģҌĐŊĐžĐŗĐž ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "cache_settings_tile_title": "ЛоĐēаĐģҌĐŊĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ", "cache_settings_title": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐēĐĩŅˆŅƒĐ˛Đ°ĐŊĐŊŅ", @@ -606,10 +608,11 @@ "change_password": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "change_password_description": "ĐĻĐĩ айО ĐŋĐĩŅ€ŅˆĐ¸Đš Ņ€Đ°Đˇ, ĐēĐžĐģи ви ŅƒĐ˛Ņ–ĐšŅˆĐģи в ŅĐ¸ŅŅ‚ĐĩĐŧ҃, айО ĐąŅƒĐģĐž ĐˇŅ€ĐžĐąĐģĐĩĐŊĐž СаĐŋĐ¸Ņ‚ ĐŊа СĐŧŅ–ĐŊ҃ Đ˛Đ°ŅˆĐžĐŗĐž ĐŋĐ°Ņ€ĐžĐģŅ. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ввĐĩĐ´Ņ–Ņ‚ŅŒ ĐŊОвиК ĐŋĐ°Ņ€ĐžĐģҌ ĐŊиĐļ҇Đĩ.", "change_password_form_confirm_password": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", - "change_password_form_description": "ĐŸŅ€Đ¸Đ˛Ņ–Ņ‚ {name},\n\nВи айО айО вĐŋĐĩŅ€ŅˆĐĩ Đ˛Ņ…ĐžĐ´Đ¸Ņ‚Đĩ ҃ ŅĐ¸ŅŅ‚ĐĩĐŧ҃, айО ĐąŅƒĐģĐž ĐˇŅ€ĐžĐąĐģĐĩĐŊĐž СаĐŋĐ¸Ņ‚ ĐŊа СĐŧŅ–ĐŊ҃ Đ˛Đ°ŅˆĐžĐŗĐž ĐŋĐ°Ņ€ĐžĐģŅ. \nВвĐĩĐ´Ņ–Ņ‚ŅŒ Đ˛Đ°Ņˆ ĐŊОвиК ĐŋĐ°Ņ€ĐžĐģҌ.", + "change_password_form_description": "ĐŸŅ€Đ¸Đ˛Ņ–Ņ‚, {name},\n\nĐĻĐĩ айО Đ˛Đ°Ņˆ ĐŋĐĩŅ€ŅˆĐ¸Đš Đ˛Ņ…Ņ–Đ´ ҃ ŅĐ¸ŅŅ‚ĐĩĐŧ҃, айО ĐąŅƒĐģĐž ĐŊĐ°Đ´Ņ–ŅĐģаĐŊĐž СаĐŋĐ¸Ņ‚ ĐŊа СĐŧŅ–ĐŊ҃ ĐŋĐ°Ņ€ĐžĐģŅ. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ввĐĩĐ´Ņ–Ņ‚ŅŒ ĐŊОвиК ĐŋĐ°Ņ€ĐžĐģҌ ĐŊиĐļ҇Đĩ.", "change_password_form_new_password": "Новий ĐŋĐ°Ņ€ĐžĐģҌ", "change_password_form_password_mismatch": "ĐŸĐ°Ņ€ĐžĐģŅ– ĐŊĐĩ ҁĐŋŅ–Đ˛ĐŋĐ°Đ´Đ°ŅŽŅ‚ŅŒ", "change_password_form_reenter_new_password": "ĐŸĐžĐ˛Ņ‚ĐžŅ€Ņ–Ņ‚ŅŒ ĐŊОвиК ĐŋĐ°Ņ€ĐžĐģҌ", + "change_pin_code": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ PIN-ĐēОд", "change_your_password": "ЗĐŧŅ–ĐŊŅ–Ņ‚ŅŒ ŅĐ˛Ņ–Đš ĐŋĐ°Ņ€ĐžĐģҌ", "changed_visibility_successfully": "ВидиĐŧŅ–ŅŅ‚ŅŒ ҃ҁĐŋŅ–ŅˆĐŊĐž СĐŧŅ–ĐŊĐĩĐŊĐž", "check_all": "ПозĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ Đ˛ŅŅ–", @@ -624,13 +627,12 @@ "clear_all_recent_searches": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐžŅŅ‚Đ°ĐŊĐŊŅ– ĐŋĐžŅˆŅƒĐēĐžĐ˛Ņ– СаĐŋĐ¸Ņ‚Đ¸", "clear_message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ", "clear_value": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ", - "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", "client_cert_import": "ІĐŧĐŋĐžŅ€Ņ‚", "client_cert_import_success_msg": "КĐģŅ–Ņ”ĐŊŅ‚ŅŅŒĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ Ņ–ĐŧĐŋĐžŅ€Ņ‚ĐžĐ˛Đ°ĐŊĐž", "client_cert_invalid_msg": "НĐĩĐ´Ņ–ĐšŅĐŊиК Ņ„Đ°ĐšĐģ ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ° айО ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ", "client_cert_remove_msg": "КĐģŅ–Ņ”ĐŊŅ‚ŅŅŒĐēиК ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ видаĐģĐĩĐŊĐž", - "client_cert_subtitle": "ĐŸŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚ŅŒŅŅ ĐģĐ¸ŅˆĐĩ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ PKCS12 (.p12, .pfx). ІĐŧĐŋĐžŅ€Ņ‚/видаĐģĐĩĐŊĐŊŅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ° Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐĩ ĐģĐ¸ŅˆĐĩ ĐŋĐĩŅ€ĐĩĐ´ Đ˛Ņ…ĐžĐ´ĐžĐŧ ҃ ŅĐ¸ŅŅ‚ĐĩĐŧ҃.", + "client_cert_subtitle": "ĐŸŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚ŅŒŅŅ ĐģĐ¸ŅˆĐĩ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ PKCS12 (.p12, .pfx). ІĐŧĐŋĐžŅ€Ņ‚/видаĐģĐĩĐŊĐŊŅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ° Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ĐģĐ¸ŅˆĐĩ Đ´Đž Đ˛Ņ…ĐžĐ´Ņƒ в ŅĐ¸ŅŅ‚ĐĩĐŧ҃", "client_cert_title": "КĐģŅ–Ņ”ĐŊŅ‚ŅŅŒĐēиК SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚", "clockwise": "По ĐŗĐžĐ´Đ¸ĐŊĐŊиĐēĐžĐ˛Ņ–Đš ҁ҂ҀҖĐģ҆Җ", "close": "ЗаĐēŅ€Đ¸Ņ‚Đ¸", @@ -650,11 +652,12 @@ "confirm_delete_face": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ОйĐģĐ¸Ņ‡Ņ‡Ņ {name} С аĐēŅ‚Đ¸Đ˛Ņƒ?", "confirm_delete_shared_link": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆Đĩ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ?", "confirm_keep_this_delete_others": "ĐŖŅŅ– Ņ–ĐŊŅˆŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҁ҂ĐĩĐē҃ ĐąŅƒĐ´Đĩ видаĐģĐĩĐŊĐž, ĐžĐēҀҖĐŧ Ņ†ŅŒĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ. Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸?", + "confirm_new_pin_code": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ĐŊОвиК PIN-ĐēОд", "confirm_password": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "contain": "ĐœŅ–ŅŅ‚Đ¸Ņ‚Đ¸", "context": "КоĐŊŅ‚ĐĩĐēҁ҂", "continue": "ĐŸŅ€ĐžĐ´ĐžĐ˛ĐļŅƒĐšŅ‚Đĩ", - "control_bottom_app_bar_album_info_shared": "{} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ¡ ĐĄĐŋŅ–ĐģҌĐŊŅ–", + "control_bottom_app_bar_album_info_shared": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ¡ ĐĄĐŋŅ–ĐģҌĐŊŅ–", "control_bottom_app_bar_create_new_album": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŊОвиК аĐģŅŒĐąĐžĐŧ", "control_bottom_app_bar_delete_from_immich": "ВидаĐģĐ¸Ņ‚Đ¸ С Immich", "control_bottom_app_bar_delete_from_local": "ВидаĐģĐ¸Ņ‚Đ¸ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", @@ -695,16 +698,14 @@ "crop": "ĐšĐ°Đ´Ņ€ŅƒĐ˛Đ°Ņ‚Đ¸", "curated_object_page_title": "Đ Đĩ҇Җ", "current_device": "ĐŸĐžŅ‚ĐžŅ‡ĐŊиК ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš", + "current_pin_code": "ĐŸĐžŅ‚ĐžŅ‡ĐŊиК PIN-ĐēОд", "current_server_address": "ĐŸĐžŅ‚ĐžŅ‡ĐŊа Đ°Đ´Ņ€ĐĩŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēиК Ņ€ĐĩĐŗŅ–ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Đ¸ Ņ‚Đ° Ņ‡Đ¸ŅĐģа С ŅƒŅ€Đ°Ņ…ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ĐŧОви Ņ‚Đ° Ņ€ĐĩĐŗŅ–ĐžĐŊ҃", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "ĐĸĐĩĐŧĐŊиК", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋҖҁĐģŅ", "date_and_time": "Đ”Đ°Ņ‚Đ° Ņ– Ņ‡Đ°Ņ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐž СйĐĩŅ€ĐĩĐļĐĩĐŊа", "date_range": "ĐŸŅ€ĐžĐŧŅ–ĐļĐžĐē Ņ‡Đ°ŅŅƒ", "day": "ДĐĩĐŊҌ", @@ -719,8 +720,8 @@ "delete_album": "ВидаĐģĐ¸Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ", "delete_api_key_prompt": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆ĐĩĐš ĐēĐģŅŽŅ‡ API?", "delete_dialog_alert": "ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Immich Ņ‚Đ° Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", - "delete_dialog_alert_local": "ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ видаĐģĐĩĐŊŅ– видаĐģĐĩĐŊŅ– С Đ’Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ, аĐģĐĩ СаĐģĐ¸ŅˆĐ°Ņ‚ŅŒŅŅ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиĐŧи ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich", - "delete_dialog_alert_local_non_backed_up": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— Đ´ĐĩŅĐēĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐŊĐĩ ĐąŅƒĐģи СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– в Immich Ņ– ĐąŅƒĐ´ŅƒŅ‚ŅŒ видаĐģĐĩĐŊŅ– видаĐģĐĩĐŊŅ– С Đ’Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", + "delete_dialog_alert_local": "ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ, аĐģĐĩ СаĐģĐ¸ŅˆĐ°Ņ‚ŅŒŅŅ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиĐŧи ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich", + "delete_dialog_alert_local_non_backed_up": "ДĐĩŅĐēŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŊĐĩ ĐąŅƒĐģи СйĐĩŅ€ĐĩĐļĐĩĐŊŅ– ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich Ņ– ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "delete_dialog_alert_remote": "ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐŊаСавĐļди видаĐģĐĩĐŊŅ– С ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Immich", "delete_dialog_ok_force": "Đ’ŅĐĩ ОдĐŊĐž видаĐģĐ¸Ņ‚Đ¸", "delete_dialog_title": "ВидаĐģĐ¸Ņ‚Đ¸ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž", @@ -746,7 +747,6 @@ "direction": "НаĐŋŅ€ŅĐŧ", "disabled": "ВиĐŧĐēĐŊĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąĐžŅ€ĐžĐŊĐ¸Ņ‚Đ¸ Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°ĐŊĐŊŅ", - "discord": "Discord", "discover": "Đ’Đ¸ŅĐ˛Đ¸Ņ‚Đ¸", "dismiss_all_errors": "ĐŸŅ€ĐžĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŋĐžĐŧиĐģĐēи", "dismiss_error": "ĐŸŅ€ĐžĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐžĐŧиĐģĐē҃", @@ -763,7 +763,7 @@ "download_enqueue": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊĐž в ҇ĐĩŅ€ĐŗŅƒ", "download_error": "ПоĐŧиĐģĐēа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "download_failed": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŊĐĩ вдаĐģĐžŅŅ", - "download_filename": "Ņ„Đ°ĐšĐģ: {}", + "download_filename": "Ņ„Đ°ĐšĐģ: {filename}", "download_finished": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СаĐēŅ–ĐŊ҇ĐĩĐŊĐž", "download_include_embedded_motion_videos": "Đ’ĐąŅƒĐ´ĐžĐ˛Đ°ĐŊŅ– Đ˛Ņ–Đ´ĐĩĐž", "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ Đ˛Ņ–Đ´ĐĩĐž, Đ˛ĐąŅƒĐ´ĐžĐ˛Đ°ĐŊŅ– в Ņ€ŅƒŅ…ĐžĐŧŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—, ŅĐē ĐžĐēŅ€ĐĩĐŧиК Ņ„Đ°ĐšĐģ", @@ -814,12 +814,12 @@ "enabled": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", "end_date": "Đ”Đ°Ņ‚Đ° СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ", "enqueued": "ĐŖ ҇ĐĩŅ€ĐˇŅ–", - "enter_wifi_name": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŊĐ°ĐˇĐ˛Ņƒ WiFi", + "enter_wifi_name": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi", "error": "ПоĐŧиĐģĐēа", "error_change_sort_album": "НĐĩ вдаĐģĐžŅŅ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧ҃", "error_delete_face": "ПоĐŧиĐģĐēа ĐŋŅ€Đ¸ видаĐģĐĩĐŊĐŊŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ С аĐēŅ‚Đ¸Đ˛Ņƒ", "error_loading_image": "ПоĐŧиĐģĐēа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", - "error_saving_image": "ПоĐŧиĐģĐēа: {}", + "error_saving_image": "ПоĐŧиĐģĐēа: {error}", "error_title": "ПоĐŧиĐģĐēа: Ņ‰ĐžŅŅŒ ĐŋŅ–ŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "errors": { "cannot_navigate_next_asset": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ", @@ -849,10 +849,12 @@ "failed_to_keep_this_delete_others": "НĐĩ вдаĐģĐžŅŅ СйĐĩŅ€ĐĩĐŗŅ‚Đ¸ ҆ĐĩĐš Ņ€ĐĩŅŅƒŅ€Ņ Ņ– видаĐģĐ¸Ņ‚Đ¸ Ņ–ĐŊŅˆŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_load_asset": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ", "failed_to_load_assets": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", + "failed_to_load_notifications": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", "failed_to_load_people": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš", "failed_to_remove_product_key": "НĐĩ вдаĐģĐžŅŅ видаĐģĐ¸Ņ‚Đ¸ ĐēĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Ņƒ", "failed_to_stack_assets": "НĐĩ вдаĐģĐžŅŅ ĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_unstack_assets": "НĐĩ вдаĐģĐžŅŅ Ņ€ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", + "failed_to_update_notification_status": "НĐĩ вдаĐģĐžŅŅ ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅŅ‚Đ°Ņ‚ŅƒŅ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", "import_path_already_exists": "ĐĻĐĩĐš ҈ĐģŅŅ… Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ вĐļĐĩ ҖҁĐŊŅƒŅ”.", "incorrect_email_or_password": "НĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊа Đ°Đ´Ņ€ĐĩŅĐ° ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐžŅ— ĐŋĐžŅˆŅ‚Đ¸ айО ĐŋĐ°Ņ€ĐžĐģҌ", "paths_validation_failed": "{paths, plural, one {# ҈ĐģŅŅ…} few {# ҈ĐģŅŅ…Đ¸} many {# ҈ĐģŅŅ…Ņ–Đ˛} other {# ҈ĐģŅŅ…Ņƒ}} ĐŊĐĩ ĐŋŅ€ĐžĐšŅˆĐģĐž ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃", @@ -920,6 +922,7 @@ "unable_to_remove_reaction": "НĐĩ вдаĐģĐžŅŅ видаĐģĐ¸Ņ‚Đ¸ Ņ€ĐĩаĐēŅ†Ņ–ŅŽ", "unable_to_repair_items": "НĐĩ вдаĐģĐžŅŅ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "unable_to_reset_password": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ҁĐēиĐŊŅƒŅ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", + "unable_to_reset_pin_code": "НĐĩĐŧĐžĐļĐģивО ҁĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд", "unable_to_resolve_duplicate": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚", "unable_to_restore_assets": "НĐĩĐŧĐžĐļĐģивО Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ аĐēŅ‚Đ¸Đ˛Đ¸", "unable_to_restore_trash": "НĐĩ вдаĐģĐžŅŅ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ вĐŧҖҁ҂", @@ -947,16 +950,15 @@ "unable_to_update_user": "НĐĩĐŧĐžĐļĐģивО ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ даĐŊŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "unable_to_upload_file": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ„Đ°ĐšĐģ" }, - "exif": "Exif", "exif_bottom_sheet_description": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ...", "exif_bottom_sheet_details": "ПОДРОБИĐĻІ", "exif_bottom_sheet_location": "МІСĐĻЕ", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ–Đŧ'Ņ", - "exif_bottom_sheet_person_age": "Đ’Ņ–Đē {}", - "exif_bottom_sheet_person_age_months": "Đ’Ņ–Đē {} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", - "exif_bottom_sheet_person_age_year_months": "Đ’Ņ–Đē 1 ҀҖĐē, {} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", - "exif_bottom_sheet_person_age_years": "Đ’Ņ–Đē {}", + "exif_bottom_sheet_person_age": "Đ’Ņ–Đē {age}", + "exif_bottom_sheet_person_age_months": "Đ’Ņ–Đē {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", + "exif_bottom_sheet_person_age_year_months": "Đ’Ņ–Đē 1 ҀҖĐē, {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", + "exif_bottom_sheet_person_age_years": "Đ’Ņ–Đē {years}", "exit_slideshow": "Đ’Đ¸ĐšŅ‚Đ¸ ĐˇŅ– ҁĐģаКд-ŅˆĐžŅƒ", "expand_all": "Đ ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Đ˛ŅĐĩ", "experimental_settings_new_asset_list_subtitle": "В Ņ€ĐžĐˇŅ€ĐžĐąŅ†Ņ–", @@ -974,7 +976,7 @@ "external": "ЗовĐŊŅ–ŅˆĐŊŅ–", "external_libraries": "ЗовĐŊŅ–ŅˆĐŊŅ– ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи", "external_network": "ЗовĐŊŅ–ŅˆĐŊŅ ĐŧĐĩŅ€ĐĩĐļа", - "external_network_sheet_info": "КоĐģи ви ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž ĐŋĐĩŅ€ĐĩваĐļĐŊĐžŅ— ĐŧĐĩŅ€ĐĩĐļŅ– WiFi, Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ĐŋĐĩŅ€ŅˆŅƒ С ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… ĐŊиĐļ҇Đĩ URL-Đ°Đ´Ņ€Đĩҁ, ŅĐē҃ Đ˛Ņ–ĐŊ СĐŧĐžĐļĐĩ Đ´ĐžŅŅĐŗŅ‚Đ¸, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ СвĐĩŅ€Ņ…Ņƒ вĐŊиС", + "external_network_sheet_info": "КоĐģи ви ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž ĐŋĐĩŅ€ĐĩваĐļĐŊĐžŅ— ĐŧĐĩŅ€ĐĩĐļŅ– Wi-Fi, Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ĐŋĐĩŅ€ŅˆŅƒ С ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… ĐŊиĐļ҇Đĩ URL-Đ°Đ´Ņ€Đĩҁ, ŅĐē҃ Đ˛Ņ–ĐŊ СĐŧĐžĐļĐĩ Đ´ĐžŅŅĐŗŅ‚Đ¸, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ СвĐĩŅ€Ņ…Ņƒ вĐŊиС", "face_unassigned": "НĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž", "failed": "НĐĩ вдаĐģĐžŅŅ", "failed_to_load_assets": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", @@ -992,6 +994,7 @@ "filetype": "ĐĸиĐŋ Ņ„Đ°ĐšĐģ҃", "filter": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€", "filter_people": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€ ĐŋĐž ĐģŅŽĐ´ŅŅ…", + "filter_places": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€ ĐŋĐž ĐŧŅ–ŅŅ†ŅŅ…", "find_them_fast": "ШвидĐēĐž СĐŊĐ°Ņ…ĐžĐ´ŅŒŅ‚Đĩ Ņ—Ņ… Са ĐŊĐ°ĐˇĐ˛ĐžŅŽ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ ĐŋĐžŅˆŅƒĐē҃", "fix_incorrect_match": "ВиĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊиК ĐˇĐąŅ–Đŗ", "folder": "ПаĐŋĐēа", @@ -1020,7 +1023,7 @@ "header_settings_field_validator_msg": "ЗĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐąŅƒŅ‚Đ¸ ĐŋĐžŅ€ĐžĐļĐŊŅ–Đŧ", "header_settings_header_name_input": "ІĐŧ'Ņ ĐˇĐ°ĐŗĐžĐģОвĐē҃", "header_settings_header_value_input": "ЗĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐˇĐ°ĐŗĐžĐģОвĐē҃", - "headers_settings_tile_subtitle": "ВизĐŊĐ°Ņ‡Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ, ŅĐēŅ– ĐŋŅ€ĐžĐŗŅ€Đ°Đŧа ĐŧĐ°Ņ” ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅ€ĐĩĐļĐĩвиĐŧ СаĐŋĐ¸Ņ‚ĐžĐŧ.", + "headers_settings_tile_subtitle": "ВизĐŊĐ°Ņ‡Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ, ŅĐēŅ– ĐŋŅ€ĐžĐŗŅ€Đ°Đŧа ĐŧĐ°Ņ” ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅ€ĐĩĐļĐĩвиĐŧ СаĐŋĐ¸Ņ‚ĐžĐŧ", "headers_settings_tile_title": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°ĐģҌĐŊĐ¸Ņ†ŅŒĐēŅ– ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ", "hi_user": "ĐŸŅ€Đ¸Đ˛Ņ–Ņ‚ {name} ({email})", "hide_all_people": "ĐĄŅ…ĐžĐ˛Đ°Ņ‚Đ¸ Đ˛ŅŅ–Ņ…", @@ -1040,7 +1043,7 @@ "home_page_delete_remote_err_local": "ЛоĐēаĐģҌĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) вĐļĐĩ в ĐŋŅ€ĐžŅ†ĐĩҁҖ видаĐģĐĩĐŊĐŊŅ С ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "home_page_favorite_err_local": "ПоĐēи Ņ‰Đž ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ… ĐģĐžĐēаĐģҌĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "home_page_favorite_err_partner": "ПоĐēи Ņ‰Đž ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", - "home_page_first_time_notice": "Đ¯ĐēŅ‰Đž ви вĐŋĐĩŅ€ŅˆĐĩ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒŅ”Ņ‚ĐĩŅŅ ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐžŅŽ, ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ви Đ˛Đ¸ĐąŅ€Đ°Đģи аĐģŅŒĐąĐžĐŧи Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ŅƒĐ˛Đ°ĐŊĐŊŅ, Ņ‰ĐžĐą ĐŧĐžĐŗŅ‚Đ¸ СаĐŋОвĐŊŅŽĐ˛Đ°Ņ‚Đ¸ Ņ…Ņ€ĐžĐŊĐžĐģĐžĐŗŅ–ŅŽ СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž в аĐģŅŒĐąĐžĐŧĐ°Ņ….", + "home_page_first_time_notice": "Đ¯ĐēŅ‰Đž ви ĐēĐžŅ€Đ¸ŅŅ‚ŅƒŅ”Ņ‚ĐĩŅŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐŧ вĐŋĐĩŅ€ŅˆĐĩ, ĐąŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ОйĐĩŅ€Ņ–Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ, Ņ‰ĐžĐą ĐŊа ҈ĐēаĐģŅ– Ņ‡Đ°ŅŅƒ Đˇâ€™ŅĐ˛Đ¸ĐģĐ¸ŅŅ Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "home_page_share_err_local": "НĐĩĐŧĐžĐļĐģивО ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ĐģĐžĐēаĐģҌĐŊиĐŧи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧи ҇ĐĩŅ€ĐĩС ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "home_page_upload_err_limit": "МоĐļĐŊа ваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐŊĐĩ ĐąŅ–ĐģҌ҈Đĩ 30 ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ вОдĐŊĐžŅ‡Đ°Ņ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "host": "ĐĨĐžŅŅ‚", @@ -1120,7 +1123,7 @@ "local_network": "ЛоĐēаĐģҌĐŊа ĐŧĐĩŅ€ĐĩĐļа", "local_network_sheet_info": "Đ”ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ҆ĐĩĐš URL, ĐēĐžĐģи виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ вĐēаСаĐŊа Wi-Fi ĐŧĐĩŅ€ĐĩĐļа", "location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", - "location_permission_content": "ЊОй ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ‚Đ¸ ĐŧĐĩŅ€ĐĩĐļŅ– ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–, Immich ĐŧĐ°Ņ” *СавĐļди* ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ— ĐŗĐĩĐžĐģĐžĐēĐ°Ņ†Ņ–Ņ—, Ņ‰ĐžĐą ĐˇŅ‡Đ¸Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", + "location_permission_content": "ЊОй ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ‚Đ¸ ĐŧĐĩŅ€ĐĩĐļŅ– ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–, Immich ĐŧĐ°Ņ” СавĐļди ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ— ĐŗĐĩĐžĐģĐžĐēĐ°Ņ†Ņ–Ņ—, Ņ‰ĐžĐą ĐˇŅ‡Đ¸Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", "location_picker_choose_on_map": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐŊа ĐŧаĐŋŅ–", "location_picker_latitude_error": "ВĐēаĐļŅ–Ņ‚ŅŒ Đ´Ņ–ĐšŅĐŊ҃ ŅˆĐ¸Ņ€ĐžŅ‚Ņƒ", "location_picker_latitude_hint": "ВĐēаĐļŅ–Ņ‚ŅŒ ŅˆĐ¸Ņ€ĐžŅ‚Ņƒ", @@ -1132,10 +1135,8 @@ "logged_out_device": "Đ’Đ¸Ņ…Ņ–Đ´ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "login": "Đ’Ņ…Ņ–Đ´", "login_disabled": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ ĐąŅƒĐģа Đ˛Ņ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊа", - "login_form_api_exception": "ПоĐŧиĐģĐēа API. ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ Đ°Đ´Ņ€Đĩҁ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Ņ– ҁĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ СĐŊĐžĐ˛Ņƒ", + "login_form_api_exception": "ПоĐŧиĐģĐēа API. ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ Đ°Đ´Ņ€Đĩҁ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Ņ– ҁĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ СĐŊĐžĐ˛Ņƒ.", "login_form_back_button_text": "Назад", - "login_form_email_hint": "youremail@email.com", - "login_form_endpoint_hint": "http://your-server-ip:port", "login_form_endpoint_url": "ĐĐ´Ņ€ĐĩŅĐ° Ņ‚ĐžŅ‡Đēи Đ´ĐžŅŅƒĐŋ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ", "login_form_err_http": "ВĐēаĐļŅ–Ņ‚ŅŒ http:// айО https://", "login_form_err_invalid_email": "ĐĨийĐŊиК Ņ–ĐŧĐĩĐšĐģ", @@ -1149,7 +1150,7 @@ "login_form_password_hint": "ĐŋĐ°Ņ€ĐžĐģҌ", "login_form_save_login": "ЗаĐŋаĐŧ'ŅŅ‚Đ°Ņ‚Đ¸ Đ˛Ņ…Ņ–Đ´", "login_form_server_empty": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ URL-Đ°Đ´Ņ€Đĩҁ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", - "login_form_server_error": "НĐĩĐŧĐžĐļĐģивО С'Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ŅŅ Ņ–Đˇ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", + "login_form_server_error": "НĐĩ вдаĐģĐžŅŅ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸ŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "login_has_been_disabled": "Đ’Ņ…Ņ–Đ´ ĐąŅƒĐģĐž виĐŧĐēĐŊĐĩĐŊĐž.", "login_password_changed_error": "ПоĐŧиĐģĐēа ҃ ĐžĐŊОвĐģĐĩĐŊŅ– Đ˛Đ°ŅˆĐžĐŗĐž ĐŋĐ°Ņ€ĐžĐģŅ", "login_password_changed_success": "ĐŸĐ°Ņ€ĐžĐģҌ ĐžĐŊОвĐģĐĩĐŊĐž ҃ҁĐŋŅ–ŅˆĐŊĐž", @@ -1170,8 +1171,8 @@ "manage_your_devices": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи, ŅĐēŅ– ŅƒĐ˛Ņ–ĐšŅˆĐģи в ŅĐ¸ŅŅ‚ĐĩĐŧ҃", "manage_your_oauth_connection": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊĐžĐŗĐž OAuth", "map": "МаĐŋа", - "map_assets_in_bound": "{} Ņ„ĐžŅ‚Đž", - "map_assets_in_bounds": "{} Ņ„ĐžŅ‚Đž", + "map_assets_in_bound": "{count} Ņ„ĐžŅ‚Đž", + "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚Đž", "map_cannot_get_user_location": "НĐĩ ĐŧĐžĐļ҃ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "map_location_dialog_yes": "ĐĸаĐē", "map_location_picker_page_use_location": "ĐĻĐĩ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", @@ -1185,15 +1186,18 @@ "map_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŧаĐŋи", "map_settings_dark_mode": "ĐĸĐĩĐŧĐŊиК Ņ€ĐĩĐļиĐŧ", "map_settings_date_range_option_day": "МиĐŊ҃ĐģŅ– 24 ĐŗĐžĐ´Đ¸ĐŊи", - "map_settings_date_range_option_days": "МиĐŊ҃ĐģĐ¸Ņ… {} Đ´ĐŊŅ–Đ˛", + "map_settings_date_range_option_days": "МиĐŊ҃ĐģĐ¸Ņ… {days} Đ´ĐŊŅ–Đ˛", "map_settings_date_range_option_year": "МиĐŊ҃ĐģиК ҀҖĐē", - "map_settings_date_range_option_years": "МиĐŊ҃ĐģŅ– {} Ņ€ĐžĐēи", + "map_settings_date_range_option_years": "МиĐŊ҃ĐģŅ– {years} Ņ€ĐžĐēи", "map_settings_dialog_title": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŧаĐŋи", "map_settings_include_show_archived": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ Đ°Ņ€Ņ…Ņ–Đ˛", "map_settings_include_show_partners": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "map_settings_only_show_favorites": "Đ›Đ¸ŅˆĐĩ ҃ĐģŅŽĐąĐĩĐŊŅ–", "map_settings_theme_settings": "ĐĸĐĩĐŧа ĐēĐ°Ņ€Ņ‚Đ¸", "map_zoom_to_see_photos": "ЗĐŧĐĩĐŊŅˆŅ–Ņ‚ŅŒ, айи ĐŋĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ СĐŊŅ–ĐŧĐēи", + "mark_all_as_read": "ПозĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊŅ–", + "mark_as_read": "ПозĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ", + "marked_all_as_read": "ПозĐŊĐ°Ņ‡ĐĩĐŊĐž Đ˛ŅŅ– ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊŅ–", "matches": "Đ—ĐąŅ–ĐŗĐ¸", "media_type": "ĐĸиĐŋ ĐŧĐĩĐ´Ņ–Đ°", "memories": "ĐĄĐŋĐžĐŗĐ°Đ´Đ¸", @@ -1202,8 +1206,6 @@ "memories_setting_description": "КĐĩŅ€ŅƒĐšŅ‚Đĩ Ņ‚Đ¸Đŧ, Ņ‰Đž ĐąĐ°Ņ‡Đ¸Ņ‚Đĩ ҃ ŅĐ˛ĐžŅ—Ņ… ҁĐŋĐžĐŗĐ°Đ´Đ°Ņ…", "memories_start_over": "ĐŸĐžŅ‡Đ°Ņ‚Đ¸ СаĐŊОвО", "memories_swipe_to_close": "ЗĐŧĐ°Ņ…ĐŊŅ–Ņ‚ŅŒ Đ˛ĐŗĐžŅ€Ņƒ, Ņ‰ĐžĐą СаĐēŅ€Đ¸Ņ‚Đ¸", - "memories_year_ago": "Đ Ņ–Đē Ņ‚ĐžĐŧ҃", - "memories_years_ago": "{} Ņ€ĐžĐēŅ–Đ˛ Ņ‚ĐžĐŧ҃", "memory": "ПаĐŧ'ŅŅ‚ŅŒ", "memory_lane_title": "АĐģĐĩŅ ĐĄĐŋĐžĐŗĐ°Đ´Ņ–Đ˛ {title}", "menu": "МĐĩĐŊŅŽ", @@ -1218,8 +1220,9 @@ "missing": "Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", "model": "МодĐĩĐģҌ", "month": "ĐœŅ–ŅŅŅ†ŅŒ", - "monthly_title_text_date_format": "MMMM y", "more": "Đ‘Ņ–ĐģҌ҈Đĩ", + "moved_to_archive": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Ņ–Đ˛}} в Đ°Ņ€Ņ…Ņ–Đ˛", + "moved_to_library": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Ņ–Đ˛}} в ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃", "moved_to_trash": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž Đ´Đž ҁĐŧŅ–Ņ‚ĐŊиĐēа", "multiselect_grid_edit_date_time_err_read_only": "НĐĩĐŧĐžĐļĐģивО Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "multiselect_grid_edit_gps_err_read_only": "НĐĩĐŧĐžĐļĐģивО Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", @@ -1234,6 +1237,7 @@ "new_api_key": "Новий ĐēĐģŅŽŅ‡ API", "new_password": "Новий ĐŋĐ°Ņ€ĐžĐģҌ", "new_person": "Нова ĐģŅŽĐ´Đ¸ĐŊа", + "new_pin_code": "Новий PIN-ĐēОд", "new_user_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž ĐŊĐžĐ˛ĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ НОВА Đ’Đ•Đ ĐĄĐ†Đ¯", "newest_first": "ĐĄĐŋĐžŅ‡Đ°Ņ‚Đē҃ ĐŊĐžĐ˛Ņ–", @@ -1252,6 +1256,8 @@ "no_favorites_message": "Đ”ĐžĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ ҃ĐģŅŽĐąĐģĐĩĐŊŅ– Ņ„Đ°ĐšĐģи, Ņ‰ĐžĐą ŅˆĐ˛Đ¸Đ´ĐēĐž СĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚Đ¸ Đ˛Đ°ŅˆŅ– ĐŊаКĐēŅ€Đ°Ņ‰Ņ– ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "no_libraries_message": "ĐĄŅ‚Đ˛ĐžŅ€Ņ–Ņ‚ŅŒ СОвĐŊŅ–ŅˆĐŊŅŽ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš Ņ– Đ˛Ņ–Đ´ĐĩĐž", "no_name": "БĐĩС Ņ–ĐŧĐĩĐŊŅ–", + "no_notifications": "НĐĩĐŧĐ°Ņ” ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ", + "no_people_found": "Đ›ŅŽĐ´ĐĩĐš, Ņ‰Đž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°ŅŽŅ‚ŅŒ СаĐŋĐ¸Ņ‚Ņƒ, ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", "no_places": "ĐœŅ–ŅŅ†ŅŒ ĐŊĐĩĐŧĐ°Ņ”", "no_results": "НĐĩĐŧĐ°Ņ” Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛", "no_results_description": "ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ŅĐ¸ĐŊĐžĐŊŅ–Đŧ айО ĐąŅ–ĐģҌ҈ ĐˇĐ°ĐŗĐ°ĐģҌĐŊĐĩ ĐēĐģŅŽŅ‡ĐžĐ˛Đĩ ҁĐģОвО", @@ -1267,7 +1273,6 @@ "notification_toggle_setting_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐžŅŽ ĐŋĐžŅˆŅ‚ĐžŅŽ", "notifications": "ĐĄĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", "notifications_setting_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅĐŧи", - "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Ņ–Ņ†Ņ–ĐšĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ Immich", "offline": "ĐžŅ„ĐģаКĐŊ", "offline_paths": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ҈ĐģŅŅ…Đ¸", @@ -1282,6 +1287,7 @@ "onboarding_welcome_user": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž, {user}", "online": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊиК", "only_favorites": "Đ›Đ¸ŅˆĐĩ ĐžĐąŅ€Đ°ĐŊŅ–", + "open": "Đ’Ņ–Đ´ĐēŅ€Đ¸Ņ‚Đ¸", "open_in_map_view": "Đ’Ņ–Đ´ĐēŅ€Đ¸Ņ‚Đ¸ ҃ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņ– ĐŧаĐŋи", "open_in_openstreetmap": "Đ’Ņ–Đ´ĐēŅ€Đ¸Ņ‚Đ¸ в OpenStreetMap", "open_the_search_filters": "Đ’Ņ–Đ´ĐēŅ€Đ¸ĐšŅ‚Đĩ ҄ҖĐģŅŒŅ‚Ņ€Đ¸ ĐŋĐžŅˆŅƒĐē҃", @@ -1304,8 +1310,8 @@ "partner_page_no_more_users": "Đ‘Ņ–ĐģҌ҈Đĩ ĐŊĐĩĐŧĐ°Ņ” ĐēĐžĐŗĐž Đ´ĐžĐ´Đ°Ņ‚Đ¸", "partner_page_partner_add_failed": "НĐĩ вдаĐģĐžŅŅ Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "partner_page_select_partner": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", - "partner_page_shared_to_title": "ĐĄĐŋŅ–ĐģҌĐŊĐĩ Ņ–Đˇ ", - "partner_page_stop_sharing_content": "{} Đ˛Ņ‚Ņ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛.", + "partner_page_shared_to_title": "ĐĄĐŋŅ–ĐģҌĐŊĐĩ Ņ–Đˇ", + "partner_page_stop_sharing_content": "{partner} Đ˛Ņ‚Ņ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛.", "partner_sharing": "ĐĄĐŋŅ–ĐģҌĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "partners": "ĐŸĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ¸", "password": "ĐŸĐ°Ņ€ĐžĐģҌ", @@ -1338,9 +1344,9 @@ "permission_onboarding_continue_anyway": "Đ’ŅĐĩ ОдĐŊĐž ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸", "permission_onboarding_get_started": "РОСĐŋĐžŅ‡Đ°Ņ‚Đ¸", "permission_onboarding_go_to_settings": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ", - "permission_onboarding_permission_denied": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐˇĐ°ĐąĐžŅ€ĐžĐŊĐĩĐŊĐž. Айи ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ŅŅ Immich, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ҃ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", + "permission_onboarding_permission_denied": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐˇĐ°ĐąĐžŅ€ĐžĐŊĐĩĐŊĐž. ДĐģŅ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ Immich ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ дОСвОĐģи Đ´Đž \"Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž\" в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", "permission_onboarding_permission_granted": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐŊадаĐŊĐž! Đ’ŅĐĩ ĐŗĐžŅ‚ĐžĐ˛Đž.", - "permission_onboarding_permission_limited": "ОбĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ. Айи дОСвОĐģĐ¸Ņ‚Đ¸ Immich Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Ņ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžŅŽ ĐŗĐ°ĐģĐĩŅ€ĐĩŅ”ŅŽ, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ҃ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ…", + "permission_onboarding_permission_limited": "ОбĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ. Айи дОСвОĐģĐ¸Ņ‚Đ¸ Immich Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Ņ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžŅŽ ĐŗĐ°ĐģĐĩŅ€ĐĩŅ”ŅŽ, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ҃ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", "permission_onboarding_request": "Immich ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒŅ” Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.", "person": "Đ›ŅŽĐ´Đ¸ĐŊа", "person_birthdate": "ĐĐ°Ņ€ĐžĐ´Đ¸Đ˛ŅŅ {date}", @@ -1351,6 +1357,9 @@ "photos_count": "{count, plural, one {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ} few {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—} many {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš} other {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš}}", "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— ĐŧиĐŊ҃ĐģĐ¸Ņ… Ņ€ĐžĐēŅ–Đ˛ ҃ ҆ĐĩĐš Đ´ĐĩĐŊҌ", "pick_a_location": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŧҖҁ҆Đĩ Ņ€ĐžĐˇŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ", + "pin_code_changed_successfully": "PIN-ĐēОд ҃ҁĐŋŅ–ŅˆĐŊĐž СĐŧŅ–ĐŊĐĩĐŊĐž", + "pin_code_reset_successfully": "PIN-ĐēОд ҃ҁĐŋŅ–ŅˆĐŊĐž ҁĐēиĐŊŅƒŅ‚Đž", + "pin_code_setup_successfully": "PIN-ĐēОд ҃ҁĐŋŅ–ŅˆĐŊĐž ĐŊаĐģĐ°ŅˆŅ‚ĐžĐ˛Đ°ĐŊĐž", "place": "ĐœŅ–ŅŅ†Đĩ", "places": "ĐœŅ–ŅŅ†Ņ", "places_count": "{count, plural, one {{count, number} ĐœŅ–ŅŅ†Đĩ} other {{count, number} ĐœŅ–ŅŅ†Ņ}}", @@ -1372,7 +1381,6 @@ "profile_drawer_client_out_of_date_major": "ĐœĐžĐąŅ–ĐģҌĐŊиК Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧаĐļĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_drawer_client_out_of_date_minor": "ĐœĐžĐąŅ–ĐģҌĐŊиК Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧŅ–ĐŊĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_drawer_client_server_up_to_date": "КĐģŅ–Ņ”ĐŊŅ‚ Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€ — аĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ–", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧаĐļĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_drawer_server_out_of_date_minor": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧŅ–ĐŊĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_image_of_user": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ {user}", @@ -1381,7 +1389,7 @@ "public_share": "ĐŸŅƒĐąĐģҖ҇ĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ", "purchase_account_info": "ĐŸŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēа", "purchase_activated_subtitle": "Đ”ŅĐēŅƒŅ”ĐŧĐž Са ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐē҃ Immich Ņ‚Đ° ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŊĐžĐŗĐž СайĐĩСĐŋĐĩ҇ĐĩĐŊĐŊŅ С Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Đ¸Đŧ ĐēОдОĐŧ", - "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛ĐžĐ˛Đ°ĐŊĐž {date, date}", + "purchase_activated_time": "АĐēŅ‚Đ¸Đ˛ĐžĐ˛Đ°ĐŊĐž {date}", "purchase_activated_title": "Đ’Đ°Ņˆ ĐēĐģŅŽŅ‡ ĐąŅƒĐģĐž ҃ҁĐŋŅ–ŅˆĐŊĐž аĐēŅ‚Đ¸Đ˛ĐžĐ˛Đ°ĐŊĐž", "purchase_button_activate": "АĐēŅ‚Đ¸Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", "purchase_button_buy": "ĐšŅƒĐŋĐ¸Ņ‚Đ¸", @@ -1426,6 +1434,8 @@ "recent_searches": "НĐĩŅ‰ĐžĐ´Đ°Đ˛ĐŊŅ– ĐŋĐžŅˆŅƒĐēĐžĐ˛Ņ– СаĐŋĐ¸Ņ‚Đ¸", "recently_added": "НĐĩŅ‰ĐžĐ´Đ°Đ˛ĐŊĐž дОдаĐŊŅ–", "recently_added_page_title": "НĐĩŅ‰ĐžĐ´Đ°Đ˛ĐŊŅ–", + "recently_taken": "НĐĩдавĐŊĐž ĐˇŅ€ĐžĐąĐģĐĩĐŊĐž", + "recently_taken_page_title": "НĐĩдавĐŊĐž ĐˇŅ€ĐžĐąĐģĐĩĐŊŅ–", "refresh": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸", "refresh_encoded_videos": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ СаĐēОдОваĐŊŅ– Đ˛Ņ–Đ´ĐĩĐž", "refresh_faces": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ОйĐģĐ¸Ņ‡Ņ‡Ņ", @@ -1468,6 +1478,7 @@ "reset": "ĐĄĐēидаĐŊĐŊŅ", "reset_password": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "reset_people_visibility": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ видиĐŧŅ–ŅŅ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", + "reset_pin_code": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд", "reset_to_default": "ĐĄĐēидаĐŊĐŊŅ Đ´Đž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ", "resolve_duplicates": "ĐŖŅŅƒĐŊŅƒŅ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", "resolved_all_duplicates": "ĐŖŅŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸ ҃ҁ҃ĐŊŅƒŅ‚Đž", @@ -1538,9 +1549,9 @@ "search_places": "ĐŸĐžŅˆŅƒĐē ĐŧŅ–ŅŅ†ŅŒ", "search_rating": "ĐŸĐžŅˆŅƒĐē Са Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗĐžĐŧ...", "search_result_page_new_search_hint": "Новий ĐŋĐžŅˆŅƒĐē", - "search_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐžŅˆŅƒĐē҃", + "search_settings": "ĐŸĐžŅˆŅƒĐē ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "search_state": "ĐŸĐžŅˆŅƒĐē Ņ€ĐĩĐŗŅ–ĐžĐŊ҃...", - "search_suggestion_list_smart_search_hint_1": "ІĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊиК ĐŋĐžŅˆŅƒĐē ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ, Đ´ĐģŅ ĐŋĐžŅˆŅƒĐē҃ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐ¸Ņ… виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ŅĐ¸ĐŊŅ‚Đ°ĐēŅĐ¸Ņ", + "search_suggestion_list_smart_search_hint_1": "Đ ĐžĐˇŅƒĐŧĐŊиК ĐŋĐžŅˆŅƒĐē ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ, Đ´ĐģŅ ĐŋĐžŅˆŅƒĐē҃ Са ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊиĐŧи виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ŅĐ¸ĐŊŅ‚Đ°ĐēŅĐ¸Ņ. ", "search_suggestion_list_smart_search_hint_2": "m:Đ˛Đ°Ņˆ-ĐŋĐžŅˆŅƒĐēОвиК-Ņ‚ĐĩŅ€ĐŧŅ–ĐŊ", "search_tags": "ĐŸĐžŅˆŅƒĐē Ņ‚ĐĩĐŗŅ–Đ˛...", "search_timezone": "ĐŸĐžŅˆŅƒĐē Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅŅƒ...", @@ -1560,6 +1571,7 @@ "select_keep_all": "ЗаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ Đ˛ŅĐĩ ĐžĐąŅ€Đ°ĐŊĐĩ", "select_library_owner": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ вĐģĐ°ŅĐŊиĐēа ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи", "select_new_face": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐŊОвĐĩ ОйĐģĐ¸Ņ‡Ņ‡Ņ", + "select_person_to_tag": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐģŅŽĐ´Đ¸ĐŊ҃ Đ´ĐģŅ ĐŋОСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ", "select_photos": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ЗĐŊŅ–ĐŧĐēи", "select_trash_all": "ВидаĐģĐ¸Ņ‚Đ¸ Đ˛ŅĐĩ Đ˛Đ¸ĐąŅ€Đ°ĐŊĐĩ", "select_user_for_sharing_page_err_album": "НĐĩ вдаĐģĐžŅŅ ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ", @@ -1582,20 +1594,20 @@ "set_profile_picture": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ", "set_slideshow_to_fullscreen": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҁĐģаКд-ŅˆĐžŅƒ ĐŊа вĐĩҁҌ ĐĩĐēŅ€Đ°ĐŊ", "setting_image_viewer_help": "ПовĐŊĐžĐĩĐēŅ€Đ°ĐŊĐŊиК ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Đ°Ņ‡ ҁĐŋĐžŅ‡Đ°Ņ‚Đē҃ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ Đ´ĐģŅ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ в ĐŊĐ¸ĐˇŅŒĐēŅ–Đš Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ–Đš ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ–, ĐŋĐžŅ‚Ņ–Đŧ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ в СĐŧĐĩĐŊ҈ĐĩĐŊŅ–Đš Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ–Đš ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– Đ˛Ņ–Đ´ĐŊĐžŅĐŊĐž ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ҃ (ŅĐēŅ‰Đž вĐēĐģŅŽŅ‡ĐĩĐŊĐž) Ņ– ĐˇŅ€ĐĩŅˆŅ‚ĐžŅŽ СаваĐŊŅ‚Đ°ĐļŅƒŅ” ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ (ŅĐēŅ‰Đž вĐēĐģŅŽŅ‡ĐĩĐŊĐž).", - "setting_image_viewer_original_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊĐžĐŗĐž ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С ĐŋОвĐŊĐžŅŽ Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊĐžŅŽ ĐˇĐ´Đ°Ņ‚ĐŊŅ–ŅŅ‚ŅŽ (вĐĩĐģиĐēĐĩ!).\nВиĐŧĐēĐŊŅ–Ņ‚ŅŒ, Ņ‰ĐžĐą СĐŧĐĩĐŊŅˆĐ¸Ņ‚Đ¸ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ даĐŊĐ¸Ņ… (ĐŧĐĩŅ€ĐĩĐļŅ– Ņ‚Đ° ĐēĐĩ҈҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ).", + "setting_image_viewer_original_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊĐžĐŗĐž ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С ĐŋОвĐŊĐžŅŽ Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊĐžŅŽ ĐˇĐ´Đ°Ņ‚ĐŊŅ–ŅŅ‚ŅŽ (вĐĩĐģиĐēĐĩ!). ВиĐŧĐēĐŊŅƒŅ‚Đ¸, Ņ‰ĐžĐą СĐŧĐĩĐŊŅˆĐ¸Ņ‚Đ¸ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ даĐŊĐ¸Ņ… (ŅĐē ҇ĐĩŅ€ĐĩС ĐŧĐĩŅ€ĐĩĐļ҃, Ņ‚Đ°Đē Ņ– ĐŊа ĐēĐĩŅˆŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ).", "setting_image_viewer_original_title": "ЗаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊĐĩ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", - "setting_image_viewer_preview_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ ҁĐĩŅ€ĐĩĐ´ĐŊŅŒĐžŅ— Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊĐžŅ— ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ–.\nВиĐŧĐēĐŊŅ–Ņ‚ŅŒ Đ´ĐģŅ ĐąĐĩСĐŋĐžŅĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŗĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ҃ айО виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ĐģĐ¸ŅˆĐĩ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Ņƒ.", + "setting_image_viewer_preview_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ҁĐĩŅ€ĐĩĐ´ĐŊŅŒĐžŅ— Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊĐžŅ— ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ–. ВиĐŧĐēĐŊŅƒŅ‚Đ¸, Ņ‰ĐžĐą СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ айО виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ҂ҖĐģҌĐēи ĐĩҁĐēŅ–Đˇ.", "setting_image_viewer_preview_title": "ЗаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ", "setting_image_viewer_title": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "setting_languages_apply": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸", "setting_languages_subtitle": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŧĐžĐ˛Ņƒ Đ´ĐžĐ´Đ°Ņ‚Đē҃", "setting_languages_title": "Мова", - "setting_notifications_notify_failures_grace_period": "ĐŸĐžĐ˛Ņ–Đ´ĐžĐŧĐ¸Ņ‚Đ¸ ĐŋŅ€Đž ĐŋĐžĐŧиĐģĐēи Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ: {}", - "setting_notifications_notify_hours": "{} ĐŗĐžĐ´Đ¸ĐŊ", + "setting_notifications_notify_failures_grace_period": "ĐŸĐžĐ˛Ņ–Đ´ĐžĐŧĐ¸Ņ‚Đ¸ ĐŋŅ€Đž ĐŋĐžĐŧиĐģĐēи Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ: {duration}", + "setting_notifications_notify_hours": "{count} ĐŗĐžĐ´Đ¸ĐŊ", "setting_notifications_notify_immediately": "ĐŊĐĩĐŗĐ°ĐšĐŊĐž", - "setting_notifications_notify_minutes": "{} Ņ…Đ˛Đ¸ĐģиĐŊ", + "setting_notifications_notify_minutes": "{count} Ņ…Đ˛Đ¸ĐģиĐŊ", "setting_notifications_notify_never": "ĐŊŅ–ĐēĐžĐģи", - "setting_notifications_notify_seconds": "{} ҁĐĩĐē҃ĐŊĐ´", + "setting_notifications_notify_seconds": "{count} ҁĐĩĐē҃ĐŊĐ´", "setting_notifications_single_progress_subtitle": "ДĐĩŅ‚Đ°ĐģҌĐŊа Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž Ņ…Ņ–Đ´ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Đ´ĐģŅ ĐēĐžĐļĐŊĐžĐŗĐž ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņƒ", "setting_notifications_single_progress_title": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ Ņ…Ņ–Đ´ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "setting_notifications_subtitle": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Ņ–Đ˛ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ", @@ -1607,9 +1619,10 @@ "settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "settings_require_restart": "ПĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļŅ‚Đĩ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧ҃ Đ´ĐģŅ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐ˛Đ°ĐŊĐŊŅ Ņ†ŅŒĐžĐŗĐž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "settings_saved": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ СйĐĩŅ€ĐĩĐļĐĩĐŊŅ–", + "setup_pin_code": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ PIN-ĐēОд", "share": "ĐŸĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ", "share_add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи", - "share_assets_selected": "{} ĐžĐąŅ€Đ°ĐŊĐž", + "share_assets_selected": "{count} ĐžĐąŅ€Đ°ĐŊĐž", "share_dialog_preparing": "ĐŸŅ–Đ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", "shared": "ĐĄĐŋŅ–ĐģҌĐŊŅ–", "shared_album_activities_input_disable": "КоĐŧĐĩĐŊŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ виĐŧĐēĐŊĐĩĐŊĐž", @@ -1623,34 +1636,33 @@ "shared_by_user": "ĐĄĐŋŅ–ĐģҌĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ С {user}", "shared_by_you": "Ви ĐŋĐžĐ´Ņ–ĐģиĐģĐ¸ŅŅŒ", "shared_from_partner": "Đ¤ĐžŅ‚Đž Đ˛Ņ–Đ´ {partner}", - "shared_intent_upload_button_progress_text": "{} / {} ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž", + "shared_intent_upload_button_progress_text": "{current} / {total} ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž", "shared_link_app_bar_title": "ĐĄĐŋŅ–ĐģҌĐŊŅ– ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "shared_link_clipboard_copied_massage": "ĐĄĐēĐžĐŋŅ–ĐšĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧŅ–ĐŊ҃", - "shared_link_clipboard_text": "ĐŸĐžŅĐ¸ĐģаĐŊĐŊŅ: {}\nĐŸĐ°Ņ€ĐžĐģҌ: {}", + "shared_link_clipboard_text": "ĐŸĐžŅĐ¸ĐģаĐŊĐŊŅ: {link}\nĐŸĐ°Ņ€ĐžĐģҌ: {password}", "shared_link_create_error": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "shared_link_edit_description_hint": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐžĐŋĐ¸Ņ Đ´ĐģŅ ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ҃", "shared_link_edit_expire_after_option_day": "1 Đ´ĐĩĐŊҌ", - "shared_link_edit_expire_after_option_days": "{} Đ´ĐŊŅ–Đ˛", + "shared_link_edit_expire_after_option_days": "{count} Đ´ĐŊŅ–Đ˛", "shared_link_edit_expire_after_option_hour": "1 ĐŗĐžĐ´Đ¸ĐŊ҃", - "shared_link_edit_expire_after_option_hours": "{} ĐŗĐžĐ´Đ¸ĐŊ", + "shared_link_edit_expire_after_option_hours": "{count} ĐŗĐžĐ´Đ¸ĐŊ", "shared_link_edit_expire_after_option_minute": "1 Ņ…Đ˛Đ¸ĐģиĐŊ҃", - "shared_link_edit_expire_after_option_minutes": "{} Ņ…Đ˛Đ¸ĐģиĐŊ", - "shared_link_edit_expire_after_option_months": "{} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", - "shared_link_edit_expire_after_option_year": "{} Ņ€ĐžĐēŅ–Đ˛", + "shared_link_edit_expire_after_option_minutes": "{count} Ņ…Đ˛Đ¸ĐģиĐŊ", + "shared_link_edit_expire_after_option_months": "{count} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", + "shared_link_edit_expire_after_option_year": "{count} Ņ€ĐžĐēŅ–Đ˛", "shared_link_edit_password_hint": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ҃", "shared_link_edit_submit_button": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "shared_link_error_server_url_fetch": "НĐĩĐŧĐžĐļĐģивО СаĐŋĐ¸Ņ‚Đ°Ņ‚Đ¸ URL Ņ–Đˇ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "shared_link_expires_day": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} Đ´ĐĩĐŊҌ", - "shared_link_expires_days": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} Đ´ĐŊŅ–Đ˛", - "shared_link_expires_hour": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} ĐŗĐžĐ´Đ¸ĐŊ҃", - "shared_link_expires_hours": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} ĐŗĐžĐ´Đ¸ĐŊ", - "shared_link_expires_minute": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} Ņ…Đ˛Đ¸ĐģиĐŊ҃", - "shared_link_expires_minutes": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} Ņ…Đ˛Đ¸ĐģиĐŊ", + "shared_link_expires_day": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} Đ´ĐĩĐŊҌ", + "shared_link_expires_days": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} Đ´ĐŊŅ–Đ˛", + "shared_link_expires_hour": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} ĐŗĐžĐ´Đ¸ĐŊ҃", + "shared_link_expires_hours": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} ĐŗĐžĐ´Đ¸ĐŊ", + "shared_link_expires_minute": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} Ņ…Đ˛Đ¸ĐģиĐŊ҃", + "shared_link_expires_minutes": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} Ņ…Đ˛Đ¸ĐģиĐŊ", "shared_link_expires_never": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ∞", - "shared_link_expires_second": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} ҁĐĩĐē҃ĐŊĐ´Ņƒ", - "shared_link_expires_seconds": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {} ҁĐĩĐē҃ĐŊĐ´", + "shared_link_expires_second": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} ҁĐĩĐē҃ĐŊĐ´Ņƒ", + "shared_link_expires_seconds": "ЗаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС {count} ҁĐĩĐē҃ĐŊĐ´", "shared_link_individual_shared": "ІĐŊĐ´Đ¸Đ˛Ņ–Đ´ŅƒĐ°ĐģҌĐŊиК ҁĐŋŅ–ĐģҌĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊиĐŧи ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧи", "shared_link_options": "ОĐŋ҆Җҗ ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… ĐŋĐžŅĐ¸ĐģаĐŊҌ", "shared_links": "ĐĄĐŋŅ–ĐģҌĐŊŅ– ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", @@ -1749,7 +1761,7 @@ "theme_selection": "Đ’Đ¸ĐąŅ–Ņ€ Ņ‚ĐĩĐŧи", "theme_selection_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ˛ŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚Đ¸ Ņ‚ĐĩĐŧ҃ ĐŊа ŅĐ˛Ņ–Ņ‚Đģ҃ айО Ņ‚ĐĩĐŧĐŊ҃ СаĐģĐĩĐļĐŊĐž Đ˛Ņ–Đ´ ŅĐ¸ŅŅ‚ĐĩĐŧĐŊĐ¸Ņ… ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ Đ˛Đ°ŅˆĐžĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", "theme_setting_asset_list_storage_indicator_title": "ПоĐēĐ°ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋŅ–ĐēŅ‚ĐžĐŗŅ€Đ°Đŧ҃ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° ĐŊа ĐŋĐģĐ¸Ņ‚ĐēĐ°Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", - "theme_setting_asset_list_tiles_per_row_title": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ҃ Ņ€ŅĐ´Đē҃ ({})", + "theme_setting_asset_list_tiles_per_row_title": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ҃ Ņ€ŅĐ´Đē҃ ({count})", "theme_setting_colorful_interface_subtitle": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸ ĐžŅĐŊОвĐŊиК ĐēĐžĐģŅ–Ņ€ ĐŊа ĐŋОвĐĩҀ҅ĐŊŅŽ Ņ„ĐžĐŊ҃.", "theme_setting_colorful_interface_title": "Đ‘Đ°Ņ€Đ˛Đ¸ŅŅ‚Đ¸Đš Ņ–ĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ", "theme_setting_image_viewer_quality_subtitle": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐēĐžŅŅ‚Ņ– ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ ĐŋОвĐŊĐžĐĩĐēŅ€Đ°ĐŊĐŊĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ", @@ -1783,14 +1795,16 @@ "trash_emptied": "ĐšĐžŅˆĐ¸Đē ĐžŅ‡Đ¸Ņ‰ĐĩĐŊиК", "trash_no_results_message": "ĐĸŅƒŅ‚ С'ŅĐ˛ĐģŅŅ‚Đ¸ĐŧŅƒŅ‚ŅŒŅŅ видаĐģĐĩĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.", "trash_page_delete_all": "ВидаĐģĐ¸Ņ‚Đ¸ ŅƒŅŅ–", - "trash_page_empty_trash_dialog_content": "БаĐļĐ°Ņ”Ņ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Đ˛Đ°ŅˆŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ в ĐēĐžŅˆĐ¸Đē҃? ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´Đĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž С Immich.", - "trash_page_info": "ПоĐŧҖ҉ĐĩĐŊŅ– ҃ ĐēĐžŅˆĐ¸Đē ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´Đĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž ҇ĐĩŅ€ĐĩС {} Đ´ĐŊŅ–Đ˛", + "trash_page_empty_trash_dialog_content": "Ви Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐžŅˆĐ¸Đē? ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С Immich", + "trash_page_info": "ПоĐŧҖ҉ĐĩĐŊŅ– ҃ ĐēĐžŅˆĐ¸Đē ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´Đĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž ҇ĐĩŅ€ĐĩС {days} Đ´ĐŊŅ–Đ˛", "trash_page_no_assets": "Đ’Ņ–Đ´Đ´Đ°ĐģĐĩĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", "trash_page_restore_all": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅƒŅŅ–", "trash_page_select_assets_btn": "Đ’Đ¸ĐąŅ€Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", - "trash_page_title": "ĐšĐžŅˆĐ¸Đē ({})", + "trash_page_title": "ĐšĐžŅˆĐ¸Đē ({count})", "trashed_items_will_be_permanently_deleted_after": "ВидаĐģĐĩĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ–} many {# Đ´ĐŊŅ–Đ˛} other {# Đ´ĐŊŅ–Đ˛}}.", "type": "ĐĸиĐŋ", + "unable_to_change_pin_code": "НĐĩĐŧĐžĐļĐģивО СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ PIN-ĐēОд", + "unable_to_setup_pin_code": "НĐĩĐŧĐžĐļĐģивО ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ PIN-ĐēОд", "unarchive": "Đ ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", "unarchived_count": "{count, plural, other {ПовĐĩŅ€ĐŊŅƒŅ‚Đž С Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ #}}", "unfavorite": "ВидаĐģĐ¸Ņ‚Đ¸ С ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ…", @@ -1826,15 +1840,16 @@ "upload_status_errors": "ПоĐŧиĐģĐēи", "upload_status_uploaded": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž", "upload_success": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐĩ. ОĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ŅŅ‚ĐžŅ€Ņ–ĐŊĐē҃, Ņ‰ĐžĐą ĐŋĐžĐąĐ°Ņ‡Đ¸Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸.", - "upload_to_immich": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ в Immich ({})", + "upload_to_immich": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ в Immich ({count})", "uploading": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", - "url": "URL", "usage": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "use_current_connection": "виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžŅ‚ĐžŅ‡ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ", "use_custom_date_range": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēиК Đ´Ņ–Đ°ĐŋаСОĐŊ Đ´Đ°Ņ‚", "user": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡", "user_id": "ID ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "user_liked": "{user} вĐŋОдОйав {type, select, photo {҆Đĩ Ņ„ĐžŅ‚Đž} video {҆Đĩ Đ˛Ņ–Đ´ĐĩĐž} asset {҆ĐĩĐš Ņ€ĐĩŅŅƒŅ€Ņ} other {҆Đĩ}}", + "user_pin_code_settings": "PIN-ĐēОд", + "user_pin_code_settings_description": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ŅĐ˛ĐžŅ—Đŧ PIN-ĐēОдОĐŧ", "user_purchase_settings": "ĐŸŅ€Đ¸Đ´ĐąĐ°Ņ‚Đ¸", "user_purchase_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ°ŅˆĐžŅŽ ĐŋĐžĐē҃ĐŋĐēĐžŅŽ", "user_role_set": "ĐŸŅ€Đ¸ĐˇĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ {user} ĐŊа Ņ€ĐžĐģҌ {role}", @@ -1851,9 +1866,9 @@ "version_announcement_closing": "ĐĸĐ˛Ņ–Đš Đ´Ņ€ŅƒĐŗ, АĐģĐĩĐēҁ", "version_announcement_message": "ĐŸŅ€Đ¸Đ˛Ņ–Ņ‚! Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊОва вĐĩŅ€ŅŅ–Ņ Immich. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐŋŅ€Đ¸Đ´Ņ–ĐģŅ–Ņ‚ŅŒ Ņ‚Ņ€ĐžŅ…Đ¸ Ņ‡Đ°ŅŅƒ Đ´ĐģŅ ОСĐŊаКОĐŧĐģĐĩĐŊĐŊŅ С ĐŋŅ€Đ¸ĐŧŅ–Ņ‚ĐēаĐŧи Đ´Đž виĐŋ҃ҁĐē҃, Ņ‰ĐžĐą ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°Ņ‚Đ¸ŅŅ, Ņ‰Đž Đ˛Đ°ŅˆĐ° ŅƒŅŅ‚Đ°ĐŊОвĐēа ĐžĐŊОвĐģĐĩĐŊа Ņ– ҃ĐŊиĐēĐŊŅƒŅ‚Đ¸ ĐŧĐžĐļĐģĐ¸Đ˛Đ¸Ņ… ĐŋĐžĐŧиĐģĐžĐē ҃ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ…, ĐžŅĐžĐąĐģивО ŅĐēŅ‰Đž ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ WatchTower айО ĐąŅƒĐ´ŅŒ-ŅĐēиК Ņ–ĐŊŅˆĐ¸Đš ĐŧĐĩŅ…Đ°ĐŊŅ–ĐˇĐŧ, ŅĐēиК Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐžĐŊОвĐģŅŽŅ” Đ˛Đ°Ņˆ ĐĩĐēСĐĩĐŧĐŋĐģŅŅ€ Immich.", "version_announcement_overlay_release_notes": "ĐŋŅ€Đ¸ĐŧŅ–Ņ‚Đēи Đ´Đž виĐŋ҃ҁĐē҃", - "version_announcement_overlay_text_1": "Đ’Ņ–Ņ‚Đ°Ņ”ĐŧĐž, Ņ” ĐŊОвиК виĐŋ҃ҁĐē ", + "version_announcement_overlay_text_1": "Đ’Ņ–Ņ‚Đ°Ņ”ĐŧĐž, Ņ” ĐŊОвиК виĐŋ҃ҁĐē", "version_announcement_overlay_text_2": "СĐŊĐ°ĐšĐ´Ņ–Ņ‚ŅŒ Ņ…Đ˛Đ¸ĐģҌĐē҃ ĐŊĐ°Đ˛Ņ–Đ´Đ°Ņ‚Đ¸ŅŅ ĐŊа ", - "version_announcement_overlay_text_3": "Ņ– ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž Đ˛Đ°ŅˆŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ docker-compose Ņ‚Đ° .env ĐžĐŊОвĐģĐĩĐŊŅ–, айи СаĐŋĐžĐąŅ–ĐŗŅ‚Đ¸ ĐąŅƒĐ´ŅŒ-ŅĐēŅ–Đš ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊŅ–Đš ĐēĐžĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ–Ņ—, ĐžŅĐžĐąĐģивО, ŅĐēŅ‰Đž ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ WatchTower айО Ņ–ĐŊŅˆĐ¸Đš ĐŧĐĩŅ…Đ°ĐŊŅ–ĐˇĐŧ, Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐ¸Ņ… ĐžĐŊОвĐģĐĩĐŊҌ Đ˛Đ°ŅˆĐžŅ— ҁĐĩŅ€Đ˛ĐĩŅ€ĐŊĐžŅ— Ņ‡Đ°ŅŅ‚Đ¸ĐŊи.", + "version_announcement_overlay_text_3": " Ņ– ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž Đ˛Đ°ŅˆŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ docker-compose Ņ‚Đ° .env ĐžĐŊОвĐģĐĩĐŊŅ–, айи СаĐŋĐžĐąŅ–ĐŗŅ‚Đ¸ ĐąŅƒĐ´ŅŒ-ŅĐēŅ–Đš ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊŅ–Đš ĐēĐžĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ–Ņ—, ĐžŅĐžĐąĐģивО, ŅĐēŅ‰Đž ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ WatchTower айО Ņ–ĐŊŅˆĐ¸Đš ĐŧĐĩŅ…Đ°ĐŊŅ–ĐˇĐŧ, Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐ¸Ņ… ĐžĐŊОвĐģĐĩĐŊҌ Đ˛Đ°ŅˆĐžŅ— ҁĐĩŅ€Đ˛ĐĩŅ€ĐŊĐžŅ— Ņ‡Đ°ŅŅ‚Đ¸ĐŊи.", "version_announcement_overlay_title": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊа ĐŊОва вĐĩŅ€ŅŅ–Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° 🎉", "version_history": "Đ†ŅŅ‚ĐžŅ€Ņ–Ņ вĐĩŅ€ŅŅ–Đš", "version_history_item": "Đ’ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž {version} {date}", @@ -1883,11 +1898,11 @@ "week": "ĐĸиĐļĐ´ĐĩĐŊҌ", "welcome": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž", "welcome_to_immich": "Đ›Đ°ŅĐēавО ĐŋŅ€ĐžŅĐ¸ĐŧĐž Đ´Đž Immich", - "wifi_name": "Назва WiFi", + "wifi_name": "Назва Wi-Fi", "year": "Đ Ņ–Đē", "years_ago": "{years, plural, one {# ҀҖĐē} few {# Ņ€ĐžĐēи} many {# Ņ€ĐžĐēŅ–Đ˛} other {# Ņ€ĐžĐēŅ–Đ˛}} Ņ‚ĐžĐŧ҃", "yes": "ĐĸаĐē", "you_dont_have_any_shared_links": "ĐŖ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… ĐŋĐžŅĐ¸ĐģаĐŊҌ", - "your_wifi_name": "Đ’Đ°ŅˆĐ° ĐŊаСва WiFi", + "your_wifi_name": "Назва Đ˛Đ°ŅˆĐžŅ— Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", "zoom_image": "Đ—ĐąŅ–ĐģŅŒŅˆĐ¸Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ" } diff --git a/i18n/ur.json b/i18n/ur.json index f68a3fe2ae..d34cfa759d 100644 --- a/i18n/ur.json +++ b/i18n/ur.json @@ -2,19 +2,70 @@ "about": "Ų…ØĒØšŲ„Ų‚", "account": "Ø§ÚŠØ§Ø¤Ų†Ųš", "account_settings": "Ø§ÚŠØ§Ø¤Ų†Ųš ÚŠÛŒ ØĒØąØĒیباØĒ", + "acknowledge": "ØĒØŗŲ„ÛŒŲ… ÚŠØąŲ†Ø§", "action": "ØšŲ…Ų„", + "action_common_update": "Ø§Ųž ÚˆÛŒŲš", "actions": "Ø§ØšŲ…Ø§Ų„", "active": "ŲØšØ§Ų„", "activity": "ØŗØąÚ¯ØąŲ…ÛŒ", + "activity_changed": "ØŗØąÚ¯ØąŲ…ÛŒ {enabled, select, true {ŲØšØ§Ų„ ہے} other {ØēÛŒØą ŲØšØ§Ų„ ہے}}", "add": "Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_a_description": "ØĒ؁ØĩÛŒŲ„ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_a_location": "Ų…Ų‚Ø§Ų… Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_a_name": "Ų†Ø§Ų… Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_a_title": "ØšŲ†ŲˆØ§Ų† Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_a_location": "ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", + "add_a_name": "Ų†Ø§Ų… ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", + "add_a_title": "ØšŲ†ŲˆØ§Ų† ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", + "add_endpoint": "Ø§ÛŒŲ†Úˆ ŲžŲˆØ§ØĻŲ†Ųš Ø¯ØąØŦ ÚŠØąÛŒÚē", "add_exclusion_pattern": "ØŽØ§ØąØŦ ÚŠØąŲ†Û’ ڊا Ų†Ų…ŲˆŲ†Û Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_import_path": "Ø¯ØąØĸŲ…Ø¯ ڊا ØąØ§ØŗØĒہ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_location": "Ų…Ų‚Ø§Ų… Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_location": "ØŦگہ Ø¯ØąØŦ ÚŠØąÛŒÚē", "add_more_users": "Ų…Ø˛ÛŒØ¯ ØĩØ§ØąŲÛŒŲ† Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_partner": "ØŗØ§ØĒÚžÛŒ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_path": "ØąØ§ØŗØĒہ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē" + "add_path": "ØąØ§ØŗØĒہ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_photos": "ØĒØĩØ§ŲˆÛŒØą Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_to": "Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚēâ€Ļ", + "add_to_album": "Ø§Ų„Ø¨Ų… Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_to_album_bottom_sheet_added": "{album} Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąØ¯ÛŒØ§Ú¯ÛŒØ§", + "add_to_album_bottom_sheet_already_exists": "ŲžÛŲ„Û’ ہی {album} Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ہے", + "add_to_shared_album": "Ų…Ø´ØĒØąÚŠÛ Ø§Ų„Ø¨Ų… Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_url": "URL Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "added_to_archive": "Ų…Ø­ŲŲˆØ¸ شدہ Ø¯ØŗØĒØ§ŲˆÛŒØ˛Ø§ØĒ Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ڊیا گیا", + "added_to_favorites": "ŲžØŗŲ†Ø¯ÛŒØ¯Û Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ڊیا گیا", + "added_to_favorites_count": "ŲžØŗŲ†Ø¯ÛŒØ¯Û Ų…ÛŒÚē {count, number} Ø´Ø§Ų…Ų„ ڊیے Ú¯ØĻے", + "admin": { + "add_exclusion_pattern_description": "Ø§ØŽØąØ§ØŦ ÚŠÛ’ Ų†Ų…ŲˆŲ†Û’ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē۔ *، **، Ø§ŲˆØą ? ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąØĒے ÛŲˆØĻے Globbing ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ڊیا ØŦا ØŗÚŠØĒا ہے۔ \"RAW\" Ų†Ø§Ų…ÛŒ ÚŠØŗÛŒ بڞی ڈاØĻØąÛŒÚŠŲšØąÛŒ Ų…ÛŒÚē ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/Raw/**\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ \".tif\" ØŗÛ’ ØŽØĒŲ… ÛŲˆŲ†Û’ ŲˆØ§Ų„ÛŒ ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/*.tif\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ ÚŠØŗÛŒ Ų…ØˇŲ„Ų‚ ØąØ§ØŗØĒے ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"/path/to/ignore/**\" ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔", + "asset_offline_description": "Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ڊا یہ Ø¨ÛŒØąŲˆŲ†ÛŒ اØĢاØĢہ اب ÚˆØŗÚŠ ŲžØą Ų†ÛÛŒÚē Ų…Ų„Ø§ Ø§ŲˆØą Ø§ØŗÛ’ ÚŠŲˆÚ‘Û’ Ø¯Ø§Ų† Ų…ÛŒÚē ÚˆØ§Ų„ دیا گیا ہے۔ Ø§Ú¯Øą ŲØ§ØĻŲ„ Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠÛ’ Ø§Ų†Ø¯Øą Ų…Ų†ØĒŲ‚Ų„ ÚŠÛŒ Ú¯ØĻی ØĒڞی، ØĒ؈ Ų†ØĻے Ų…ØĒØšŲ„Ų‚Û اØĢاØĢے ÚŠÛ’ Ų„ÛŒÛ’ Ø§ŲžŲ†ÛŒ ŲšØ§ØĻŲ… Ų„Ø§ØĻŲ† چیڊ ÚŠØąÛŒÚē۔ Ø§Øŗ اØĢاØĢے ÚŠŲˆ Ø¨Ø­Ø§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ Ø¨ØąØ§Û ÚŠØąŲ… ÛŒŲ‚ÛŒŲ†ÛŒ Ø¨Ų†Ø§ØĻیÚē ڊہ Ų†ÛŒÚ†Û’ دیے Ú¯ØĻے ŲØ§ØĻŲ„ ÚŠÛ’ ØąØ§ØŗØĒے ØĒÚŠ Immich ÚŠŲˆ ØąØŗØ§ØĻی حاØĩŲ„ ہے Ø§ŲˆØą Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø§ØŗÚŠÛŒŲ† ÚŠØąÛŒÚē۔", + "authentication_settings_disable_all": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ Ų„Ø§Ú¯ Ø§Ų† ÚŠÛ’ ØĒŲ…Ø§Ų… ØˇØąÛŒŲ‚ŲˆÚē ÚŠŲˆ ØēÛŒØą ŲØšØ§Ų„ ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟 Ų„Ø§Ú¯ Ø§Ų† Ų…ÚŠŲ…Ų„ ØˇŲˆØą ŲžØą ØēÛŒØą ŲØšØ§Ų„ ÛŲˆ ØŦاØĻے گا۔", + "check_all": "ØŗØ¨ چیڊ ÚŠØąÛŒÚē", + "cleanup": "ØĩØ§Ų ÚŠØąŲˆ", + "confirm_delete_library": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ {library} Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø­Ø°Ų ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟", + "confirm_email_below": "ØĒØĩØ¯ÛŒŲ‚ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ Ų†ÛŒÚ†Û’ ای Ų…ÛŒŲ„ ŲšØ§ØĻŲž ÚŠØąÛŒÚē {email}", + "image_preview_title": "ŲžÛŒØ´ Ų†Ø¸Ø§ØąÛ", + "image_quality": "Ų…ØšÛŒØ§Øą", + "image_settings": "ØĒØĩŲˆÛŒØą ÚŠÛŒ ØĒØąØĒیباØĒ" + }, + "change_pin_code": "ŲžŲ† ÚŠŲˆÚˆ ØĒØ¨Ø¯ÛŒŲ„ ÚŠØąÛŒÚē", + "confirm_new_pin_code": "Ų†ØĻے ŲžŲ† ÚŠŲˆÚˆ ÚŠÛŒ ØĒØĩØ¯ÛŒŲ‚ ÚŠØąÛŒÚē", + "current_pin_code": "Ų…ŲˆØŦŲˆØ¯Û ŲžŲ† ÚŠŲˆÚˆ", + "new_pin_code": "Ų†ÛŒØ§ ŲžŲ† ÚŠŲˆÚˆ", + "pin_code_changed_successfully": "ŲžŲ† ÚŠŲˆÚˆ ÚŠŲˆ ÚŠØ§Ų…ÛŒØ§Ø¨ÛŒ ØŗÛ’ ØĒØ¨Ø¯ÛŒŲ„ ÚŠØą دیا گیا", + "pin_code_reset_successfully": "ŲžŲ† ÚŠŲˆÚˆ ÚŠØ§Ų…ÛŒØ§Ø¨ÛŒ ÚŠÛ’ ØŗØ§ØĒÚž ØąÛŒ ØŗÛŒŲš ÛŲˆ گیا", + "pin_code_setup_successfully": "ŲžŲ† ÚŠŲˆÚˆ ÚŠØ§Ų…ÛŒØ§Ø¨ÛŒ ÚŠÛ’ ØŗØ§ØĒÚž ØŗÛŒŲš Ø§Ųž ÛŲˆ گیا", + "reset_pin_code": "ŲžŲ† ÚŠŲˆÚˆ Ø¯ŲˆØ¨Ø§ØąÛ ØĒØąØĒیب دیÚē", + "setup_pin_code": "ایڊ Ų†ÛŒØ§ ŲžŲ† ÚŠŲˆÚˆ ØĒØąØĒیب دیÚē", + "sunrise_on_the_beach": "ØŗØ§Ø­Ų„ ØŗŲ…Ų†Ø¯Øą ŲžØą ØˇŲ„ŲˆØš Øĸ؁ØĒاب", + "unable_to_change_pin_code": "ŲžŲ† ÚŠŲˆÚˆ ØĒØ¨Ø¯ÛŒŲ„ ÚŠØąŲ†Û’ ØŗÛ’ Ų‚Ø§ØĩØą", + "unable_to_setup_pin_code": "ŲžŲ† ÚŠŲˆÚˆ ØĒØąØĒیب ÚŠØąŲ†Û’ ØŗÛ’ Ų‚Ø§ØĩØą", + "user_pin_code_settings": "ŲžŲ† ÚŠŲˆÚˆ", + "user_pin_code_settings_description": "Ø§ŲžŲ†Û’ ŲžŲ† ÚŠŲˆÚˆ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē", + "user_purchase_settings": "ØŽØąÛŒØ¯Ø§ØąÛŒ", + "user_purchase_settings_description": "Ø§ŲžŲ†ÛŒ ØŽØąÛŒØ¯Ø§ØąÛŒ ڊا Ø§Ų†ØĒØ¸Ø§Ų… ÚŠØąÛŒÚē", + "version_announcement_closing": "ØĸŲž ڊا Ø¯ŲˆØŗØĒ، Ø§ÛŒŲ„ÚŠØŗ", + "video": "ŲˆÛŒÚˆÛŒŲˆ", + "videos": "ŲˆÛŒÚˆÛŒŲˆØ˛", + "view": "دیڊڞیÚē", + "view_all": "ØŗØ¨ دیڊڞیÚē", + "waiting": "Ø§Ų†ØĒØ¸Ø§Øą", + "week": "ÛŲØĒہ", + "year": "ØŗØ§Ų„", + "zoom_image": "Ø˛ŲˆŲ… ØĒØĩŲˆÛŒØą" } diff --git a/i18n/vi.json b/i18n/vi.json index fdcea7772c..d952bc9197 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -1,5 +1,5 @@ { - "about": "Làm máģ›i", + "about": "Giáģ›i thiáģ‡u", "account": "Tài khoáēŖn", "account_settings": "Cài đáēˇt tài khoáēŖn", "acknowledge": "Ghi nháē­n", @@ -22,11 +22,12 @@ "add_partner": "ThÃĒm ngưáģi thÃĸn", "add_path": "ThÃĒm đưáģng dáēĢn", "add_photos": "ThÃĒm áēŖnh", - "add_to": "ThÃĒm vào...", + "add_to": "ThÃĒm vàoâ€Ļ", "add_to_album": "ThÃĒm vào album", "add_to_album_bottom_sheet_added": "ThÃĒm vào {album}", "add_to_album_bottom_sheet_already_exists": "ÄÃŖ cÃŗ sáēĩn trong {album}", "add_to_shared_album": "ThÃĒm vào album chia sáēģ", + "add_url": "ThÃĒm URL", "added_to_archive": "ÄÃŖ thÃĒm vào Kho lưu tráģ¯", "added_to_favorites": "ÄÃŖ thÃĒm vào MáģĨc yÃĒu thích", "added_to_favorites_count": "ÄÃŖ thÃĒm {count, number} vào MáģĨc yÃĒu thích", @@ -38,8 +39,8 @@ "authentication_settings_disable_all": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n vô hiáģ‡u hoÃĄ táēĨt cáēŖ cÃĄc phÆ°ÆĄng tháģŠc đăng nháē­p? Đăng nháē­p sáēŊ báģ‹ vô hiáģ‡u hÃŗa hoàn toàn.", "authentication_settings_reenable": "Đáģƒ báē­t láēĄi, dÚng Láģ‡nh MÃĄy cháģ§.", "background_task_job": "CÃĄc tÃĄc váģĨ náģn", - "backup_database": "Sao lưu dáģ¯ liáģ‡u", - "backup_database_enable_description": "Kích hoáēĄt Sao lưu dáģ¯ liáģ‡u", + "backup_database": "TáēĄo báēŖn sao lưu cÆĄ sáģŸ dáģ¯ liáģ‡u", + "backup_database_enable_description": "Báē­t sao lưu cÆĄ sáģŸ dáģ¯ liáģ‡u", "backup_keep_last_amount": "Sáģ‘ lưáģŖng cÃĄc báēŖn Sao lưu đưáģŖc giáģ¯ láēĄi", "backup_settings": "Cài đáēˇt sao lưu", "backup_settings_description": "QuáēŖn lÃŊ cÃĄc thông sáģ‘ cài đáēˇt cáģ§a Sao lưu dáģ¯ liáģ‡u", @@ -188,20 +189,13 @@ "oauth_auto_register": "Táģą Ä‘áģ™ng đăng kÃŊ", "oauth_auto_register_description": "Táģą Ä‘áģ™ng đăng kÃŊ ngưáģi dÚng máģ›i sau khi đăng nháē­p váģ›i OAuth", "oauth_button_text": "Náģ™i dung văn báēŖn nÃēt báēĨm", - "oauth_client_id": "MÃŖ áģŠng dáģĨng khÃĄch OAuth", - "oauth_client_secret": "Máē­t kháēŠu áģŠng dáģĨng khÃĄch OAuth", "oauth_enable_description": "Đăng nháē­p váģ›i OAuth", - "oauth_issuer_url": "Đáģ‹a cháģ‰ nhà cung cáēĨp OAuth", "oauth_mobile_redirect_uri": "URI chuyáģƒn hưáģ›ng trÃĒn thiáēŋt báģ‹ di đáģ™ng", "oauth_mobile_redirect_uri_override": "Ghi đè URI chuyáģƒn hưáģ›ng cho thiáēŋt báģ‹ di đáģ™ng", "oauth_mobile_redirect_uri_override_description": "Báē­t khi nhà cung cáēĨp OAuth không cho phÊp URI di đáģ™ng, như '{callback}'", - "oauth_profile_signing_algorithm": "Thuáē­t toÃĄn kÃŊ vào háģ“ sÆĄ ngưáģi dÚng", - "oauth_profile_signing_algorithm_description": "Thuáē­t toÃĄn đưáģŖc sáģ­ dáģĨng đáģƒ kÃŊ vào háģ“ sÆĄ ngưáģi dÚng.", - "oauth_scope": "PháēĄm vi", "oauth_settings": "OAuth", "oauth_settings_description": "QuáēŖn lÃŊ cài đáēˇt đăng nháē­p OAuth", "oauth_settings_more_details": "Đáģƒ biáēŋt thÃĒm chi tiáēŋt váģ tính năng này, hÃŖy tham kháēŖo tài liáģ‡u.", - "oauth_signing_algorithm": "Thuáē­t toÃĄn kÃŊ", "oauth_storage_label_claim": "Claim cho nhÃŖn lưu tráģ¯", "oauth_storage_label_claim_description": "Táģą Ä‘áģ™ng đáēˇt nhÃŖn lưu tráģ¯ cáģ§a ngưáģi dÚng theo giÃĄ tráģ‹ cáģ§a claim này.", "oauth_storage_quota_claim": "Claim cho háēĄn máģŠc lưu tráģ¯", @@ -299,7 +293,7 @@ "transcoding_hardware_acceleration": "Tăng táģ‘c pháē§n cáģŠng", "transcoding_hardware_acceleration_description": "(Tháģ­ nghiáģ‡m) nhanh hÆĄn nhiáģu nhưng sáēŊ cÃŗ cháēĨt lưáģŖng tháēĨp hÆĄn áģŸ cÚng bitrate", "transcoding_hardware_decoding": "GiáēŖi mÃŖ pháē§n cáģŠng", - "transcoding_hardware_decoding_setting_description": "Cho phÊp tăng táģ‘c đáē§u cuáģ‘i thay vÃŦ cháģ‰ tăng táģ‘c mÃŖ hÃŗa. CÃŗ tháģƒ không hoáēĄt đáģ™ng trÃĒn táēĨt cáēŖ video.", + "transcoding_hardware_decoding_setting_description": "Cho phÊp tăng táģ‘c đáē§u cuáģ‘i thay vÃŦ cháģ‰ tăng táģ‘c mÃŖ hÃŗa. CÃŗ tháģƒ không hoáēĄt đáģ™ng váģ›i máģi video.", "transcoding_hevc_codec": "Codec HEVC", "transcoding_max_b_frames": "Sáģ‘ B-frame táģ‘i đa", "transcoding_max_b_frames_description": "GiÃĄ tráģ‹ cao hÆĄn cáēŖi thiáģ‡n hiáģ‡u quáēŖ nÊn, nhưng làm cháē­m mÃŖ hÃŗa. CÃŗ tháģƒ không tÆ°ÆĄng thích váģ›i tăng táģ‘c pháē§n cáģŠng trÃĒn cÃĄc thiáēŋt báģ‹ cÅŠ. GiÃĄ tráģ‹ 0 đáģƒ táē¯t B-frames, trong khi giÃĄ tráģ‹ -1 đáģƒ táģą Ä‘áģ™ng thiáēŋt láē­p giÃĄ tráģ‹ này.", @@ -364,7 +358,7 @@ "admin_password": "Máē­t kháēŠu QuáēŖn tráģ‹ viÃĒn", "administration": "QuáēŖn tráģ‹", "advanced": "NÃĸng cao", - "advanced_settings_log_level_title": "PhÃĸn loáēĄi nháē­t kÃŊ: {}", + "advanced_settings_log_level_title": "PhÃĸn loáēĄi nháē­t kÃŊ: {level}", "advanced_settings_prefer_remote_subtitle": "TrÃĒn máģ™t sáģ‘ thiáēŋt báģ‹, viáģ‡c táēŖi hÃŦnh thu nháģ táģĢ áēŖnh trÃĒn thiáēŋt báģ‹ diáģ…n ra cháē­m. Kích hoáēĄt cài đáēˇt này đáģƒ táēŖi áēŖnh táģĢ mÃĄy cháģ§.", "advanced_settings_prefer_remote_title": "Ưu tiÃĒn áēŖnh táģĢ mÃĄy cháģ§", "advanced_settings_proxy_headers_subtitle": "XÃĄc đáģ‹nh cÃĄc header cáģ§a proxy mà Immich sáēŊ gáģ­i kèm theo máģ—i yÃĒu cáē§u máēĄng.", @@ -393,9 +387,9 @@ "album_remove_user_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa {user} không?", "album_share_no_users": "CÃŗ váēģ như báēĄn Ä‘ÃŖ chia sáēģ album này váģ›i táēĨt cáēŖ ngưáģi dÚng hoáēˇc báēĄn không cÃŗ ngưáģi dÚng nào đáģƒ chia sáēģ.", "album_thumbnail_card_item": "1 máģĨc", - "album_thumbnail_card_items": "{} máģĨc", + "album_thumbnail_card_items": "{count} máģĨc", "album_thumbnail_card_shared": " ¡ Chia sáēģ", - "album_thumbnail_shared_by": "Chia sáēģ báģŸi {}", + "album_thumbnail_shared_by": "Chia sáēģ báģŸi {user}", "album_updated": "ÄÃŖ cáē­p nháē­t album", "album_updated_setting_description": "Nháē­n thông bÃĄo qua email khi máģ™t album chia sáēģ cÃŗ cÃĄc áēŖnh máģ›i", "album_user_left": "ÄÃŖ ráģi kháģi {album}", @@ -432,7 +426,7 @@ "archive": "Lưu tráģ¯", "archive_or_unarchive_photo": "Lưu tráģ¯ hoáēˇc huáģˇ lưu tráģ¯ áēŖnh", "archive_page_no_archived_assets": "Không tÃŦm tháēĨy áēŖnh Ä‘ÃŖ lưu tráģ¯", - "archive_page_title": "Kho lưu tráģ¯ ({})", + "archive_page_title": "Kho lưu tráģ¯ ({count})", "archive_size": "Kích thưáģ›c gÃŗi nÊn", "archive_size_description": "CáēĨu hÃŦnh kích thưáģ›c nÊn cho cÃĄc táē­p tin táēŖi xuáģ‘ng (Ä‘ÆĄn váģ‹ GiB)", "archived": "Lưu tráģ¯", @@ -469,18 +463,18 @@ "assets_added_to_album_count": "ÄÃŖ thÃĒm {count, plural, one {# máģĨc} other {# máģĨc}} vào album", "assets_added_to_name_count": "ÄÃŖ thÃĒm {count, plural, one {# máģĨc} other {# máģĨc}} vào {hasName, select, true {{name}} other {album máģ›i}}", "assets_count": "{count, plural, one {# máģĨc} other {# máģĨc}}", - "assets_deleted_permanently": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {} máģĨc", - "assets_deleted_permanently_from_server": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {} máģĨc kháģi mÃĄy cháģ§ Immich", + "assets_deleted_permanently": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {count} máģĨc", + "assets_deleted_permanently_from_server": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {count} máģĨc kháģi mÃĄy cháģ§ Immich", "assets_moved_to_trash_count": "ÄÃŖ chuyáģƒn {count, plural, one {# máģĨc} other {# máģĨc}} vào thÚng rÃĄc", "assets_permanently_deleted_count": "ÄÃŖ xÃŗa vÄŠnh viáģ…n {count, plural, one {# máģĨc} other {# máģĨc}}", "assets_removed_count": "ÄÃŖ xÃŗa {count, plural, one {# máģĨc} other {# máģĨc}}", - "assets_removed_permanently_from_device": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {} máģĨc kháģi thiáēŋt báģ‹ cáģ§a báēĄn", + "assets_removed_permanently_from_device": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {count} máģĨc kháģi thiáēŋt báģ‹ cáģ§a báēĄn", "assets_restore_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n khôi pháģĨc táēĨt cáēŖ cÃĄc máģĨc Ä‘ÃŖ xÃŗa cáģ§a mÃŦnh không? BáēĄn không tháģƒ hoàn tÃĄc hành đáģ™ng này! Lưu ÃŊ ráēąng không tháģƒ khôi pháģĨc cÃĄc áēŖnh ngoáēĄi tuyáēŋn theo cÃĄch này.", "assets_restored_count": "ÄÃŖ khôi pháģĨc {count, plural, one {# máģĨc} other {# máģĨc}}", - "assets_restored_successfully": "ÄÃŖ khôi pháģĨc {} máģĨc thành công", - "assets_trashed": "ÄÃŖ chuyáģƒn {} máģĨc vào thÚng rÃĄc", + "assets_restored_successfully": "ÄÃŖ khôi pháģĨc {count} máģĨc thành công", + "assets_trashed": "ÄÃŖ chuyáģƒn {count} máģĨc vào thÚng rÃĄc", "assets_trashed_count": "ÄÃŖ chuyáģƒn {count, plural, one {# máģĨc} other {# máģĨc}} vào thÚng rÃĄc", - "assets_trashed_from_server": "ÄÃŖ chuyáģƒn {} máģĨc táģĢ mÃĄy cháģ§ Immich vào thÚng rÃĄc", + "assets_trashed_from_server": "ÄÃŖ chuyáģƒn {count} máģĨc táģĢ mÃĄy cháģ§ Immich vào thÚng rÃĄc", "assets_were_part_of_album_count": "{count, plural, one {MáģĨc Ä‘ÃŖ} other {CÃĄc máģĨc Ä‘ÃŖ}} cÃŗ trong album", "authorized_devices": "Thiáēŋt báģ‹ Ä‘Æ°áģŖc áģ§y quyáģn", "automatic_endpoint_switching_subtitle": "Káēŋt náģ‘i náģ™i báģ™ qua Wi-Fi đưáģŖc cháģ‰ Ä‘áģ‹nh khi káēŋt náģ‘i đưáģŖc và sáģ­ dáģĨng cÃĄc káēŋt náģ‘i thay tháēŋ áģŸ nÆĄi khÃĄc", @@ -489,7 +483,7 @@ "back_close_deselect": "Quay láēĄi, Ä‘Ãŗng, hoáēˇc báģ cháģn", "background_location_permission": "Quyáģn truy cáē­p váģ‹ trí áģŸ náģn", "background_location_permission_content": "Đáģƒ chuyáģƒn đáģ•i máēĄng khi cháēĄy áģŸ cháēŋ đáģ™ náģn, Immich *luôn* pháēŖi cÃŗ quyáģn truy cáē­p váģ‹ trí chính xÃĄc đáģƒ áģŠng dáģĨng cÃŗ tháģƒ Ä‘áģc tÃĒn máēĄng Wi-Fi", - "backup_album_selection_page_albums_device": "Album trÃĒn thiáēŋt báģ‹ ({})", + "backup_album_selection_page_albums_device": "Album trÃĒn thiáēŋt báģ‹ ({count})", "backup_album_selection_page_albums_tap": "NháēĨn đáģƒ cháģn, nháēĨn đÃēp đáģƒ báģ qua", "backup_album_selection_page_assets_scatter": "áēĸnh cÃŗ tháģƒ cÃŗ trong nhiáģu album khÃĄc nhau. Trong quÃĄ trÃŦnh sao lưu, báēĄn cÃŗ tháģƒ cháģn đáģƒ sao lưu táēĨt cáēŖ cÃĄc album hoáēˇc cháģ‰ máģ™t sáģ‘ album nháēĨt đáģ‹nh.", "backup_album_selection_page_select_albums": "Cháģn album", @@ -498,11 +492,11 @@ "backup_all": "TáēĨt cáēŖ", "backup_background_service_backup_failed_message": "Sao lưu áēŖnh tháēĨt báēĄi. Đang tháģ­ láēĄi...", "backup_background_service_connection_failed_message": "Káēŋt náģ‘i táģ›i mÃĄy cháģ§ tháēĨt báēĄi. Đang tháģ­ láēĄi...", - "backup_background_service_current_upload_notification": "Đang táēŖi lÃĒn {}", + "backup_background_service_current_upload_notification": "Đang táēŖi lÃĒn {filename}", "backup_background_service_default_notification": "Đang kiáģƒm tra áēŖnh máģ›i...", "backup_background_service_error_title": "Sao lưu không thành công", "backup_background_service_in_progress_notification": "Đang sao lưu áēŖnh cáģ§a báēĄn...", - "backup_background_service_upload_failure_notification": "TáēŖi lÃĒn tháēĨt báēĄi {}", + "backup_background_service_upload_failure_notification": "TáēŖi lÃĒn tháēĨt báēĄi {filename}", "backup_controller_page_albums": "Album sao lưu", "backup_controller_page_background_app_refresh_disabled_content": "Báē­t làm máģ›i áģŠng dáģĨng trong náģn trong Cài đáēˇt > Cài đáēˇt chung > Làm máģ›i áģŠng dáģĨng trong náģn đáģƒ dÚng sao lưu náģn", "backup_controller_page_background_app_refresh_disabled_title": "Làm máģ›i áģŠng dáģĨng trong náģn báģ‹ vô hiáģ‡u hoÃĄ", @@ -513,7 +507,7 @@ "backup_controller_page_background_battery_info_title": "Tiáēŋt kiáģ‡m pin", "backup_controller_page_background_charging": "Cháģ‰ khi đang sáēĄc", "backup_controller_page_background_configure_error": "CáēĨu hÃŦnh dáģ‹ch váģĨ náģn tháēĨt báēĄi", - "backup_controller_page_background_delay": "TrÃŦ hoÃŖn sao lưu áēŖnh máģ›i: {}", + "backup_controller_page_background_delay": "TrÃŦ hoÃŖn sao lưu áēŖnh máģ›i: {duration}", "backup_controller_page_background_description": "Báē­t dáģ‹ch váģĨ náģn đáģƒ táģą Ä‘áģ™ng sao lưu áēŖnh máģ›i mà không cáē§n máģŸ áģŠng dáģĨng", "backup_controller_page_background_is_off": "Sao lưu táģą Ä‘áģ™ng trong náģn đang táē¯t", "backup_controller_page_background_is_on": "Sao lưu táģą Ä‘áģ™ng trong náģn đang báē­t", @@ -523,12 +517,11 @@ "backup_controller_page_backup": "Sao lưu", "backup_controller_page_backup_selected": "ÄÃŖ cháģn: ", "backup_controller_page_backup_sub": "áēĸnh và video Ä‘ÃŖ sao lưu", - "backup_controller_page_created": "TáēĄo vào: {}", + "backup_controller_page_created": "TáēĄo vào: {date}", "backup_controller_page_desc_backup": "Báē­t sao lưu khi áģŠng dáģĨng hoáēĄt đáģ™ng đáģƒ táģą Ä‘áģ™ng sao lưu áēŖnh máģ›i lÃĒn mÃĄy cháģ§ khi máģŸ áģŠng dáģĨng.", - "backup_controller_page_excluded": "ÄÃŖ báģ qua:", - "backup_controller_page_failed": "TháēĨt báēĄi ({})", - "backup_controller_page_filename": "TÃĒn táģ‡p: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "ÄÃŖ báģ qua: ", + "backup_controller_page_failed": "TháēĨt báēĄi ({count})", + "backup_controller_page_filename": "TÃĒn táģ‡p: {filename} [{size}]", "backup_controller_page_info": "Thông tin sao lưu", "backup_controller_page_none_selected": "Không cÃŗ máģĨc nào đưáģŖc cháģn", "backup_controller_page_remainder": "CÃ˛n láēĄi", @@ -537,7 +530,7 @@ "backup_controller_page_start_backup": "Báē¯t đáē§u sao lưu", "backup_controller_page_status_off": "Sao lưu táģą Ä‘áģ™ng khi áģŠng dáģĨng hoáēĄt đáģ™ng đang táē¯t", "backup_controller_page_status_on": "Sao lưu táģą Ä‘áģ™ng khi áģŠng dáģĨng hoáēĄt đáģ™ng đang báē­t", - "backup_controller_page_storage_format": "ÄÃŖ sáģ­ dáģĨng {} cáģ§a {} ", + "backup_controller_page_storage_format": "ÄÃŖ sáģ­ dáģĨng {used} cáģ§a {total}", "backup_controller_page_to_backup": "CÃĄc album cáē§n đưáģŖc sao lưu", "backup_controller_page_total_sub": "TáēĨt cáēŖ áēŖnh và video không trÚng láē­p táģĢ cÃĄc album đưáģŖc cháģn", "backup_controller_page_turn_off": "Táē¯t sao lưu khi áģŠng dáģĨng hoáēĄt đáģ™ng", @@ -562,21 +555,21 @@ "bulk_keep_duplicates_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n giáģ¯ láēĄi {count, plural, one {# máģĨc trÚng láēˇp} other {# máģĨc trÚng láēˇp}} không? Điáģu này sáēŊ xáģ­ lÃŊ táēĨt cáēŖ cÃĄc nhÃŗm áēŖnh trÚng láēˇp mà không xÃŗa báēĨt káģŗ tháģŠ gÃŦ.", "bulk_trash_duplicates_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n đưa {count, plural, one {# máģĨc trÚng láēˇp} other {# máģĨc trÚng láēˇp}} vào thÚng rÃĄc không? Điáģu này sáēŊ giáģ¯ láēĄi áēŖnh cháēĨt lưáģŖng nháēĨt cáģ§a máģ—i nhÃŗm và đưa táēĨt cáēŖ cÃĄc báēŖn trÚng láēˇp khÃĄc vào thÚng rÃĄc.", "buy": "Mua Immich", - "cache_settings_album_thumbnails": "Trang thư viáģ‡n hÃŦnh thu nháģ ({} áēŖnh)", + "cache_settings_album_thumbnails": "Trang thư viáģ‡n hÃŦnh thu nháģ ({count} áēŖnh)", "cache_settings_clear_cache_button": "XoÃĄ báģ™ nháģ› Ä‘áģ‡m", "cache_settings_clear_cache_button_title": "XÃŗa báģ™ nháģ› Ä‘áģ‡m cáģ§a áģŠng dáģĨng. Điáģu này sáēŊ áēŖnh hưáģŸng đáēŋn hiáģ‡u suáēĨt cáģ§a áģŠng dáģĨng đáēŋn khi báģ™ nháģ› Ä‘áģ‡m đưáģŖc táēĄo láēĄi.", "cache_settings_duplicated_assets_clear_button": "XOÁ", "cache_settings_duplicated_assets_subtitle": "áēĸnh và video không đưáģŖc phÊp hiáģƒn tháģ‹ trÃĒn áģŠng dáģĨng", - "cache_settings_duplicated_assets_title": "áēĸnh báģ‹ trÚng láēˇp ({})", - "cache_settings_image_cache_size": "Kích thưáģ›c báģ™ nháģ› Ä‘áģ‡m áēŖnh ({} áēŖnh)", + "cache_settings_duplicated_assets_title": "áēĸnh báģ‹ trÚng láēˇp ({count})", + "cache_settings_image_cache_size": "Kích thưáģ›c báģ™ nháģ› Ä‘áģ‡m áēŖnh ({count} áēŖnh)", "cache_settings_statistics_album": "Thư viáģ‡n hÃŦnh thu nháģ", - "cache_settings_statistics_assets": "{} áēŖnh ({})", + "cache_settings_statistics_assets": "{count} áēŖnh ({size})", "cache_settings_statistics_full": "áēĸnh đáē§y đáģ§", "cache_settings_statistics_shared": "HÃŦnh thu nháģ album chia sáēģ", "cache_settings_statistics_thumbnail": "HÃŦnh thu nháģ", "cache_settings_statistics_title": "MáģŠc sáģ­ dáģĨng báģ™ nháģ› Ä‘áģ‡m", "cache_settings_subtitle": "Kiáģƒm soÃĄt hành vi báģ™ nháģ› Ä‘áģ‡m cáģ§a áģŠng dáģĨng Immich", - "cache_settings_thumbnail_size": "Kích thưáģ›c báģ™ nháģ› Ä‘áģ‡m hÃŦnh thu nháģ ({} áēŖnh)", + "cache_settings_thumbnail_size": "Kích thưáģ›c báģ™ nháģ› Ä‘áģ‡m hÃŦnh thu nháģ ({count} áēŖnh)", "cache_settings_tile_subtitle": "Kiáģƒm soÃĄt cÃĄch xáģ­ lÃŊ lưu tráģ¯ cáģĨc báģ™", "cache_settings_tile_title": "Lưu tráģ¯ cáģĨc báģ™", "cache_settings_title": "Cài đáēˇt báģ™ nháģ› Ä‘áģ‡m", @@ -645,13 +638,12 @@ "contain": "CháģŠa", "context": "Ngáģ¯ cáēŖnh", "continue": "Tiáēŋp táģĨc", - "control_bottom_app_bar_album_info_shared": "{} máģĨc chia sáēģ", + "control_bottom_app_bar_album_info_shared": "{count} máģĨc chia sáēģ", "control_bottom_app_bar_create_new_album": "TáēĄo album máģ›i", "control_bottom_app_bar_delete_from_immich": "XÃŗa kháģi Immich", "control_bottom_app_bar_delete_from_local": "XÃŗa kháģi thiáēŋt báģ‹\n", "control_bottom_app_bar_edit_location": "Cháģ‰nh sáģ­a váģ‹ trí", "control_bottom_app_bar_edit_time": "Cháģ‰nh sáģ­a Ngày và Giáģ", - "control_bottom_app_bar_share_link": "Share Link", "control_bottom_app_bar_share_to": "Chia sáēģ váģ›i", "control_bottom_app_bar_trash_from_immich": "Chuyáģƒn táģ›i thÚng rÃĄc", "copied_image_to_clipboard": "ÄÃŖ sao chÊp hÃŦnh áēŖnh vào clipboard.", @@ -689,13 +681,10 @@ "current_server_address": "Đáģ‹a cháģ§ mÃĄy cháģ§ hiáģ‡n táēĄi", "custom_locale": "Ngôn ngáģ¯ và khu váģąc tÚy cháģ‰nh", "custom_locale_description": "Đáģ‹nh dáēĄng ngày và sáģ‘ dáģąa trÃĒn ngôn ngáģ¯ và khu váģąc", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Táģ‘i", "date_after": "Ngày sau", "date_and_time": "Ngày và giáģ", "date_before": "Ngày trưáģ›c", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Ngày sinh Ä‘ÃŖ đưáģŖc lưu thành công", "date_range": "KhoáēŖng tháģi gian", "day": "Ngày", @@ -732,7 +721,6 @@ "direction": "Hưáģ›ng", "disabled": "Táē¯t", "disallow_edits": "Không cho phÊp cháģ‰nh sáģ­a", - "discord": "Discord", "discover": "TÃŦm", "dismiss_all_errors": "Báģ qua táēĨt cáēŖ láģ—i", "dismiss_error": "Báģ qua láģ—i", @@ -749,7 +737,7 @@ "download_enqueue": "Đang cháģ táēŖi xuáģ‘ng", "download_error": "Láģ—i táēŖi xuáģ‘ng", "download_failed": "TáēŖi xuáģ‘ng tháēĨt báēĄi", - "download_filename": "táē­p tin: {}", + "download_filename": "táē­p tin: {filename}", "download_finished": "TáēŖi xuáģ‘ng hoàn táēĨt", "download_include_embedded_motion_videos": "CÃĄc video nhÃēng", "download_include_embedded_motion_videos_description": "Gáģ“m cÃĄc video đưáģŖc nhÃēng trong áēŖnh chuyáģƒn đáģ™ng thành máģ™t táē­p tin riÃĒng", @@ -792,7 +780,6 @@ "editor_close_without_save_title": "ÄÃŗng trÃŦnh cháģ‰nh sáģ­a?", "editor_crop_tool_h2_aspect_ratios": "Táģˇ láģ‡ khung hÃŦnh", "editor_crop_tool_h2_rotation": "Xoay", - "email": "Email", "empty_folder": "Thư máģĨc ráģ—ng", "empty_trash": "Dáģn sáēĄch thÚng rÃĄc", "empty_trash_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n dáģn sáēĄch thÚng rÃĄc không? Điáģu này sáēŊ xÃŗa vÄŠnh viáģ…n táēĨt cáēŖ cÃĄc máģĨc trong thÚng rÃĄc kháģi Immich.\nBáēĄn không tháģƒ hoàn tÃĄc hành đáģ™ng này!", @@ -804,7 +791,7 @@ "error": "Láģ—i", "error_change_sort_album": "Thay đáģ•i tháģŠ táģą hiáģƒu tháģ‹ tháēĨt báēĄi", "error_loading_image": "Láģ—i táēŖi áēŖnh", - "error_saving_image": "Láģ—i: {}", + "error_saving_image": "Láģ—i: {error}", "error_title": "Láģ—i - CÃŗ điáģu gÃŦ Ä‘Ãŗ không đÃēng", "errors": { "cannot_navigate_next_asset": "Không tháģƒ Ä‘iáģu hưáģ›ng đáēŋn áēŖnh tiáēŋp theo", @@ -932,16 +919,11 @@ "unable_to_update_user": "Không tháģƒ cáē­p nháē­t ngưáģi dÚng", "unable_to_upload_file": "Không tháģƒ táēŖi táē­p tin lÃĒn" }, - "exif": "Exif", "exif_bottom_sheet_description": "ThÃĒm mô táēŖ...", "exif_bottom_sheet_details": "CHI TIáēžT", "exif_bottom_sheet_location": "VáģŠ TRÍ", "exif_bottom_sheet_people": "MáģŒI NGƯáģœI", "exif_bottom_sheet_person_add_person": "ThÃĒm tÃĒn", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", "exit_slideshow": "ThoÃĄt trÃŦnh chiáēŋu", "expand_all": "MáģŸ ráģ™ng táēĨt cáēŖ", "experimental_settings_new_asset_list_subtitle": "Đang trong quÃĄ trÃŦnh phÃĄt triáģƒn", @@ -1019,7 +1001,6 @@ "home_page_archive_err_partner": "Không tháģƒ lưu tráģ¯ áēŖnh cáģ§a ngưáģi thÃĸn, báģ qua", "home_page_building_timeline": "Đang dáģąng dÃ˛ng tháģi gian áēŖnh", "home_page_delete_err_partner": "Không tháģƒ xoÃĄ áēŖnh cáģ§a ngưáģi thÃĸn, báģ qua", - "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", "home_page_favorite_err_local": "Không tháģƒ yÃĒu thích áēŖnh cáģĨc báģ™, báģ qua", "home_page_favorite_err_partner": "Không tháģƒ yÃĒu thích áēŖnh cáģ§a ngưáģi thÃĸn, báģ qua", "home_page_first_time_notice": "Náēŋu đÃĸy là láē§n đáē§u báēĄn sáģ­ dáģĨng áģŠng dáģĨng, đáēŖm báēŖo cháģn (cÃĄc) album sao lưu đáģƒ dÃ˛ng tháģi gian cÃŗ tháģƒ táģą Ä‘áģ™ng thÃĒm áēŖnh và video trong (cÃĄc) album.\n", @@ -1148,8 +1129,8 @@ "manage_your_devices": "QuáēŖn lÃŊ cÃĄc thiáēŋt báģ‹ Ä‘ÃŖ đăng nháē­p cáģ§a báēĄn", "manage_your_oauth_connection": "QuáēŖn lÃŊ káēŋt náģ‘i OAuth cáģ§a báēĄn", "map": "BáēŖn đáģ“", - "map_assets_in_bound": "{} áēŖnh", - "map_assets_in_bounds": "{} áēŖnh", + "map_assets_in_bound": "{count} áēŖnh", + "map_assets_in_bounds": "{count} áēŖnh", "map_cannot_get_user_location": "Không tháģƒ xÃĄc đáģ‹nh váģ‹ trí cáģ§a báēĄn", "map_location_dialog_yes": "CÃŗ", "map_location_picker_page_use_location": "DÚng váģ‹ trí này", @@ -1163,9 +1144,9 @@ "map_settings": "Cài đáēˇt báēŖn đáģ“", "map_settings_dark_mode": "Cháēŋ đáģ™ táģ‘i", "map_settings_date_range_option_day": "Trong vÃ˛ng 24 giáģ qua", - "map_settings_date_range_option_days": "Trong {} ngày qua", + "map_settings_date_range_option_days": "Trong {days} ngày qua", "map_settings_date_range_option_year": "Năm ngoÃĄi", - "map_settings_date_range_option_years": "Trong {} năm qua", + "map_settings_date_range_option_years": "Trong {years} năm qua", "map_settings_dialog_title": "Cài đáēˇt báēŖn đáģ“", "map_settings_include_show_archived": "Bao gáģ“m áēŖnh Ä‘ÃŖ lưu tráģ¯", "map_settings_include_show_partners": "Bao gáģ“m ngưáģi thÃĸn", @@ -1180,11 +1161,8 @@ "memories_setting_description": "QuáēŖn lÃŊ nháģ¯ng káģˇ niáģ‡m cáģ§a báēĄn", "memories_start_over": "Báē¯t đáē§u láēĄi", "memories_swipe_to_close": "Vuáģ‘t đáģƒ Ä‘Ãŗng", - "memories_year_ago": "Máģ™t năm trưáģ›c", - "memories_years_ago": "{} năm trưáģ›c", "memory": "Káģˇ niáģ‡m", "memory_lane_title": "Káģˇ niáģ‡m {title}", - "menu": "Menu", "merge": "HáģŖp nháēĨt", "merge_people": "HáģŖp nháēĨt ngưáģi", "merge_people_limit": "BáēĄn cháģ‰ cÃŗ tháģƒ háģŖp nháēĨt táģ‘i đa 5 khuôn máēˇt cÚng máģ™t lÃēc", @@ -1196,7 +1174,6 @@ "missing": "Thiáēŋu", "model": "DÃ˛ng", "month": "ThÃĄng", - "monthly_title_text_date_format": "MMMM y", "more": "ThÃĒm", "moved_to_trash": "ÄÃŖ chuyáģƒn vào thÚng rÃĄc", "multiselect_grid_edit_date_time_err_read_only": "Không tháģƒ cháģ‰nh sáģ­a ngày cáģ§a áēŖnh cháģ‰ cÃŗ quyáģn đáģc, báģ qua", @@ -1244,7 +1221,6 @@ "notification_toggle_setting_description": "Báē­t thông bÃĄo qua email", "notifications": "Thông bÃĄo", "notifications_setting_description": "QuáēŖn lÃŊ thông bÃĄo", - "oauth": "OAuth", "official_immich_resources": "Tài nguyÃĒn chính tháģŠc cáģ§a Immich", "offline": "NgoáēĄi tuyáēŋn", "offline_paths": "Đưáģng dáēĢn ngoáēĄi tuyáēŋn", @@ -1282,7 +1258,7 @@ "partner_page_partner_add_failed": "ThÃĒm ngưáģi thÃĸn tháēĨt báēĄi", "partner_page_select_partner": "Cháģn ngưáģi thÃĸn", "partner_page_shared_to_title": "Chia sáēģ váģ›i", - "partner_page_stop_sharing_content": "{} sáēŊ không tháģƒ truy cáē­p áēŖnh cáģ§a báēĄn.", + "partner_page_stop_sharing_content": "{partner} sáēŊ không tháģƒ truy cáē­p áēŖnh cáģ§a báēĄn.", "partner_sharing": "Chia sáēģ váģ›i ngưáģi thÃĸn", "partners": "Ngưáģi thÃĸn", "password": "Máē­t kháēŠu", @@ -1347,7 +1323,6 @@ "profile_drawer_client_out_of_date_major": "áģ¨ng dáģĨng Ä‘ÃŖ láģ—i tháģi. Vui lÃ˛ng cáē­p nháē­t lÃĒn phiÃĒn báēŖn chính máģ›i nháēĨt.", "profile_drawer_client_out_of_date_minor": "áģ¨ng dáģĨng Ä‘ÃŖ láģ—i tháģi. Vui lÃ˛ng cáē­p nháē­t lÃĒn phiÃĒn báēŖn pháģĨ máģ›i nháēĨt.", "profile_drawer_client_server_up_to_date": "MÃĄy khÃĄch và mÃĄy cháģ§ Ä‘ÃŖ cáē­p nháē­t", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "MÃĄy cháģ§ Ä‘ÃŖ láģ—i tháģi. Vui lÃ˛ng cáē­p nháē­t lÃĒn phiÃĒn báēŖn chính máģ›i nháēĨt.", "profile_drawer_server_out_of_date_minor": "MÃĄy cháģ§ Ä‘ÃŖ láģ—i tháģi. Vui lÃ˛ng cáē­p nháē­t lÃĒn phiÃĒn báēŖn pháģĨ máģ›i nháēĨt.", "profile_image_of_user": "áēĸnh đáēĄi diáģ‡ncáģ§a {user}", @@ -1356,7 +1331,7 @@ "public_share": "Chia sáēģ công khai", "purchase_account_info": "Ngưáģi háģ— tráģŖ", "purchase_activated_subtitle": "CáēŖm ÆĄn báēĄn Ä‘ÃŖ háģ— tráģŖ Immich và pháē§n máģm mÃŖ nguáģ“n máģŸ", - "purchase_activated_time": "ÄÃŖ kích hoáēĄt vào {date, date}", + "purchase_activated_time": "ÄÃŖ kích hoáēĄt vào {date}", "purchase_activated_title": "KhÃŗa cáģ§a báēĄn Ä‘ÃŖ đưáģŖc kích hoáēĄt thành công", "purchase_button_activate": "Kích hoáēĄt", "purchase_button_buy": "Mua", @@ -1554,12 +1529,12 @@ "setting_languages_apply": "Áp dáģĨng", "setting_languages_subtitle": "QuáēŖn lÃŊ tuáģŗ cháģn ngôn ngáģ¯", "setting_languages_title": "Ngôn ngáģ¯", - "setting_notifications_notify_failures_grace_period": "Thông bÃĄo sao lưu náģn tháēĨt báēĄi: {}", - "setting_notifications_notify_hours": "{} giáģ", + "setting_notifications_notify_failures_grace_period": "Thông bÃĄo sao lưu náģn tháēĨt báēĄi: {duration}", + "setting_notifications_notify_hours": "{count} giáģ", "setting_notifications_notify_immediately": "ngay láē­p táģŠc", - "setting_notifications_notify_minutes": "{} phÃēt", + "setting_notifications_notify_minutes": "{count} phÃēt", "setting_notifications_notify_never": "không bao giáģ", - "setting_notifications_notify_seconds": "{} giÃĸy", + "setting_notifications_notify_seconds": "{count} giÃĸy", "setting_notifications_single_progress_subtitle": "Thông tin chi tiáēŋt cáģ§a táģĢng áēŖnh đang táēŖi lÃĒn", "setting_notifications_single_progress_title": "Hiáģƒn tháģ‹ chi tiáēŋt sao lưu náģn đang tháģąc hiáģ‡n", "setting_notifications_subtitle": "Điáģu cháģ‰nh tuáģŗ cháģ‰nh thông bÃĄo cáģ§a báēĄn", @@ -1573,7 +1548,7 @@ "settings_saved": "ÄÃŖ lưu cài đáēˇt", "share": "Chia sáēģ", "share_add_photos": "ThÃĒm áēŖnh", - "share_assets_selected": "{} Ä‘ÃŖ cháģn", + "share_assets_selected": "{count} Ä‘ÃŖ cháģn", "share_dialog_preparing": "Đang xáģ­ lÃŊ...", "shared": "ÄÃŖ đưáģŖc chia sáēģ", "shared_album_activities_input_disable": "Nháē­n xÊt hiáģ‡n Ä‘ÃŖ táē¯t", @@ -1587,32 +1562,32 @@ "shared_by_user": "ĐưáģŖc chia sáēģ báģŸi {user}", "shared_by_you": "ĐưáģŖc chia sáēģ báģŸi báēĄn", "shared_from_partner": "áēĸnh táģĢ {partner}", - "shared_intent_upload_button_progress_text": "{} / {} ÄÃŖ táēŖi lÃĒn", + "shared_intent_upload_button_progress_text": "{current} / {total} ÄÃŖ táēŖi lÃĒn", "shared_link_app_bar_title": "LiÃĒn káēŋt chia sáēģ", "shared_link_clipboard_copied_massage": "ÄÃŖ sao chÊp táģ›i báēŖn ghi táēĄm", - "shared_link_clipboard_text": "LiÃĒn káēŋt: {}\nMáē­t kháēŠu: {}", + "shared_link_clipboard_text": "LiÃĒn káēŋt: {link}\nMáē­t kháēŠu: {password}", "shared_link_create_error": "TáēĄo liÃĒn káēŋt chia sáēģ không thành công", "shared_link_edit_description_hint": "Nháē­p mô táēŖ chia sáēģ", "shared_link_edit_expire_after_option_day": "1 ngày", - "shared_link_edit_expire_after_option_days": "{} ngày", + "shared_link_edit_expire_after_option_days": "{count} ngày", "shared_link_edit_expire_after_option_hour": "1 giáģ", - "shared_link_edit_expire_after_option_hours": "{} giáģ", + "shared_link_edit_expire_after_option_hours": "{count} giáģ", "shared_link_edit_expire_after_option_minute": "1 phÃēt", - "shared_link_edit_expire_after_option_minutes": "{} phÃēt", - "shared_link_edit_expire_after_option_months": "{} thÃĄng", - "shared_link_edit_expire_after_option_year": "{} năm", + "shared_link_edit_expire_after_option_minutes": "{count} phÃēt", + "shared_link_edit_expire_after_option_months": "{count} thÃĄng", + "shared_link_edit_expire_after_option_year": "{count} năm", "shared_link_edit_password_hint": "Nháē­p máē­t kháēŠu chia sáēģ", "shared_link_edit_submit_button": "Cáē­p nháē­t liÃĒn káēŋt", "shared_link_error_server_url_fetch": "Không tháģƒ káēŋt náģ‘i đáģ‹a cháģ‰ mÃĄy cháģ§", - "shared_link_expires_day": "Háēŋt háēĄn trong {} ngày", - "shared_link_expires_days": "Háēŋt háēĄn trong {} ngày", - "shared_link_expires_hour": "Háēŋt háēĄn trong {} giáģ", - "shared_link_expires_hours": "Háēŋt háēĄn trong {} giáģ", - "shared_link_expires_minute": "Háēŋt háēĄn trong {} phÃēt", - "shared_link_expires_minutes": "Háēŋt háēĄn trong {} phÃēt", + "shared_link_expires_day": "Háēŋt háēĄn trong {count} ngày", + "shared_link_expires_days": "Háēŋt háēĄn trong {count} ngày", + "shared_link_expires_hour": "Háēŋt háēĄn trong {count} giáģ", + "shared_link_expires_hours": "Háēŋt háēĄn trong {count} giáģ", + "shared_link_expires_minute": "Háēŋt háēĄn trong {count} phÃēt", + "shared_link_expires_minutes": "Háēŋt háēĄn trong {count} phÃēt", "shared_link_expires_never": "Háēŋt háēĄn ∞\n", - "shared_link_expires_second": "Háēŋt háēĄn trong {} giÃĸy", - "shared_link_expires_seconds": "Háēŋt háēĄn trong {} giÃĸy", + "shared_link_expires_second": "Háēŋt háēĄn trong {count} giÃĸy", + "shared_link_expires_seconds": "Háēŋt háēĄn trong {count} giÃĸy", "shared_link_individual_shared": "Chia sáēģ riÃĒng tư", "shared_link_info_chip_metadata": "Dáģ¯ liáģ‡u EXIF", "shared_link_manage_links": "QuáēŖn lÃŊ liÃĒn káēŋt đưáģŖc chia sáēģ", @@ -1709,7 +1684,7 @@ "theme_selection": "Cháģ§ Ä‘áģ táģ•ng tháģƒ", "theme_selection_description": "Táģą Ä‘áģ™ng đáēˇt cháģ§ Ä‘áģ sÃĄng hoáēˇc táģ‘i dáģąa trÃĒn tÚy cháģn háģ‡ tháģ‘ng cáģ§a trÃŦnh duyáģ‡t cáģ§a báēĄn", "theme_setting_asset_list_storage_indicator_title": "Hiáģ‡n tháģ‹ tráēĄng thÃĄi sao lưu áēŖnh trÃĒn hÃŦnh thu nháģ ", - "theme_setting_asset_list_tiles_per_row_title": "Sáģ‘ lưáģŖng áēŖnh trÃĒn máģ™t dÃ˛ng ({})", + "theme_setting_asset_list_tiles_per_row_title": "Sáģ‘ lưáģŖng áēŖnh trÃĒn máģ™t dÃ˛ng ({count})", "theme_setting_colorful_interface_subtitle": "Áp dáģĨng màu cháģ§ Ä‘áēĄo cho náģn áģŠng dáģĨng", "theme_setting_colorful_interface_title": "Giao diáģ‡n màu sáē¯c", "theme_setting_image_viewer_quality_subtitle": "Điáģu cháģ‰nh cháēĨt lưáģŖng cáģ§a trÃŦnh xem áēŖnh", @@ -1742,11 +1717,11 @@ "trash_no_results_message": "áēĸnh và video Ä‘ÃŖ báģ‹ xoÃĄ sáēŊ hiáģƒn tháģ‹ áģŸ Ä‘Ãĸy.", "trash_page_delete_all": "XoÃĄ táēĨt cáēŖ", "trash_page_empty_trash_dialog_content": "BáēĄn cÃŗ muáģ‘n dáģn sáēĄch thÚng rÃĄc cáģ§a mÃŦnh không? Nháģ¯ng máģĨc này sáēŊ báģ‹ xoÃĄ vÄŠnh viáģ…n kháģi Immich", - "trash_page_info": "Nháģ¯ng máģĨc này sáēŊ báģ‹ xoÃĄ sau {} ngày", + "trash_page_info": "Nháģ¯ng máģĨc này sáēŊ báģ‹ xoÃĄ sau {days} ngày", "trash_page_no_assets": "Không cÃŗ máģĨc nào", "trash_page_restore_all": "Khôi pháģĨc táēĨt cáēŖ", "trash_page_select_assets_btn": "Cháģn áēŖnh", - "trash_page_title": "ThÚng rÃĄc ({})", + "trash_page_title": "ThÚng rÃĄc ({count})", "trashed_items_will_be_permanently_deleted_after": "CÃĄc máģĨc Ä‘ÃŖ xÃŗa sáēŊ báģ‹ xÃŗa vÄŠnh viáģ…n sau {days, plural, one {# ngày} other {# ngày}}.", "type": "LoáēĄi", "unarchive": "Huáģˇ lưu tráģ¯", @@ -1782,9 +1757,8 @@ "upload_status_errors": "Láģ—i", "upload_status_uploaded": "ÄÃŖ táēŖi lÃĒn", "upload_success": "TáēŖi lÃĒn thành công, làm máģ›i trang đáģƒ xem cÃĄc táē­p tin máģ›i táēŖi lÃĒn.", - "upload_to_immich": "TáēŖi lÃĒn Immich ({})", + "upload_to_immich": "TáēŖi lÃĒn Immich ({count})", "uploading": "Đang táēŖi lÃĒn", - "url": "URL", "usage": "Sáģ­ dáģĨng", "use_current_connection": "dÚng káēŋt náģ‘i hiáģ‡n táēĄi", "use_custom_date_range": "Sáģ­ dáģĨng khoáēŖng tháģi gian tuáģŗ cháģ‰nh", @@ -1811,7 +1785,6 @@ "version_announcement_overlay_title": "PhiÃĒn báēŖn mÃĄy cháģ§ cÃŗ báēŖn cáē­p nháē­t máģ›i", "version_history": "Láģ‹ch sáģ­ phiÃĒn báēŖn", "version_history_item": "ÄÃŖ cài đáēˇt {version} vào {date}", - "video": "Video", "video_hover_setting": "PhÃĄt đoáēĄn video xem trưáģ›c khi di chuáģ™t", "video_hover_setting_description": "PhÃĄt đoáēĄn video xem trưáģ›c khi di chuáģ™t qua máģĨc. Ngay cáēŖ khi táē¯t cháģŠc năng này, váēĢn cÃŗ tháģƒ báē¯t đáē§u phÃĄt video báēąng cÃĄch di chuáģ™t qua biáģƒu tưáģŖng phÃĄt.", "videos": "Video", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index bb5094e352..cde9717e22 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -14,7 +14,7 @@ "add_a_location": "新åĸžåœ°éģž", "add_a_name": "加å…Ĩ姓名", "add_a_title": "新åĸžæ¨™éĄŒ", - "add_endpoint": "Add endpoint", + "add_endpoint": "新åĸžį̝éģž", "add_exclusion_pattern": "加å…Ĩį¯Šé¸æĸäģļ", "add_import_path": "新åĸžåŒ¯å…Ĩčˇ¯åž‘", "add_location": "新åĸžåœ°éģž", @@ -39,11 +39,11 @@ "authentication_settings_disable_all": "įĸē厚čĻåœį”¨æ‰€æœ‰į™ģå…Ĩæ–šåŧå—ŽīŧŸé€™æ¨ŖæœƒåŽŒå…¨į„Ąæŗ•į™ģå…Ĩ。", "authentication_settings_reenable": "åĻ‚éœ€é‡æ–°å•Ÿį”¨īŧŒčĢ‹äŊŋᔍ äŧ翜å™¨æŒ‡äģ¤ ã€‚", "background_task_job": "čƒŒæ™¯åŸˇčĄŒ", - "backup_database": "備äģŊčŗ‡æ–™åēĢ", - "backup_database_enable_description": "å•Ÿį”¨čŗ‡æ–™åēĢ備äģŊ", + "backup_database": "åģēįĢ‹čŗ‡æ–™åēĢ備äģŊ", + "backup_database_enable_description": "é–‹å•Ÿčŗ‡æ–™åēĢ備äģŊ", "backup_keep_last_amount": "äŋį•™å…ˆå‰å‚™äģŊįš„æ•¸é‡", - "backup_settings": "備äģŊč¨­åŽš", - "backup_settings_description": "įŽĄį†čŗ‡æ–™åēĢ備äģŊč¨­åŽš", + "backup_settings": "čŗ‡æ–™åēĢ備äģŊč¨­åŽš", + "backup_settings_description": "įŽĄį†čŗ‡æ–™åēĢ備äģŊč¨­åŽšã€‚ *č¨ģīŧšé€™é …äģģå‹™ä¸æœƒæœ‰į´€éŒ„īŧŒå¤ąæ•—æ™‚į„Ąæŗ•æ”ļ到通įŸĨ。", "check_all": "全選", "cleanup": "æ¸…į†", "cleared_jobs": "厞åˆĒ除「{job}」äģģ務", @@ -53,24 +53,25 @@ "confirm_email_below": "čĢ‹åœ¨åē•下čŧ¸å…Ĩ {email} 來įĸēčĒ", "confirm_reprocess_all_faces": "įĸē厚čĻé‡æ–°č™•į†æ‰€æœ‰č‡‰å­”å—ŽīŧŸé€™æœƒæ¸…除厞å‘Ŋåįš„äēēį‰Šã€‚", "confirm_user_password_reset": "您įĸē厚čĻé‡č¨­ {user} įš„å¯†įĸŧ嗎īŧŸ", + "confirm_user_pin_code_reset": "įĸē厚čĻé‡įŊŽ {user} įš„ PIN įĸŧ嗎īŧŸ", "create_job": "åģēįĢ‹äŊœæĨ­", "cron_expression": "Cron é‹įŽ—åŧ", "cron_expression_description": "äģĨ Cron æ ŧåŧč¨­åŽšæŽƒææ™‚æŽĩã€‚čŠŗį´°čŗ‡č¨ŠčĢ‹åƒé–ą Crontab Guru", "cron_expression_presets": "įžæˆįš„ Cron é‹įŽ—åŧ", "disable_login": "åœį”¨į™ģå…Ĩ", - "duplicate_detection_job_description": "對æĒ”æĄˆåŸˇčĄŒæŠŸå™¨å­¸įŋ’䞆åĩæ¸Ŧᛏäŧŧåœ–į‰‡ã€‚īŧˆæ­¤åŠŸčƒŊäģ°čŗ´æ™ē慧搜尋īŧ‰", + "duplicate_detection_job_description": "䞝靠æ™ēæ…§æœå°‹ã€‚åŸˇčĄŒæŠŸå™¨å­¸įŋ’å°é …į›Žäž†åĩæ¸Ŧᛏäŧŧåœ–į‰‡ã€‚", "exclusion_pattern_description": "排除čĻå‰‡čŽ“æ‚¨åœ¨æŽƒæčŗ‡æ–™åēĢæ™‚åŋŊį•Ĩį‰šåŽšæ–‡äģļ和文äģļå¤žã€‚į”¨æ–ŧį•ļæ‚¨æœ‰ä¸æƒŗå°Žå…Ĩįš„æ–‡äģļīŧˆäž‹åĻ‚ RAW 文äģļīŧ‰æˆ–æ–‡äģļ夞。", "external_library_created_at": "å¤–éƒ¨į›¸į°ŋ(æ–ŧ {date} åģēįĢ‹)", "external_library_management": "å¤–éƒ¨į›¸į°ŋįŽĄį†", "face_detection": "臉孔åĩæ¸Ŧ", - "face_detection_description": "äŊŋį”¨æŠŸå™¨å­¸įŋ’åĩæ¸ŦæĒ”æĄˆä¸­įš„č‡‰å­”īŧˆåŊąį‰‡åĒ會åĩæ¸Ŧį¸Žåœ–ä¸­įš„č‡‰å­”īŧ‰ã€‚é¸æ“‡ã€Œé‡æ–°æ•´į†ã€æœƒé‡æ–°č™•į†æ‰€æœ‰æĒ”æĄˆã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒæ¸…é™¤į›Žå‰æ‰€æœ‰įš„č‡‰å­”čŗ‡æ–™ã€‚é¸æ“‡ã€Œéēå¤ąįš„ã€æœƒæŠŠå°šæœĒč™•į†įš„æĒ”æĄˆæŽ’å…Ĩ處ᐆäŊ‡åˆ—ã€‚č‡‰å­”åĩæ¸Ŧ厌成垌īŧŒæœƒæŠŠåĩæ¸Ŧåˆ°įš„č‡‰å­”æŽ’å…Ĩ臉部辨識äŊ‡åˆ—īŧŒå°‡å…ļ分įĩ„åˆ°įžæœ‰įš„æˆ–æ–°įš„äēēį‰Šä¸­ã€‚", + "face_detection_description": "äŊŋį”¨æŠŸå™¨å­¸įŋ’åĩæ¸Ŧé …į›Žä¸­įš„č‡‰å­”(åŊąį‰‡åĒ會åĩæ¸Ŧį¸Žåœ–ä¸­įš„č‡‰å­”)ã€‚é¸æ“‡ã€Œé‡æ–°č™•į†ã€æœƒé‡æ–°č™•į†æ‰€æœ‰å°šæœĒ處ᐆäģĨåŠåˇ˛įļ“č™•į†įš„é …į›Žã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒæ¸…é™¤į›Žå‰æ‰€æœ‰įš„č‡‰å­”čŗ‡æ–™ã€‚é¸æ“‡ã€ŒæŽ’å…ĨæœĒč™•į†ã€æœƒæŠŠå°šæœĒč™•į†įš„é …į›ŽæŽ’å…Ĩ處ᐆåēåˆ—ä¸­ã€‚č‡‰å­”åĩæ¸Ŧ厌成垌īŧŒæœƒæŠŠåĩæ¸Ŧåˆ°įš„č‡‰å­”æŽ’å…Ĩ臉部辨識åēåˆ—中īŧŒå°‡å…ļ分įĩ„åˆ°įžæœ‰įš„æˆ–æ–°įš„äēēį‰Šä¸­ã€‚", "facial_recognition_job_description": "將åĩæ¸Ŧåˆ°įš„č‡‰å­”äžį…§äēēį‰Šåˆ†įĩ„。此æ­ĨéŠŸæœƒåœ¨č‡‰å­”åĩæ¸ŦåŽŒæˆåžŒåŸˇčĄŒã€‚é¸æ“‡ã€Œé‡č¨­ã€æœƒé‡æ–°åˆ†įĩ„æ‰€æœ‰č‡‰å­”ã€‚é¸æ“‡ã€Œéēå¤ąįš„ã€æœƒæŠŠå°šæœĒ指厚äēēį‰Šįš„č‡‰å­”æŽ’å…ĨäŊ‡åˆ—。", "failed_job_command": "{job} äģģå‹™įš„ {command} 指äģ¤åŸˇčĄŒå¤ąæ•—", - "force_delete_user_warning": "č­Ļ告īŧšé€™å°‡įĢ‹åŗåˆĒ除äŊŋį”¨č€…åŠå…ļčŗ‡æ–™ã€‚æ“äŊœåžŒį„Ąæŗ•反悔且åˆĒé™¤įš„æĒ”æĄˆį„Ąæŗ•æĸ垊。", + "force_delete_user_warning": "č­Ļ告īŧšé€™å°‡įĢ‹åŗåˆĒ除äŊŋį”¨č€…åŠæ‰€æœ‰é …į›Žã€‚į„Ąæŗ•é‚„åŽŸåˆĒé™¤įš„æĒ”æĄˆã€‚", "forcing_refresh_library_files": "åŧˇåˆļé‡æ–°æ•´į†æ‰€æœ‰åœ–åēĢæĒ”æĄˆ", "image_format": "æ ŧåŧ", "image_format_description": "WebP čƒŊį”ĸį”Ÿį›¸å°æ–ŧ JPEG æ›´å°įš„æĒ”æĄˆīŧŒäŊ†įˇ¨įĸŧ速åēĻčŧƒæ…ĸ。", - "image_fullsize_description": "剝é›ĸåœ–į‰‡čŠŽé‡‹čŗ‡æ–™/å…ƒæ•¸æ“šåžŒįš„å…¨å°ēå¯¸åœ–į‰‡īŧŒåœ¨åœ–ቇčĸĢæ”žå¤§įš„æƒ…æŗä¸‹äŊŋᔍ", + "image_fullsize_description": "剝é›ĸåœ–į‰‡čŠŗį´°čŗ‡æ–™/å…ƒæ•¸æ“šåžŒįš„å…¨å°ēå¯¸åœ–į‰‡īŧŒåœ¨åœ–ቇčĸĢæ”žå¤§įš„æƒ…æŗä¸‹äŊŋᔍ", "image_fullsize_enabled": "開啟全å°ēå¯¸åœ–į‰‡į”Ÿæˆ", "image_fullsize_enabled_description": "į‚ē非įļ˛čˇ¯å‹åĨŊåœ–į‰‡æ ŧåŧįš„åœ–į‰‡į”Ÿæˆå…¨å°ē寸圖像。在開啟 “偏åĨŊåĩŒå…Ĩįš„é čĻŊ” įš„é¸é …åžŒīŧŒåĩŒå…Ĩ預čĻŊæœƒåœ¨æ˛’æœ‰čŊ‰æ›æ ŧåŧä¸‹įš„į‹€æŗčĸĢäŊŋį”¨ã€‚é€™é …é¸é …ä¸åŊąéŸŋJPEGį­‰įļ˛čˇ¯å‹åĨŊåœ–į‰‡æ ŧåŧã€‚", "image_fullsize_quality_description": "åžž1-100įš„å…¨å°ēå¯¸åœ–į‰‡å“čŗĒ。čļŠéĢ˜įš„æ•¸å­—äģŖčĄ¨č‘—į”ĸå‡ēįš„å“čŗĒčļŠé̘īŧŒæĒ”æĄˆæ›´å¤§ã€‚", @@ -106,7 +107,7 @@ "library_scanning_enable_description": "å•Ÿį”¨åœ–åēĢ厚期掃描", "library_settings": "外部圖åēĢ", "library_settings_description": "įŽĄį†å¤–éƒ¨åœ–åēĢč¨­åŽš", - "library_tasks_description": "æŽƒæå¤–éƒ¨čŗ‡æ–™åēĢäģĨ尋扞新åĸžæˆ–æ›´æ”šįš„čŗ‡æē", + "library_tasks_description": "æŽƒæå¤–éƒ¨čŗ‡æēäģĨ尋扞新åĸžæˆ–æ›´æ”šįš„é …į›Ž", "library_watching_enable_description": "į›ŖæŽ§å¤–éƒ¨åœ–åēĢįš„æĒ”æĄˆčŽŠåŒ–", "library_watching_settings": "圖åēĢį›ŖæŽ§īŧˆå¯Ļ驗中īŧ‰", "library_watching_settings_description": "č‡Ēå‹•į›ŖæŽ§æĒ”æĄˆįš„čŽŠåŒ–", @@ -117,7 +118,7 @@ "machine_learning_clip_model_description": "é€™čŖĄæœ‰äģŊ CLIP æ¨Ąåž‹åå–Žã€‚č¨ģīŧšæ›´æ›æ¨Ąåž‹åžŒé ˆå°æ‰€æœ‰åœ–į‰‡é‡æ–°åŸˇčĄŒã€Œæ™ē慧搜尋」äŊœæĨ­ã€‚", "machine_learning_duplicate_detection": "é‡č¤‡é …į›Žåĩæ¸Ŧ", "machine_learning_duplicate_detection_enabled": "å•Ÿį”¨é‡č¤‡é …į›Žåĩæ¸Ŧ", - "machine_learning_duplicate_detection_enabled_description": "åŗäŊŋåœį”¨īŧŒåŽŒå…¨ä¸€æ¨Ŗįš„į´ æäģæœƒčĸĢåŋŊį•Ĩ。", + "machine_learning_duplicate_detection_enabled_description": "é—œé–‰čŠ˛åŠŸčƒŊ會åŋŊį•Ĩæœ‰é‡č¤‡įš„é …į›Žã€‚", "machine_learning_duplicate_detection_setting_description": "ᔍ CLIP 向量比對æŊ›åœ¨é‡č¤‡", "machine_learning_enabled": "å•Ÿį”¨æŠŸå™¨å­¸įŋ’", "machine_learning_enabled_description": "č‹Ĩåœį”¨īŧŒå‰‡į„ĄčĻ–ä¸‹æ–šįš„č¨­åŽšīŧŒæ‰€æœ‰æŠŸå™¨å­¸įŋ’įš„åŠŸčƒŊéƒŊå°‡åœį”¨ã€‚", @@ -166,11 +167,11 @@ "metadata_settings": "čŠŗį´°čŗ‡æ–™č¨­åŽš", "metadata_settings_description": "įŽĄį†čŠŽé‡‹čŗ‡æ–™č¨­åŽš", "migration_job": "遡į§ģ", - "migration_job_description": "å°‡į…§į‰‡å’Œäēēč‡‰įš„į¸Žåœ–éˇį§ģåˆ°æœ€æ–°įš„æ–‡äģļ夞įĩæ§‹", + "migration_job_description": "å°‡é …į›Žå’Œč‡‰å­”įš„į¸Žåœ–į§ģåˆ°æ–°įš„åģļäŧ¸čŗ‡æ–™å¤ž", "no_paths_added": "æœĒæˇģåŠ čˇ¯åž‘", "no_pattern_added": "æœĒæˇģ加pattern", - "note_apply_storage_label_previous_assets": "č¨ģīŧščĻå°‡å„˛å­˜æ¨™įą¤į”¨æ–ŧå…ˆå‰ä¸Šå‚ŗįš„æĒ”æĄˆīŧŒčĢ‹åŸˇčĄŒ", - "note_cannot_be_changed_later": "č¨ģīŧšäš‹åžŒå°ąį„Ąæŗ•更攚嘍īŧ", + "note_apply_storage_label_previous_assets": "*č¨ģīŧšåŸˇčĄŒåĨ—į”¨å„˛å­˜æ¨™įą¤å‰å…ˆä¸Šå‚ŗé …į›Ž", + "note_cannot_be_changed_later": "*č¨ģīŧšäš‹åžŒį„Ąæŗ•äŋŽæ”šīŧ", "notification_email_from_address": "寄äģļ地址", "notification_email_from_address_description": "寄äģļ者é›ģ子éƒĩäģļ地址īŧˆäž‹īŧšImmich Photo Server īŧ‰", "notification_email_host_description": "é›ģ子éƒĩäģļäŧ翜å™¨ä¸ģ抟īŧˆäž‹īŧšsmtp.immich.appīŧ‰", @@ -192,26 +193,22 @@ "oauth_auto_register": "č‡Ē動č¨ģ冊", "oauth_auto_register_description": "äŊŋᔍ OAuth į™ģ錄垌č‡Ē動č¨ģå†Šæ–°į”¨æˆļ", "oauth_button_text": "按鈕文字", - "oauth_client_id": "åŽĸæˆļį̝ ID", - "oauth_client_secret": "åŽĸæˆļį̝坆鑰", + "oauth_client_secret_description": "åĻ‚æžœ OAuth æäž›č€…ä¸æ”¯æ´ PKCEīŧˆæŽˆæŦŠįĸŧ驗證įĸŧä礿›æŠŸåˆļīŧ‰īŧŒå‰‡æ­¤į‚ēåŋ…åĄĢé …į›Ž", "oauth_enable_description": "ᔍ OAuth į™ģå…Ĩ", - "oauth_issuer_url": "į°Ŋį™ŧ者įļ˛å€", "oauth_mobile_redirect_uri": "į§ģ動įĢ¯é‡åŽšå‘ URI", "oauth_mobile_redirect_uri_override": "į§ģ動įĢ¯é‡åŽšå‘ URI čφ蓋", "oauth_mobile_redirect_uri_override_description": "į•ļ OAuth æäž›č€…ä¸å…č¨ąäŊŋį”¨čĄŒå‹• URIīŧˆåĻ‚ã€Œ'{callback}'」īŧ‰æ™‚å•Ÿį”¨", - "oauth_profile_signing_algorithm": "č¨­åŽšæĒ”į°ŊįĢ æŧ”įŽ—æŗ•", - "oauth_profile_signing_algorithm_description": "ᔍæ–ŧį°ŊįŊ˛äŊŋį”¨č€…č¨­åŽšæĒ”įš„æŧ”įŽ—æŗ•ã€‚", - "oauth_scope": "į¯„åœ", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģå…Ĩč¨­åŽš", "oauth_settings_more_details": "æŦ˛įž­č§Ŗæ­¤åŠŸčƒŊīŧŒčĢ‹åƒé–ąčĒĒæ˜Žæ›¸ã€‚", - "oauth_signing_algorithm": "į°ŊįĢ æŧ”įŽ—æŗ•", "oauth_storage_label_claim": "å„˛å­˜æ¨™įą¤åŽŖå‘Š", "oauth_storage_label_claim_description": "č‡Ē動將äŊŋį”¨č€…įš„å„˛å­˜æ¨™įą¤åŽšį‚ēæ­¤åŽŖå‘Šäš‹å€ŧ。", "oauth_storage_quota_claim": "å„˛å­˜é…éĄåŽŖå‘Š", "oauth_storage_quota_claim_description": "č‡Ē動將äŊŋį”¨č€…įš„å„˛å­˜é…éĄåŽšį‚ēæ­¤åŽŖå‘Šäš‹å€ŧ。", "oauth_storage_quota_default": "é č¨­å„˛å­˜é…éĄīŧˆGiBīŧ‰", "oauth_storage_quota_default_description": "æœĒåŽŖå‘Šæ™‚æ‰€äŊŋį”¨įš„é…éĄīŧˆå–ŽäŊīŧšGiBīŧ‰īŧˆčŧ¸å…Ĩ 0 襨į¤ē不限åˆļ配額īŧ‰ã€‚", + "oauth_timeout": "čĢ‹æą‚é€žæ™‚", + "oauth_timeout_description": "čĢ‹æą‚įš„é€žæ™‚æ™‚é–“īŧˆæ¯Ģį§’īŧ‰", "offline_paths": "å¤ąæ•ˆčˇ¯åž‘", "offline_paths_description": "這äē›å¯čƒŊ是手動åˆĒ除非外部圖åēĢįš„æĒ”æĄˆæ™‚æ‰€éēį•™įš„ã€‚", "password_enable_description": "ᔍé›ģ子éƒĩäģļ和密įĸŧį™ģå…Ĩ", @@ -243,15 +240,15 @@ "sidecar_job": "邊č슿¨ĄåŧčŠŽé‡‹čŗ‡æ–™", "sidecar_job_description": "åžžæĒ”æĄˆįŗģįĩ࿐œį´ĸ或同æ­Ĩ邊č슿¨ĄåŧčŠŽé‡‹čŗ‡æ–™", "slideshow_duration_description": "每åŧĩåœ–į‰‡æ”žæ˜ įš„į§’æ•¸", - "smart_search_job_description": "對æĒ”æĄˆåŸˇčĄŒæŠŸå™¨å­¸įŋ’īŧŒäģĨ刊æ™ē慧搜尋", + "smart_search_job_description": "åŸˇčĄŒæŠŸå™¨å­¸įŋ’有劊æ–ŧæ™ē慧搜尋", "storage_template_date_time_description": "æĒ”æĄˆįš„å‰ĩåģēæ™‚æˆŗæœƒį”¨æ–ŧåˆ¤æ–ˇæ™‚é–“čŗ‡č¨Š", "storage_template_date_time_sample": "æ™‚é–“æ¨Ŗåŧ {date}", "storage_template_enable_description": "å•Ÿį”¨å­˜å„˛æ¨Ąæŋåŧ•擎", "storage_template_hash_verification_enabled": "æ•Ŗåˆ—å‡Ŋæ•°éŠ—č­‰åˇ˛å•Ÿį”¨", "storage_template_hash_verification_enabled_description": "å•Ÿį”¨æ•Ŗåˆ—å‡Ŋæ•°éŠ—č­‰īŧŒé™¤éžæ‚¨åžˆæ¸…æĨšåœ°įŸĨé“é€™å€‹é¸é …įš„äŊœį”¨īŧŒåĻ則čĢ‹å‹ŋåœį”¨æ­¤åŠŸčƒŊ", "storage_template_migration": "å­˜å„˛æ¨Ąæŋ遡į§ģ", - "storage_template_migration_description": "將į•ļå‰įš„ {template} æ‡‰į”¨æ–ŧå…ˆå‰ä¸Šå‚ŗįš„æĒ”æĄˆ", - "storage_template_migration_info": "æĒ”æĄˆå„˛å­˜æ¨Ąæŋ將把所有æĒ”æĄˆå‰¯æĒ”åæ”šįˆ˛å°å¯Ģã€‚æ¨Ąæŋæ›´æ”šåƒ…éŠį”¨æ–ŧ新æĒ”æĄˆã€‚č‹ĨčρčŋŊæē¯æ‡‰į”¨æ¨Ąæŋč‡ŗå…ˆå‰ä¸Šå‚ŗįš„æĒ”æĄˆīŧŒčĢ‹é‹čĄŒ {job}。", + "storage_template_migration_description": "åĨ—į”¨å‰ {template} å…ˆä¸Šå‚ŗé …į›Ž", + "storage_template_migration_info": "é€į”¨å„˛å­˜į¯„äž‹å°‡å°‡æŠŠæ‰€æœ‰æĒ”æĄˆå‰¯æĒ”åæ”šįˆ˛å°å¯Ģã€‚æ¨Ąæŋæ›´æ–°åƒ…éŠį”¨æ–ŧæ–°é …į›Žã€‚č‹ĨčρåĨ—į”¨éŽåŽģį¯„äž‹čĢ‹å…ˆä¸Šå‚ŗé …į›ŽīŧŒčĢ‹åŸˇčĄŒ {job}。", "storage_template_migration_job": "å­˜å„˛æ¨Ąæŋ遡į§ģäģģ務", "storage_template_more_details": "æŦ˛äē†č§Ŗæ›´å¤šæœ‰é—œæ­¤åŠŸčƒŊįš„čŠŗį´°äŋĄæ¯īŧŒčĢ‹åƒé–ą å­˜å„˛æ¨Ąæŋ 及å…ļ åŊąéŸŋ", "storage_template_onboarding_description": "å•Ÿį”¨æ­¤åŠŸčƒŊ垌īŧŒå°‡æ šæ“šį”¨æˆļč‡ĒåŽšįžŠįš„æ¨Ąæŋč‡Ē動įĩ„į𔿖‡äģļã€‚į”ąæ–ŧįŠŠåŽšæ€§å•éĄŒīŧŒæ­¤åŠŸčƒŊ厞éģ˜čĒé—œé–‰ã€‚æŦ˛äē†č§Ŗæ›´å¤šäŋĄæ¯īŧŒčĢ‹åƒé–ą 文æĒ”。", @@ -279,7 +276,7 @@ "thumbnail_generation_job": "į”ĸį”Ÿį¸Žåœ–", "thumbnail_generation_job_description": "į‚ē每個æĒ”æĄˆį”ĸį”Ÿå¤§ã€å°åŠæ¨ĄįŗŠį¸Žåœ–īŧŒäšŸį‚ē每äŊäēēį‰Šį”ĸį”Ÿį¸Žåœ–", "transcoding_acceleration_api": "加速 API", - "transcoding_acceleration_api_description": "芲 API å°‡į”¨æ‚¨įš„č¨­å‚™åŠ é€ŸčŊ‰įĸŧã€‚č¨­įŊŽæ˜¯â€œį›ĄåŠ›č€Œį‚ē”īŧšåĻ‚æžœå¤ąæ•—īŧŒåŽƒå°‡é€€å›žåˆ°čģŸéĢ”čŊ‰įĸŧ。VP9 čŊ‰įĸŧ是åĻå¯čĄŒå–æąēæ–ŧæ‚¨įš„įĄŦéĢ”ã€‚", + "transcoding_acceleration_api_description": "API 將ᔍæ–ŧįĄŦéĢ”åŠ é€Ÿã€‚č¨­åŽšå„Ē先äŊŋᔍīŧšå¤ąæ•—會äŊŋᔍčģŸéĢ”čŊ‰įĸŧ。是åĻ支援 VP9 ᎍįĸŧæ ŧåŧäžį…§æ‚¨įš„įĄŦéĢ”æ”¯æ´č€ŒåŽšã€‚", "transcoding_acceleration_nvenc": "NVENCīŧˆéœ€čρ NVIDIA GPUīŧ‰", "transcoding_acceleration_qsv": "åŋĢ速同æ­Ĩīŧˆéœ€čρįŦŦ七äģŖæˆ–é̘æ–ŧįŦŦ七äģŖįš„ Intel CPUīŧ‰", "transcoding_acceleration_rkmpp": "RKMPPīŧˆåƒ…éŠį”¨æ–ŧ Rockchip SoCīŧ‰", @@ -341,16 +338,16 @@ "transcoding_video_codec_description": "VP9 å…ˇæœ‰éĢ˜æ•ˆčƒŊä¸”į›¸åŽšæ–ŧįļ˛é īŧŒäŊ†čŊ‰įĸŧ時間čŧƒé•ˇã€‚HEVC įš„æ•ˆčƒŊᛏčŋ‘īŧŒäŊ†įļ˛é į›¸åŽšæ€§čŧƒäŊŽã€‚H.264 å…ˇæœ‰åģŖæŗ›įš„į›¸åŽšæ€§ä¸”čŊ‰įĸŧ速åēĻåŋĢīŧŒäŊ†į”ĸį”Ÿįš„æĒ”æĄˆčŧƒå¤§ã€‚AV1 æ˜¯į›Žå‰æ•ˆįŽ‡æœ€åĨŊįš„įˇ¨č§Ŗįĸŧ器īŧŒäŊ†čŧƒčˆŠč¨­å‚™ä¸æ”¯æ´ã€‚", "trash_enabled_description": "å•Ÿį”¨åžƒåœžæĄļ功čƒŊ", "trash_number_of_days": "æ—Ĩ數", - "trash_number_of_days_description": "永䚅åˆĒ除䚋前īŧŒå°‡æĒ”æĄˆäŋį•™åœ¨åžƒåœžæĄļä¸­įš„æ—Ĩ數", + "trash_number_of_days_description": "永䚅åˆĒé™¤å‰é …į›Žå°‡äŋį•™åœ¨åžƒåœžæĄļ中數夊", "trash_settings": "垃圞æĄļ", "trash_settings_description": "įŽĄį†åžƒåœžæĄļč¨­åŽš", "untracked_files": "æœĒčĸĢčŋŊčš¤įš„æĒ”æĄˆ", - "untracked_files_description": "這äē›æĒ”æĄˆä¸æœƒčĸĢčŋŊčš¤ã€‚åŽƒå€‘å¯čƒŊ是į§ģå‹•å¤ąčĒ¤ã€ä¸Šå‚ŗä¸­æ–ˇæˆ–é‡åˆ°æŧæ´žč€Œéēį•™įš„į”ĸį‰Š", + "untracked_files_description": "這äē›æĒ”æĄˆä¸æœƒčĸĢčŋŊčš¤ã€‚åŽƒå€‘å¯čƒŊ是į§ģå‹•å¤ąæ•—ã€ä¸Šå‚ŗå¤ąæ•—ã€æŧæ´žč€Œé€ æˆįš„。", "user_cleanup_job": "æ¸…į†äŊŋᔍ者", - "user_delete_delay": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žå°‡æ–ŧ {delay, plural, other {# 夊}}垌永䚅åˆĒ除。", + "user_delete_delay": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žæœƒåœ¨ {delay, plural, other {# 夊}} 垌永䚅åˆĒ除。", "user_delete_delay_settings": "åģļ垌åˆĒ除", - "user_delete_delay_settings_description": "į§ģ除垌īŧŒæ°¸äš…åˆĒ除äŊŋį”¨č€…å¸ŗč™Ÿå’ŒæĒ”æĄˆįš„å¤Šæ•¸ã€‚äŊŋᔍ者åˆĒ除äŊœæĨ­æœƒåœ¨åˆå¤œæĒĸæŸĨ是åĻ有可äģĨåˆĒé™¤įš„äŊŋį”¨č€…ã€‚čŽŠæ›´é€™é …č¨­åŽšåžŒīŧŒæœƒåœ¨ä¸‹æŦĄåŸˇčĄŒæ™‚æĒĸæŸĨ。", - "user_delete_immediately": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žå°‡įĢ‹åŗæ°¸äš…åˆĒ除。", + "user_delete_delay_settings_description": "夊數垌將永䚅åˆĒé™¤å¸ŗč™Ÿčˆ‡é …į›Žã€‚åˆĒ除äģģ務會在 00:00 垌æĒĸæŸĨ可äģĨåˆĒé™¤įš„äŊŋį”¨č€…ã€‚čŽŠæ›´č¨­åŽšåžŒæœƒåœ¨ä¸‹æŦĄåŸˇčĄŒæĒĸæŸĨ。", + "user_delete_immediately": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žå°‡ įĢ‹åŗ 永䚅åˆĒ除。", "user_delete_immediately_checkbox": "將äŊŋį”¨č€…å’Œé …į›ŽįĢ‹åŗåˆĒ除", "user_management": "äŊŋį”¨č€…įŽĄį†", "user_password_has_been_reset": "äŊŋᔍ者坆įĸŧåˇ˛é‡č¨­īŧš", @@ -371,13 +368,17 @@ "admin_password": "įŽĄį†č€…å¯†įĸŧ", "administration": "įŽĄį†", "advanced": "進階", - "advanced_settings_log_level_title": "æ—ĨčĒŒį­‰į´šīŧš {}", - "advanced_settings_prefer_remote_subtitle": "在某äē›čŖįŊŽä¸ŠīŧŒåžžæœŦåœ°įš„é …į›Žčŧ‰å…Ĩį¸Žåœ–įš„é€ŸåēĻ非常æ…ĸ。\nå•“į”¨æ­¤é¸é …äģĨčŧ‰å…Ĩé™čˇé …į›Žã€‚", + "advanced_settings_enable_alternate_media_filter_subtitle": "äŊŋį”¨æ­¤é¸é …å¯åœ¨åŒæ­Ĩæ™‚äžį…§æ›ŋäģŖæĸäģļį¯Šé¸åĒ’éĢ”ã€‚åƒ…į•ᅦ‰į”¨į¨‹åŧåœ¨åĩæ¸Ŧæ‰€æœ‰į›¸į°ŋ時å‡ēįžå•éĄŒæ™‚æ‰åģēč­°äŊŋį”¨ã€‚", + "advanced_settings_enable_alternate_media_filter_title": "[å¯Ļ驗]äŊŋᔍå…ļäģ–įš„čŖįŊŽį›¸į°ŋ同æ­Ĩį¯Šé¸å™¨", + "advanced_settings_log_level_title": "æ—ĨčĒŒį­‰į´šīŧš{level}", + "advanced_settings_prefer_remote_subtitle": "į‰šåŽščŖįŊŽčŧ‰å…Ĩį¸Žåœ–įš„é€ŸåēĻéžå¸¸įˇŠæ…ĸ。開啟čŧ‰å…Ĩ遠įĢ¯é …į›Žįš„åŠŸčƒŊ。", "advanced_settings_prefer_remote_title": "å„Ēå…ˆé™čˇé …į›Ž", "advanced_settings_proxy_headers_subtitle": "åŽšįžŠäģŖį†æ¨™é ­īŧŒåĨ—ᔍæ–ŧImmichįš„æ¯æŦĄįļ˛įĩĄčĢ‹æą‚", "advanced_settings_proxy_headers_title": "äģŖį†æ¨™é ­", "advanced_settings_self_signed_ssl_subtitle": "į•Ĩ過äŧ翜å™¨į̝éģžįš„ SSL č­‰æ›¸éŠ—č­‰īŧˆčŠ˛é¸é …éŠį”¨æ–ŧäŊŋᔍč‡Ēį°Ŋåč­‰æ›¸įš„äŧ翜å™¨īŧ‰ã€‚", "advanced_settings_self_signed_ssl_title": "å…č¨ąč‡Ēį°Ŋ名 SSL č­‰æ›¸", + "advanced_settings_sync_remote_deletions_subtitle": "在įļ˛é ä¸ŠåŸˇčĄŒåˆĒ除或還原操äŊœæ™‚īŧŒč‡Ēå‹•åœ¨æ­¤čŖįŊŽä¸ŠåˆĒ除或還原æĒ”æĄˆ", + "advanced_settings_sync_remote_deletions_title": "同æ­Ĩ遠į̝åˆĒ除[å¯Ļ驗]", "advanced_settings_tile_subtitle": "é€˛éšŽį”¨æˆļč¨­åŽš", "advanced_settings_troubleshooting_subtitle": "啓ᔍᔍæ–ŧæ•…éšœæŽ’é™¤įš„éĄå¤–åŠŸčƒŊ", "advanced_settings_troubleshooting_title": "故障排除", @@ -400,143 +401,142 @@ "album_remove_user_confirmation": "įĸē厚čρį§ģ除 {user} 嗎īŧŸ", "album_share_no_users": "įœ‹äž†æ‚¨čˆ‡æ‰€æœ‰äŊŋį”¨č€…å…ąäēĢäē†é€™æœŦᛏį°ŋīŧŒæˆ–æ˛’æœ‰å…ļäģ–äŊŋį”¨č€…å¯äž›åˆ†äēĢ。", "album_thumbnail_card_item": "1 項", - "album_thumbnail_card_items": "{} 項", + "album_thumbnail_card_items": "{count} 項", "album_thumbnail_card_shared": " ¡ åˇ˛å…ąäēĢ", - "album_thumbnail_shared_by": "į”ą {} å…ąäēĢ", + "album_thumbnail_shared_by": "į”ą {user} å…ąäēĢ", "album_updated": "æ›´æ–°į›¸į°ŋ時", - "album_updated_setting_description": "į•ļå…ąäēĢᛏį°ŋ有新æĒ”æĄˆæ™‚īŧŒį”¨é›ģ子éƒĩäģļ通įŸĨ我", - "album_user_left": "厞é›ĸ開 {album}", - "album_user_removed": "厞į§ģ除 {user}", - "album_viewer_appbar_delete_confirm": "įĸē厚čĻåžžčŗŦæˆļ中åˆĒé™¤æ­¤į›¸į°ŋ嗎īŧŸ", - "album_viewer_appbar_share_err_delete": "åˆĒ除ᛏį°ŋå¤ąæ•—", - "album_viewer_appbar_share_err_leave": "退å‡ēå…ąäēĢå¤ąæ•—", - "album_viewer_appbar_share_err_remove": "åžžį›¸į°ŋ中į§ģ除時å‡ēįžéŒ¯čǤ", - "album_viewer_appbar_share_err_title": "äŋŽæ”šį›¸į°ŋæ¨™éĄŒå¤ąæ•—", - "album_viewer_appbar_share_leave": "退å‡ēå…ąäēĢ", - "album_viewer_appbar_share_to": "å…ąäēĢįĩĻ", - "album_viewer_page_share_add_users": "新åĸžį”¨æˆļ", + "album_updated_setting_description": "į•ļå…ąäēĢᛏį°ŋæœ‰æ–°é …į›Žæ™‚į”¨é›ģ子éƒĩäģļ通įŸĨ我", + "album_user_left": "é›ĸ開 {album}", + "album_user_removed": "į§ģ除 {user}", + "album_viewer_appbar_delete_confirm": "įĸē厚čĻåžžå¸ŗč™Ÿä¸­åˆĒé™¤æ­¤į›¸į°ŋ嗎īŧŸ", + "album_viewer_appbar_share_err_delete": "į„Ąæŗ•åˆĒ除ᛏį°ŋ", + "album_viewer_appbar_share_err_leave": "į„Ąæŗ•é›ĸ開ᛏį°ŋ", + "album_viewer_appbar_share_err_remove": "åžžį›¸į°ŋ中į§ģé™¤é …į›Žæ™‚å‡ēįžéŒ¯čǤ", + "album_viewer_appbar_share_err_title": "į„Ąæŗ•įˇ¨čŧ¯į›¸į°ŋæ¨™éĄŒ", + "album_viewer_appbar_share_leave": "é›ĸ開ᛏį°ŋ", + "album_viewer_appbar_share_to": "分äēĢįĩĻ", + "album_viewer_page_share_add_users": "邀čĢ‹å…ļäģ–äēē", "album_with_link_access": "įŸĨ道逪įĩįš„äŊŋᔍ者éƒŊ可äģĨæŸĨįœ‹é€™æœŦᛏį°ŋä¸­įš„į›¸į‰‡å’ŒäŊŋį”¨č€…ã€‚", "albums": "ᛏį°ŋ", "albums_count": "{count, plural, one {{count, number} æœŦᛏį°ŋ} other {{count, number} æœŦᛏį°ŋ}}", "all": "全部", "all_albums": "æ‰€æœ‰į›¸į°ŋ", - "all_people": "所有äēē", + "all_people": "所有äēēį‰Š", "all_videos": "所有åŊąį‰‡", "allow_dark_mode": "å…č¨ąæˇąč‰˛æ¨Ąåŧ", "allow_edits": "å…č¨ąįˇ¨čŧ¯", - "allow_public_user_to_download": "開攞įĩĻäŊŋᔍ者䏋čŧ‰", - "allow_public_user_to_upload": "é–‹æ”žčŽ“äŊŋį”¨č€…ä¸Šå‚ŗ", - "alt_text_qr_code": "QR įĸŧåœ–į‰‡", + "allow_public_user_to_download": "開攞äŊŋᔍ者䏋čŧ‰", + "allow_public_user_to_upload": "開攞äŊŋį”¨č€…ä¸Šå‚ŗ", + "alt_text_qr_code": "QR code åœ–į‰‡", "anti_clockwise": "逆時針", "api_key": "API 金鑰", - "api_key_description": "æ­¤å€ŧåƒ…éĄ¯į¤ē一æŦĄã€‚čĢ‹įĸēäŋåœ¨é—œé–‰įĒ—åŖäš‹å‰č¤‡čŖŊ厃。", - "api_key_empty": "æ‚¨įš„ API é‡‘é‘°åį¨ąä¸čƒŊį‚ēįŠē", + "api_key_description": "æ­¤é‡‘é‘°åƒ…éĄ¯į¤ē一æŦĄã€‚čĢ‹åœ¨é—œé–‰å‰č¤‡čŖŊ厃。", + "api_key_empty": "æ‚¨įš„ API é‡‘é‘°åį¨ąä¸čƒŊį‚ēįŠēå€ŧ", "api_keys": "API 金鑰", - "app_bar_signout_dialog_content": "您įĸē厚čρ退å‡ē嗎īŧŸ", + "app_bar_signout_dialog_content": "您įĸē厚čρį™ģå‡ēīŧŸ", "app_bar_signout_dialog_ok": "是", - "app_bar_signout_dialog_title": "退å‡ēį™ģå…Ĩ", + "app_bar_signout_dialog_title": "į™ģå‡ē", "app_settings": "æ‡‰į”¨į¨‹åŧč¨­åޚ", - "appears_in": "å‡ēįžåœ¨", + "appears_in": "地éģž", "archive": "封存", "archive_or_unarchive_photo": "封存或取æļˆå°å­˜į…§į‰‡", - "archive_page_no_archived_assets": "æœĒ扞到歸æĒ”é …į›Ž", - "archive_page_title": "æ­¸æĒ”īŧˆ {} īŧ‰", - "archive_size": "封存量", - "archive_size_description": "č¨­åŽščρ䏋čŧ‰įš„封存量īŧˆå–ŽäŊīŧšGiBīŧ‰", - "archived": "厞存æĒ”", + "archive_page_no_archived_assets": "æœĒæ‰žåˆ°å°å­˜é …į›Ž", + "archive_page_title": "封存 ({count})", + "archive_size": "封存æĒ”æĄˆå¤§å°", + "archive_size_description": "č¨­åŽščρ䏋čŧ‰įš„封存æĒ”æĄˆå¤§å° (å–ŽäŊīŧš GB)", + "archived": "厞封存", "archived_count": "{count, plural, other {厞封存 # å€‹é …į›Ž}}", - "are_these_the_same_person": "這䚟是同一個äēē嗎īŧŸ", - "are_you_sure_to_do_this": "您įĸē厚čρ這éēŧ做嗎īŧŸ", - "asset_action_delete_err_read_only": "į„Ąæŗ•åˆĒé™¤å”¯čŽ€é …į›ŽīŧŒį•Ĩ過", - "asset_action_share_err_offline": "į„Ąæŗ•į˛å–é›ĸįˇšé …į›ŽīŧŒį•Ĩ過", - "asset_added_to_album": "厞加å…Ĩᛏį°ŋ", - "asset_adding_to_album": "加å…Ĩᛏį°ŋ中â€Ļ", - "asset_description_updated": "æĒ”æĄˆæčŋ°åˇ˛æ›´æ–°", - "asset_filename_is_offline": "æĒ”æĄˆ {filename} é›ĸ᎚äē†", - "asset_has_unassigned_faces": "æĒ”æĄˆä¸­æœ‰æœĒæŒ‡åŽšįš„č‡‰å­”", + "are_these_the_same_person": "同一äŊäēēį‰ŠīŧŸ", + "are_you_sure_to_do_this": "您įĸē厚嗎īŧŸ", + "asset_action_delete_err_read_only": "į•ĨéŽį„Ąæŗ•åˆĒé™¤å”¯čŽ€é …į›Ž", + "asset_action_share_err_offline": "į•ĨéŽį„Ąæŗ•å–åž—įš„é›ĸįˇšé …į›Ž", + "asset_added_to_album": "厞åģēį̋ᛏį°ŋ", + "asset_adding_to_album": "新åĸžåˆ°į›¸į°ŋâ€Ļ", + "asset_description_updated": "é …į›ŽčĒĒæ˜Žåˇ˛æ›´æ–°", + "asset_filename_is_offline": "é …į›Ž {filename} 厞é›ĸ᎚", + "asset_has_unassigned_faces": "é …į›Žæœ‰æœĒ新åĸžč‡‰å­”", "asset_hashing": "č¨ˆįŽ—é›œæšŠå€ŧâ€Ļ", - "asset_list_group_by_sub_title": "分įĩ„æ–šåŧ", - "asset_list_layout_settings_dynamic_layout_title": "動態äŊˆåą€", + "asset_list_group_by_sub_title": "åˆ†éĄžæ–šåŧ", + "asset_list_layout_settings_dynamic_layout_title": "å‹•æ…‹æŽ’į‰ˆ", "asset_list_layout_settings_group_automatically": "č‡Ē動", - "asset_list_layout_settings_group_by": "é …į›Žåˆ†įĩ„æ–šåŧ", - "asset_list_layout_settings_group_by_month_day": "月和æ—Ĩ", - "asset_list_layout_sub_title": "äŊˆåą€", - "asset_list_settings_subtitle": "ᅧቇį ŧäŊˆåą€č¨­åޚ", - "asset_list_settings_title": "ᅧቇį ŧ", - "asset_offline": "æĒ”æĄˆé›ĸ᎚", - "asset_offline_description": "᪁įĸŸä¸­æ‰žä¸åˆ°æ­¤å¤–部æĒ”æĄˆã€‚čĢ‹å‘æ‚¨įš„ Immich įŽĄį†å“Ąå°‹æą‚å”åŠŠã€‚", - "asset_restored_successfully": "åˇ˛æˆåŠŸæĸåžŠæ‰€æœ‰é …į›Ž", - "asset_skipped": "厞į•Ĩ過", - "asset_skipped_in_trash": "åˇ˛ä¸ŸæŽ‰", + "asset_list_layout_settings_group_by": "é …į›Žåˆ†éĄžæ–šåŧ", + "asset_list_layout_settings_group_by_month_day": "月äģŊ和æ—Ĩ期", + "asset_list_layout_sub_title": "æŽ’į‰ˆ", + "asset_list_settings_subtitle": "į…§į‰‡æŽ’į‰ˆč¨­åŽš", + "asset_list_settings_title": "į…§į‰‡æŽ’åˆ—", + "asset_offline": "é …į›Žé›ĸ᎚", + "asset_offline_description": "᪁įĸŸä¸­æ‰žä¸åˆ°æ­¤é …į›Žã€‚čĢ‹å‘æ‚¨įš„ Immich įŽĄį†å“Ąå°‹æą‚å”åŠŠã€‚", + "asset_restored_successfully": "åˇ˛åžŠåŽŸæ‰€æœ‰é …į›Ž", + "asset_skipped": "čˇŗéŽ", + "asset_skipped_in_trash": "į§ģč‡ŗåžƒåœžæĄļ", "asset_uploaded": "åˇ˛ä¸Šå‚ŗ", "asset_uploading": "ä¸Šå‚ŗä¸­â€Ļ", - "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", - "asset_viewer_settings_title": "čŗ‡æēæŸĨįœ‹å™¨", - "assets": "æĒ”æĄˆ", + "asset_viewer_settings_subtitle": "įŽĄį†į›¸į°ŋį€čĻŊč¨­åŽš", + "asset_viewer_settings_title": "é …į›Žį€čĻŊ", + "assets": "é …į›Ž", "assets_added_count": "åˇ˛æ–°åĸž {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", - "assets_added_to_album_count": "厞將 {count, plural, other {# 個æĒ”æĄˆ}}加å…Ĩᛏį°ŋ", - "assets_added_to_name_count": "厞將 {count, plural, other {# 個æĒ”æĄˆ}}加å…Ĩ{hasName, select, true {{name}} other {æ–°į›¸į°ŋ}}", - "assets_count": "{count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", - "assets_deleted_permanently": "{} å€‹é …į›Žåˇ˛čĸĢæ°¸äš…åˆĒ除", - "assets_deleted_permanently_from_server": "åˇ˛åžžäŧ翜å™¨ä¸­æ°¸äš…į§ģ除 {} å€‹é …į›Ž", - "assets_moved_to_trash_count": "厞將 {count, plural, other {# 個æĒ”æĄˆ}}ä¸Ÿé€˛åžƒåœžæĄļ", - "assets_permanently_deleted_count": "åˇ˛æ°¸äš…åˆĒ除 {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", - "assets_removed_count": "厞į§ģ除 {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", - "assets_removed_permanently_from_device": "åˇ˛åžžčŖįŊŽä¸­æ°¸äš…į§ģ除 {} å€‹é …į›Ž", - "assets_restore_confirmation": "įĸē厚čĻé‚„åŽŸæ‰€æœ‰ä¸ŸæŽ‰įš„æĒ”æĄˆå—ŽīŧŸæ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧč¨ģīŧšé€™į„Ąæŗ•還原äģģäŊ•é›ĸ᎚æĒ”æĄˆã€‚", - "assets_restored_count": "åˇ˛é‚„åŽŸ {count, plural, other {# 個æĒ”æĄˆ}}", - "assets_restored_successfully": "åˇ˛æˆåŠŸæĸ垊 {} å€‹é …į›Ž", - "assets_trashed": "{} 個回æ”ļæĄļé …į›Ž", - "assets_trashed_count": "åˇ˛ä¸ŸæŽ‰ {count, plural, other {# 個æĒ”æĄˆ}}", - "assets_trashed_from_server": "{} å€‹é …į›Žåˇ˛æ”žå…Ĩ回æ”ļæĄļ", - "assets_were_part_of_album_count": "{count, plural, one {æĒ”æĄˆåˇ˛} other {æĒ”æĄˆåˇ˛}} æ˜¯į›¸į°ŋįš„ä¸€éƒ¨åˆ†", + "assets_added_to_album_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}加å…Ĩᛏį°ŋ", + "assets_added_to_name_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}加å…Ĩ{hasName, select, true {{name}} other {æ–°į›¸į°ŋ}}", + "assets_count": "{count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", + "assets_deleted_permanently": "{count} å€‹é …į›Žåˇ˛čĸĢæ°¸äš…åˆĒ除", + "assets_deleted_permanently_from_server": "åˇ˛åžžäŧ翜å™¨ä¸­æ°¸äš…į§ģ除 {count} å€‹é …į›Ž", + "assets_moved_to_trash_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}ä¸Ÿé€˛åžƒåœžæĄļ", + "assets_permanently_deleted_count": "永䚅åˆĒ除 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", + "assets_removed_count": "į§ģ除 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", + "assets_removed_permanently_from_device": "åžžčŖįŊŽä¸­æ°¸äš…į§ģ除 {count} å€‹é …į›Ž", + "assets_restore_confirmation": "įĸē厚čĻé‚„åŽŸæ‰€æœ‰æ¨æŖ„é …į›Žå—ŽīŧŸæ­¤æ­ĨéŠŸį„Ąæŗ•é‚„åŽŸīŧ(*č¨ģīŧšé€™į„Ąæŗ•還原äģģäŊ•é›ĸįˇšé …į›Ž)", + "assets_restored_count": "åˇ˛é‚„åŽŸ {count, plural, other {# å€‹é …į›Ž}}", + "assets_restored_successfully": "成功垊原 {count} å€‹é …į›Ž", + "assets_trashed": "æ¨æŖ„ {count} å€‹é …į›Ž", + "assets_trashed_count": "æ¨æŖ„ {count, plural, other {# å€‹é …į›Ž}}", + "assets_trashed_from_server": "{count} å€‹é …į›Žį§ģč‡ŗåžƒåœžæĄļ", + "assets_were_part_of_album_count": "{count, plural, one {é …į›Žåˇ˛} other {é …į›Žåˇ˛}} åˇ˛åœ¨į›¸į°ŋ", "authorized_devices": "授æŦŠčŖįŊŽ", - "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", - "automatic_endpoint_switching_title": "Automatic URL switching", + "automatic_endpoint_switching_subtitle": "å„Ē先äŊŋᔍ Wi-Fi é€ŖįˇšīŧŒå…ļäģ–į‹€æŗäŊŋᔍå…ļäģ–é€Ŗįˇšæ–šåŧ", + "automatic_endpoint_switching_title": "č‡Ēå‹•åˆ‡æ›é€Ŗįĩ", "back": "čŋ”回", "back_close_deselect": "čŋ”回、關閉及取æļˆé¸å–", - "background_location_permission": "Background location permission", - "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", - "backup_album_selection_page_albums_device": "čŖįŊŽä¸Šįš„ᛏį°ŋīŧˆ {} īŧ‰", - "backup_album_selection_page_albums_tap": "喎擊選中īŧŒé›™æ“Šå–æļˆ", - "backup_album_selection_page_assets_scatter": "é …į›Žæœƒåˆ†æ•Ŗåœ¨å¤šå€‹į›¸į°ŋ中。因此īŧŒå¯äģĨ在備äģŊéŽį¨‹ä¸­åŒ…åĢæˆ–æŽ’é™¤į›¸į°ŋ。", + "background_location_permission": "čƒŒæ™¯åŽšäŊæŦŠé™", + "background_location_permission_content": "é–‹å•ŸčƒŒæ™¯åŸˇčĄŒæ™‚č‡Ē動切換įļ˛čˇ¯īŧŒčĢ‹å……č¨ą Immich ä¸€åž‹å……č¨ąäŊŋį”¨į˛žįĸēäŊįŊŽæŦŠé™īŧŒäģĨįĸēčĒ Wi-Fi įļ˛čˇ¯åį¨ą", + "backup_album_selection_page_albums_device": "čŖįŊŽä¸Šįš„ᛏį°ŋīŧˆ{count}īŧ‰", + "backup_album_selection_page_albums_tap": "éģžæ“Šé¸å–īŧŒé€ŖįēŒéģžæ“Šå…ŠæŦĄå–æļˆ", + "backup_album_selection_page_assets_scatter": "é …į›Žæœƒåˆ†æ•Ŗåœ¨ä¸åŒį›¸į°ŋ。因此īŧŒå¯äģĨč¨­åŽščρ備äģŊįš„į›¸į°ŋ。", "backup_album_selection_page_select_albums": "é¸æ“‡į›¸į°ŋ", "backup_album_selection_page_selection_info": "é¸æ“‡čŗ‡č¨Š", - "backup_album_selection_page_total_assets": "į¸Ŋ計", + "backup_album_selection_page_total_assets": "į¸Ŋč¨ˆé …į›Ž", "backup_all": "全部", - "backup_background_service_backup_failed_message": "備äģŊå¤ąæ•—īŧŒæ­Ŗåœ¨é‡čŠĻâ€Ļ", - "backup_background_service_connection_failed_message": "逪æŽĨäŧ翜å™¨å¤ąæ•—īŧŒæ­Ŗåœ¨é‡čŠĻâ€Ļ", - "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šå‚ŗ {} ", + "backup_background_service_backup_failed_message": "備äģŊå¤ąæ•—īŧŒé‡æ–°å‚™äģŊ中â€Ļ", + "backup_background_service_connection_failed_message": "į„Ąæŗ•é€Ŗįˇšäŧ翜å™¨īŧŒé‡æ–°é€Ŗįˇšä¸­â€Ļ", + "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šå‚ŗ {filename}", "backup_background_service_default_notification": "æ­Ŗåœ¨æĒĸæŸĨæ–°é …į›Žâ€Ļ", - "backup_background_service_error_title": "備äģŊå¤ąæ•—", + "backup_background_service_error_title": "備äģŊ錯čǤ", "backup_background_service_in_progress_notification": "æ­Ŗåœ¨å‚™äģŊâ€Ļ", - "backup_background_service_upload_failure_notification": "ä¸Šå‚ŗå¤ąæ•— {} ", + "backup_background_service_upload_failure_notification": "į„Ąæŗ•ä¸Šå‚ŗ {filename}", "backup_controller_page_albums": "備äģŊᛏį°ŋ", - "backup_controller_page_background_app_refresh_disabled_content": "čρäŊŋį”¨čƒŒæ™¯å‚™äģŊ功čƒŊīŧŒčĢ‹åœ¨ã€Œč¨­åŽšã€>「備äģŊ」>ã€ŒčƒŒæ™¯åĨ—į”¨æ›´æ–°ã€ä¸­å•“į”¨čƒŒæœŦፋåŧæ›´æ–°ã€‚", - "backup_controller_page_background_app_refresh_disabled_title": "čƒŒæ™¯åĨ—į”¨æ›´æ–°åˇ˛įρᔍ", + "backup_controller_page_background_app_refresh_disabled_content": "é–‹å•Ÿæ‡‰į”¨į¨‹åŧčƒŒæ™¯č‡Ēå‹•é‡æ–°æ•´į†īŧŒčĢ‹åœ¨ã€Œč¨­åŽš>備äģŊ>čƒŒæ™¯é‡æ–°æ•´į†ã€é–‹å•ŸčƒŒæ™¯é‡æ–°æ•´į†ã€‚", + "backup_controller_page_background_app_refresh_disabled_title": "é—œé–‰æ‡‰į”¨į¨‹åŧčƒŒæ™¯é‡æ–°æ•´į†", "backup_controller_page_background_app_refresh_enable_button_text": "å‰åž€č¨­åŽš", "backup_controller_page_background_battery_info_link": "怎éēŧ做", "backup_controller_page_background_battery_info_message": "į‚ēäē†į˛åž—最äŊŗįš„čƒŒæ™¯å‚™äģŊéĢ”éŠ—īŧŒčĢ‹įĻį”¨æœƒäģģäŊ•限åˆļ Immich čƒŒæ™¯æ´ģå‹•įš„é›ģæą å„Ē化。\n\nį”ąæ–ŧé€™æ˜¯čŖįŊŽį›¸é—œįš„īŧŒå› æ­¤čĢ‹æŸĨæ‰žčŖįŊŽčŖŊé€ å•†æäž›įš„čŗ‡č¨Šé€˛čĄŒæ“äŊœã€‚", "backup_controller_page_background_battery_info_ok": "我įŸĨ道äē†", "backup_controller_page_background_battery_info_title": "é›ģæą æœ€äŊŗåŒ–", "backup_controller_page_background_charging": "僅在充é›ģ時", - "backup_controller_page_background_configure_error": "č¨­åŽščƒŒæ™¯æœå‹™å¤ąæ•—", - "backup_controller_page_background_delay": "åģļ遲 {} 垌備äģŊ", + "backup_controller_page_background_configure_error": "č¨­åŽščƒŒæ™¯å¤ąæ•—", + "backup_controller_page_background_delay": "åģļ遲 {duration} 垌備äģŊ", "backup_controller_page_background_description": "æ‰“é–‹čƒŒæ™¯æœå‹™äģĨč‡Ē動備äģŊäģģäŊ•æ–°é …į›ŽīŧŒä¸”į„Ąéœ€æ‰“é–‹åĨ—ᔍ", "backup_controller_page_background_is_off": "čƒŒæ™¯č‡Ē動備äģŊåˇ˛é—œé–‰", "backup_controller_page_background_is_on": "čƒŒæ™¯č‡Ē動備äģŊ厞開啓", "backup_controller_page_background_turn_off": "é—œé–‰čƒŒæ™¯æœå‹™", "backup_controller_page_background_turn_on": "é–‹å•“čƒŒæ™¯æœå‹™", - "backup_controller_page_background_wifi": "僅äŊŋᔍ WiFi", + "backup_controller_page_background_wifi": "僅äŊŋᔍ Wi-Fi", "backup_controller_page_backup": "備äģŊ", - "backup_controller_page_backup_selected": "厞遏䏭īŧš", + "backup_controller_page_backup_selected": "厞遏䏭: ", "backup_controller_page_backup_sub": "厞備äģŊįš„į…§į‰‡å’ŒįŸ­į‰‡", - "backup_controller_page_created": "新åĸžæ™‚é–“: {} ", + "backup_controller_page_created": "新åĸžæ™‚é–“: {date}", "backup_controller_page_desc_backup": "打開前台備äģŊīŧŒäģĨæœŦፋåŧé‹čĄŒæ™‚č‡Ē動備äģŊæ–°é …į›Žã€‚", - "backup_controller_page_excluded": "åˇ˛æŽ’é™¤īŧš", - "backup_controller_page_failed": "å¤ąæ•—īŧˆ {} īŧ‰", - "backup_controller_page_filename": "文äģļåį¨ą: {} [ {} ]", - "backup_controller_page_id": "ID: {} ", + "backup_controller_page_excluded": "åˇ˛æŽ’é™¤: ", + "backup_controller_page_failed": "å¤ąæ•—īŧˆ{count}īŧ‰", + "backup_controller_page_filename": "文äģļåį¨ą: {filename} [{size}]", "backup_controller_page_info": "備äģŊčŗ‡č¨Š", "backup_controller_page_none_selected": "æœĒ選擇", "backup_controller_page_remainder": "削餘", @@ -545,7 +545,7 @@ "backup_controller_page_start_backup": "開始備äģŊ", "backup_controller_page_status_off": "前台č‡Ē動備äģŊåˇ˛é—œé–‰", "backup_controller_page_status_on": "前台č‡Ē動備äģŊ厞開啓", - "backup_controller_page_storage_format": " {} / {} 厞äŊŋᔍ", + "backup_controller_page_storage_format": "{used} / {total} 厞äŊŋᔍ", "backup_controller_page_to_backup": "čρ備äģŊįš„į›¸į°ŋ", "backup_controller_page_total_sub": "遏䏭ᛏį°ŋä¸­æ‰€æœ‰ä¸é‡č¤‡įš„įŸ­į‰‡å’Œåœ–į‰‡", "backup_controller_page_turn_off": "關閉前台備äģŊ", @@ -558,7 +558,7 @@ "backup_manual_success": "成功", "backup_manual_title": "ä¸Šå‚ŗį‹€æ…‹", "backup_options_page_title": "備äģŊ選項", - "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_setting_subtitle": "įŽĄį†åžŒå°čˆ‡å‰å°ä¸Šå‚ŗč¨­åŽš", "backward": "倒čŊ‰", "birthdate_saved": "å‡ēį”Ÿæ—ĨæœŸå„˛å­˜æˆåŠŸ", "birthdate_set_description": "å‡ēį”Ÿæ—ĨæœŸæœƒį”¨äž†č¨ˆįŽ—æ­¤äēēæ‹į…§æ™‚įš„æ­˛æ•¸ã€‚", @@ -570,21 +570,21 @@ "bulk_keep_duplicates_confirmation": "您įĸē厚čρäŋį•™ {count, plural, one {# å€‹é‡č¤‡æĒ”æĄˆ} other {# å€‹é‡č¤‡æĒ”æĄˆ}} 嗎īŧŸé€™å°‡č§Ŗæąēæ‰€æœ‰é‡č¤‡įĩ„č€Œä¸åˆĒ除äģģäŊ•內厚。", "bulk_trash_duplicates_confirmation": "įĸē厚čρ䏀æŦĄä¸ŸæŽ‰ {count, plural, other {# å€‹é‡č¤‡įš„æĒ”æĄˆ}}嗎īŧŸé€™æ¨Ŗæ¯įĩ„é‡č¤‡įš„æĒ”æĄˆä¸­īŧŒæœ€å¤§įš„æœƒį•™ä¸‹äž†īŧŒå…ļåŽƒįš„æœƒčĸĢä¸Ÿé€˛åžƒåœžæĄļ。", "buy": "čŗŧįŊŽ Immich", - "cache_settings_album_thumbnails": "圖åēĢį¸Žåœ–īŧˆ {} 項īŧ‰", + "cache_settings_album_thumbnails": "圖åēĢį¸Žåœ–īŧˆ{count} 項īŧ‰", "cache_settings_clear_cache_button": "æ¸…é™¤įˇŠå­˜", "cache_settings_clear_cache_button_title": "清除åĨ—į”¨įˇŠå­˜ã€‚åœ¨é‡æ–°į”ŸæˆįˇŠå­˜äš‹å‰īŧŒå°‡éĄ¯č‘—åŊąéŸŋåĨ—į”¨įš„æ€§čƒŊ。", "cache_settings_duplicated_assets_clear_button": "清除", "cache_settings_duplicated_assets_subtitle": "厞加å…Ĩéģ‘åå–Žįš„į…§į‰‡å’ŒįŸ­į‰‡", - "cache_settings_duplicated_assets_title": "é‡č¤‡é …į›Žīŧˆ {} īŧ‰", - "cache_settings_image_cache_size": "åœ–į‰‡įˇŠå­˜å¤§å°īŧˆ {} 項īŧ‰", + "cache_settings_duplicated_assets_title": "é‡č¤‡é …į›Žīŧˆ{count}īŧ‰", + "cache_settings_image_cache_size": "åœ–į‰‡åŋĢ取大小īŧˆ{count} 項īŧ‰", "cache_settings_statistics_album": "圖åēĢį¸Žåœ–", - "cache_settings_statistics_assets": " {} 項īŧˆ {} īŧ‰", + "cache_settings_statistics_assets": "{count} 項 ({size})", "cache_settings_statistics_full": "åŽŒæ•´åœ–į‰‡", "cache_settings_statistics_shared": "å…ąäēĢᛏį°ŋį¸Žåœ–", "cache_settings_statistics_thumbnail": "į¸Žåœ–", "cache_settings_statistics_title": "įˇŠå­˜äŊŋį”¨æƒ…æŗ", "cache_settings_subtitle": "控åˆļ Immich app įš„įˇŠå­˜čĄŒį‚ē", - "cache_settings_thumbnail_size": "į¸Žåœ–įˇŠå­˜å¤§å°īŧˆ {} 項īŧ‰", + "cache_settings_thumbnail_size": "į¸Žåœ–åŋĢ取大小({count} 項)", "cache_settings_tile_subtitle": "č¨­åŽšæœŦåœ°å­˜å„˛čĄŒį‚ē", "cache_settings_tile_title": "æœŦåœ°å­˜å„˛", "cache_settings_title": "įˇŠå­˜č¨­åŽš", @@ -593,12 +593,12 @@ "camera_model": "į›¸æŠŸåž‹č™Ÿ", "cancel": "取æļˆ", "cancel_search": "取æļˆæœå°‹", - "canceled": "Canceled", + "canceled": "åˇ˛å–æļˆ", "cannot_merge_people": "į„Ąæŗ•åˆäŊĩäēēį‰Š", "cannot_undo_this_action": "æ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", "cannot_update_the_description": "į„Ąæŗ•æ›´æ–°æčŋ°", "change_date": "更攚æ—Ĩ期", - "change_display_order": "Change display order", + "change_display_order": "æ›´æ›éĄ¯į¤ē順åē", "change_expiration_time": "æ›´æ”šå¤ąæ•ˆæœŸé™", "change_location": "更攚äŊįŊŽ", "change_name": "攚名", @@ -606,16 +606,17 @@ "change_password": "更攚密įĸŧ", "change_password_description": "這是您įŦŦ一æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–您čĸĢčĻæą‚æ›´æ”šå¯†įĸŧ。čĢ‹åœ¨ä¸‹éĸčŧ¸å…Ĩ新密įĸŧ。", "change_password_form_confirm_password": "įĸēčĒå¯†įĸŧ", - "change_password_form_description": "您åĨŊ {name} īŧš\n\n這是您éĻ–æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–čĸĢįŽĄį†å“ĄčĻæą‚æ›´æ”šå¯†įĸŧ。\nčĢ‹åœ¨ä¸‹æ–ščŧ¸å…Ĩ新密įĸŧ。", + "change_password_form_description": "您åĨŊ {name} īŧš\n\n這是您éĻ–æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–čĸĢįŽĄį†å“ĄčĻæą‚æ›´æ”šå¯†įĸŧ。čĢ‹åœ¨ä¸‹æ–ščŧ¸å…Ĩ新密įĸŧ。", "change_password_form_new_password": "新密įĸŧ", "change_password_form_password_mismatch": "密įĸŧ不一致", "change_password_form_reenter_new_password": "再æŦĄčŧ¸å…Ĩ新密įĸŧ", + "change_pin_code": "更攚PINįĸŧ", "change_your_password": "æ›´æ”šæ‚¨įš„å¯†įĸŧ", "changed_visibility_successfully": "åˇ˛æˆåŠŸæ›´æ”šå¯čĻ‹æ€§", "check_all": "全選", - "check_corrupt_asset_backup": "Check for corrupt asset backups", - "check_corrupt_asset_backup_button": "Perform check", - "check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.", + "check_corrupt_asset_backup": "æĒĸæŸĨææ¯€įš„å‚™äģŊé …į›Ž", + "check_corrupt_asset_backup_button": "åŸˇčĄŒæĒĸæŸĨ", + "check_corrupt_asset_backup_description": "åƒ…åœ¨åˇ˛å‚™äģŊæ‰€æœ‰é …į›Žä¸”é€ŖæŽĨ Wi-Fi æ™‚åŸˇčĄŒæ­¤æĒĸæŸĨã€‚æ­¤į¨‹åēå¯čƒŊ需čĻåšžåˆ†é˜ã€‚", "check_logs": "æĒĸæŸĨæ—Ĩčnj", "choose_matching_people_to_merge": "選擇čρ合äŊĩįš„åŒšé…äēēį‰Š", "city": "城市", @@ -644,23 +645,24 @@ "comments_are_disabled": "į•™č¨€åˇ˛åœį”¨", "common_create_new_album": "新åĸžį›¸į°ŋ", "common_server_error": "čĢ‹æĒĸæŸĨæ‚¨įš„įļ˛įĩĄé€ŖæŽĨīŧŒįĸēäŋäŧ翜å™¨å¯é€ŖæŽĨīŧŒä¸”æœŦፋåŧčˆ‡äŧ翜å™¨į‰ˆæœŦå…ŧ厚。", - "completed": "Completed", + "completed": "åˇ˛åŽŒæˆ", "confirm": "įĸēčĒ", "confirm_admin_password": "įĸēčĒįŽĄį†č€…å¯†įĸŧ", "confirm_delete_face": "您įĸē厚čĻåžžé …į›Žä¸­åˆĒ除 {name} įš„č‡‰å­”å—ŽīŧŸ", "confirm_delete_shared_link": "įĸē厚åˆĒ除逪įĩå—ŽīŧŸ", "confirm_keep_this_delete_others": "æ‰€æœ‰įš„å…ļäģ–å †į–Šé …į›Žå°‡čĸĢåˆĒ除。įĸē厚įšŧįēŒå—ŽīŧŸ", + "confirm_new_pin_code": "įĸēčĒæ–° PIN įĸŧ", "confirm_password": "įĸēčĒå¯†įĸŧ", "contain": "包åĢ", "context": "情åĸƒ", "continue": "įšŧįēŒ", - "control_bottom_app_bar_album_info_shared": " {} 項 ¡ åˇ˛å…ąäēĢ", + "control_bottom_app_bar_album_info_shared": "{count} 項 ¡ åˇ˛å…ąäēĢ", "control_bottom_app_bar_create_new_album": "新åĸžį›¸į°ŋ", "control_bottom_app_bar_delete_from_immich": "åžžImmichäŧ翜å™¨ä¸­åˆĒ除", "control_bottom_app_bar_delete_from_local": "åžžį§ģå‹•čŖįŊŽä¸­åˆĒ除", "control_bottom_app_bar_edit_location": "ᎍčŧ¯äŊįŊŽčŗ‡č¨Š", "control_bottom_app_bar_edit_time": "ᎍčŧ¯æ—Ĩ期和時間", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "分äēĢ逪įĩ", "control_bottom_app_bar_share_to": "į™ŧ送įĩĻ", "control_bottom_app_bar_trash_from_immich": "攞å…Ĩ回æ”ļæĄļ", "copied_image_to_clipboard": "åœ–į‰‡åˇ˛č¤‡čŖŊ到å‰Ēč˛ŧį°ŋ。", @@ -695,16 +697,14 @@ "crop": "誁å‰Ē", "curated_object_page_title": "äē‹į‰Š", "current_device": "æ­¤čŖįŊŽ", - "current_server_address": "Current server address", + "current_pin_code": "į•ļ前 PIN įĸŧ", + "current_server_address": "į›Žå‰įš„äŧ翜å™¨äŊå€", "custom_locale": "č‡Ēč¨‚å€åŸŸ", "custom_locale_description": "䞝čĒžč¨€å’Œå€åŸŸč¨­åŽšæ—Ĩ期和數字æ ŧåŧ", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "æˇąč‰˛", "date_after": "æ—Ĩ期䚋垌", "date_and_time": "æ—ĨæœŸčˆ‡æ™‚é–“", "date_before": "æ—Ĩ期䚋前", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "å‡ēį”Ÿæ—ĨæœŸå„˛å­˜æˆåŠŸ", "date_range": "æ—ĨæœŸį¯„åœ", "day": "æ—Ĩ", @@ -746,7 +746,7 @@ "direction": "斚向", "disabled": "åœį”¨", "disallow_edits": "ä¸å…č¨ąįˇ¨čŧ¯", - "discord": "Discord", + "discord": "Discord į¤žįž¤", "discover": "æŽĸį´ĸ", "dismiss_all_errors": "åŋŊį•Ĩ所有錯čǤ", "dismiss_error": "åŋŊį•Ĩ錯čǤ", @@ -763,7 +763,7 @@ "download_enqueue": "厞加å…Ĩ下čŧ‰éšŠåˆ—", "download_error": "下čŧ‰å‡ē錯", "download_failed": "下čŧ‰å¤ąæ•—", - "download_filename": "文äģļīŧš {} ", + "download_filename": "文äģļ: {filename}", "download_finished": "下čŧ‰åŽŒæˆ", "download_include_embedded_motion_videos": "åĩŒå…ĨåŊąį‰‡", "download_include_embedded_motion_videos_description": "把åĩŒå…Ĩå‹•æ…‹į…§į‰‡įš„åŊąį‰‡äŊœį‚ēå–Žį¨įš„æĒ”æĄˆåŒ…åĢ在內", @@ -807,19 +807,20 @@ "editor_crop_tool_h2_aspect_ratios": "長å¯Ŧ比", "editor_crop_tool_h2_rotation": "旋čŊ‰", "email": "é›ģ子éƒĩäģļ", - "empty_folder": "This folder is empty", + "email_notifications": "Email 通įŸĨ", + "empty_folder": "æ­¤čŗ‡æ–™å¤žį‚ēįŠē", "empty_trash": "清įŠē垃圞æĄļ", "empty_trash_confirmation": "įĸē厚čĻæ¸…įŠē垃圞æĄļ嗎īŧŸé€™æœƒæ°¸äš…åˆĒ除 Immich 垃圞æĄļä¸­æ‰€æœ‰įš„æĒ”æĄˆã€‚\næ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", "enable": "å•Ÿį”¨", "enabled": "åˇąå•Ÿį”¨", "end_date": "įĩæŸæ—Ĩ期", - "enqueued": "Enqueued", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "排å…ĨäŊ‡åˆ—中", + "enter_wifi_name": "čŧ¸å…Ĩ Wi-Fi åį¨ą", "error": "錯čǤ", - "error_change_sort_album": "Failed to change album sort order", + "error_change_sort_album": "į„Ąæŗ•æ”ščŽŠį›¸į°ŋ排åē", "error_delete_face": "åžžé …į›Žä¸­åˆĒé™¤č‡‰å­”æ™‚į™ŧį”ŸéŒ¯čǤ", "error_loading_image": "čŧ‰å…Ĩåœ–į‰‡æ™‚å‡ē錯", - "error_saving_image": "錯čǤīŧš {} ", + "error_saving_image": "錯čǤ: {error}", "error_title": "錯čǤ - å‡ēå•éĄŒäē†", "errors": { "cannot_navigate_next_asset": "į„Ąæŗ•į€čĻŊ下一個æĒ”æĄˆ", @@ -841,7 +842,7 @@ "error_removing_assets_from_album": "åžžį›¸į°ŋ中į§ģ除æĒ”æĄˆæ™‚å‡ē錯äē†īŧŒčĢ‹åˆ°æŽ§åˆļč‡ēįž­č§ŖčŠŗį´°čŗ‡č¨Š", "error_selecting_all_assets": "選擇所有æĒ”æĄˆæ™‚å‡ē錯", "exclusion_pattern_already_exists": "æ­¤æŽ’é™¤æ¨Ąåŧåˇ˛å­˜åœ¨ã€‚", - "failed_job_command": "å‘Ŋäģ¤ {command} åŸˇčĄŒå¤ąæ•—īŧŒäŊœæĨ­īŧš{job}", + "failed_job_command": "åŸˇčĄŒ {command} å‘Ŋäģ¤äģģ務錯čǤīŧš {job}", "failed_to_create_album": "ᛏį°ŋåģēįĢ‹å¤ąæ•—", "failed_to_create_shared_link": "åģēįĢ‹å…ąäēĢ逪įĩå¤ąæ•—", "failed_to_edit_shared_link": "ᎍčŧ¯å…ąäēĢ逪įĩå¤ąæ•—", @@ -849,10 +850,12 @@ "failed_to_keep_this_delete_others": "į„Ąæŗ•äŋį•™æ­¤é …į›Žä¸ĻåˆĒ除å…ļäģ–é …į›Ž", "failed_to_load_asset": "æĒ”æĄˆčŧ‰å…Ĩå¤ąæ•—", "failed_to_load_assets": "æĒ”æĄˆčŧ‰å…Ĩå¤ąæ•—", + "failed_to_load_notifications": "į„Ąæŗ•čŧ‰å…Ĩ通įŸĨ", "failed_to_load_people": "į„Ąæŗ•čŧ‰å…Ĩäēēį‰Š", "failed_to_remove_product_key": "į„Ąæŗ•į§ģ除į”ĸ品密鑰", "failed_to_stack_assets": "į„Ąæŗ•å †į–ŠæĒ”æĄˆ", "failed_to_unstack_assets": "į„Ąæŗ•č§Ŗé™¤å †į–ŠæĒ”æĄˆ", + "failed_to_update_notification_status": "į„Ąæŗ•æ›´æ–°é€šįŸĨį‹€æ…‹", "import_path_already_exists": "此匯å…Ĩčˇ¯åž‘åˇ˛å­˜åœ¨ã€‚", "incorrect_email_or_password": "é›ģ子éƒĩäģ￈–密įĸŧ有čǤ", "paths_validation_failed": "{paths, plural, one {# å€‹čˇ¯åž‘} other {# å€‹čˇ¯åž‘}} éŠ—č­‰å¤ąæ•—", @@ -864,7 +867,7 @@ "unable_to_add_comment": "į„Ąæŗ•æ–°åĸžį•™č¨€", "unable_to_add_exclusion_pattern": "į„Ąæŗ•æˇģåŠ æŽ’é™¤æ¨Ąåŧ", "unable_to_add_import_path": "į„Ąæŗ•æˇģ加匯å…Ĩčˇ¯åž‘", - "unable_to_add_partners": "į„Ąæŗ•æˇģ加å¤Ĩäŧ´", + "unable_to_add_partners": "į„Ąæŗ•æˇģ加čĻĒæœ‹åĨŊ友", "unable_to_add_remove_archive": "į„Ąæŗ•{archived, select, true {垞封存中į§ģ除æĒ”æĄˆ} other {將æĒ”æĄˆåŠ å…Ĩ封存}}", "unable_to_add_remove_favorites": "į„Ąæŗ•å°‡æĒ”æĄˆ{favorite, select, true {加å…Ĩæ”ļ藏} other {åžžæ”ļ藏中į§ģ除}}", "unable_to_archive_unarchive": "į„Ąæŗ•{archived, select, true {封存} other {取æļˆå°å­˜}}", @@ -916,10 +919,11 @@ "unable_to_remove_assets_from_shared_link": "åˆĒé™¤å…ąäēĢ逪įĩä¸­æĒ”æĄˆå¤ąæ•—", "unable_to_remove_deleted_assets": "į„Ąæŗ•į§ģ除é›ĸ᎚æĒ”æĄˆ", "unable_to_remove_library": "į„Ąæŗ•į§ģé™¤čŗ‡æ–™åēĢ", - "unable_to_remove_partner": "į„Ąæŗ•į§ģ除å¤Ĩäŧ´", + "unable_to_remove_partner": "į„Ąæŗ•į§ģ除čĻĒæœ‹åĨŊ友", "unable_to_remove_reaction": "į„Ąæŗ•į§ģ除反應", "unable_to_repair_items": "į„Ąæŗ•įŗžæ­Ŗé …į›Ž", "unable_to_reset_password": "į„Ąæŗ•é‡č¨­å¯†įĸŧ", + "unable_to_reset_pin_code": "į„Ąæŗ•é‡įŊŽ PIN įĸŧ", "unable_to_resolve_duplicate": "į„Ąæŗ•č§Ŗæąē重複項", "unable_to_restore_assets": "į„Ąæŗ•é‚„åŽŸæĒ”æĄˆ", "unable_to_restore_trash": "į„Ąæŗ•é‚„åŽŸåžƒåœžæĄļä¸­įš„é …į›Ž", @@ -953,10 +957,10 @@ "exif_bottom_sheet_location": "äŊįŊŽ", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "新åĸžå§“名", - "exif_bottom_sheet_person_age": "Age {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "åš´éŊĄ {age}", + "exif_bottom_sheet_person_age_months": "åš´éŊĄ {months} 月", + "exif_bottom_sheet_person_age_year_months": "1 æ­˛ {months} 個月", + "exif_bottom_sheet_person_age_years": "{years} æ­˛", "exit_slideshow": "退å‡ēåšģį‡ˆį‰‡", "expand_all": "åą•é–‹å…¨éƒ¨", "experimental_settings_new_asset_list_subtitle": "æ­Ŗåœ¨č™•į†", @@ -973,12 +977,12 @@ "extension": "副æĒ”名", "external": "外部", "external_libraries": "外部圖åēĢ", - "external_network": "External network", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "外部įļ˛čˇ¯", + "external_network_sheet_info": "č‹Ĩį„Ąæŗ•äŊŋį”¨ååĨŊįš„ Wi-FiīŧŒå°‡äžåˆ—čĄ¨åžžä¸Šåˆ°ä¸‹é¸æ“‡å¯é€Ŗįˇšįš„äŧ翜å™¨įļ˛å€", "face_unassigned": "æœĒ指厚", - "failed": "Failed", + "failed": "å¤ąæ•—", "failed_to_load_assets": "į„Ąæŗ•åŠ čŧ‰æĒ”æĄˆ", - "failed_to_load_folder": "Failed to load folder", + "failed_to_load_folder": "į„Ąæŗ•čŧ‰å…Ĩčŗ‡æ–™å¤ž", "favorite": "æ”ļ藏", "favorite_or_unfavorite_photo": "æ”ļč—æˆ–å–æļˆæ”ļč—į…§į‰‡", "favorites": "æ”ļ藏", @@ -992,21 +996,22 @@ "filetype": "æĒ”æĄˆéĄžåž‹", "filter": "į¯Šé¸", "filter_people": "į¯Šé¸äēēį‰Š", + "filter_places": "į¯Šé¸åœ°éģž", "find_them_fast": "æœå°‹åį¨ąīŧŒåŋĢ速扞äēē", "fix_incorrect_match": "äŋŽåžŠä¸į›¸įŦĻįš„", - "folder": "Folder", - "folder_not_found": "Folder not found", + "folder": "čŗ‡æ–™å¤ž", + "folder_not_found": "æœĒæ‰žåˆ°čŗ‡æ–™å¤ž", "folders": "čŗ‡æ–™å¤ž", "folders_feature_description": "äģĨčŗ‡æ–™å¤žį€čĻŊæĒ”æĄˆįŗģįĩąä¸­įš„į…§į‰‡å’ŒåŊąį‰‡", "forward": "順åē", "general": "一čˆŦ", "get_help": "įˇšä¸Šæą‚åŠŠ", - "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", + "get_wifiname_error": "į„Ąæŗ•å–åž— Wi-Fi åį¨ąã€‚čĢ‹įĸēčĒæ‚¨åˇ˛æŽˆäēˆåŋ…čĻįš„æŦŠé™īŧŒä¸Ļ厞逪æŽĨ臺 Wi-Fi įļ˛čˇ¯", "getting_started": "開始äŊŋᔍ", "go_back": "čŋ”回", "go_to_folder": "čŊ‰č‡ŗčŗ‡æ–™å¤ž", "go_to_search": "前垀搜尋", - "grant_permission": "Grant permission", + "grant_permission": "į˛åž—æŦŠé™", "group_albums_by": "åˆ†éĄžįž¤įĩ„įš„æ–šåŧ...", "group_country": "按國åŽļ分įĩ„", "group_no": "į„Ąåˆ†įĩ„", @@ -1029,18 +1034,18 @@ "hide_password": "éšąč—å¯†įĸŧ", "hide_person": "隱藏äēēį‰Š", "hide_unnamed_people": "隱藏æœĒå‘Ŋ名äēēį‰Š", - "home_page_add_to_album_conflicts": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} 項。\nå…ļ中 {failed} é …åœ¨į›¸į°ŋä¸­åˇ˛å­˜åœ¨ã€‚", + "home_page_add_to_album_conflicts": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} 項。å…ļ中 {failed} é …åœ¨į›¸į°ŋä¸­åˇ˛å­˜åœ¨ã€‚", "home_page_add_to_album_err_local": "æšĢ不čƒŊ將æœŦåœ°é …į›Žæ–°åĸžåˆ°į›¸į°ŋ中īŧŒį•Ĩ過", - "home_page_add_to_album_success": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} 項。", - "home_page_album_err_partner": "æšĢį„Ąæŗ•å°‡åŒäŧ´įš„é …į›Žæ–°åĸžåˆ°į›¸į°ŋīŧŒį•Ĩ過", - "home_page_archive_err_local": "æšĢį„Ąæŗ•æ­¸æĒ”æœŦåœ°é …į›ŽīŧŒį•Ĩ過", - "home_page_archive_err_partner": "į„Ąæŗ•å­˜æĒ”同äŧ´įš„é …į›ŽīŧŒį•Ĩ過", + "home_page_add_to_album_success": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} 項。", + "home_page_album_err_partner": "æšĢį„Ąæŗ•å°‡čĻĒæœ‹åĨŊå‹įš„é …į›Žæ–°åĸžåˆ°į›¸į°ŋīŧŒį•Ĩ過", + "home_page_archive_err_local": "æšĢį„Ąæŗ•å°å­˜æœŦåœ°é …į›ŽīŧŒį•Ĩ過", + "home_page_archive_err_partner": "į„Ąæŗ•å°å­˜čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", "home_page_building_timeline": "æ­Ŗåœ¨į”Ÿæˆæ™‚é–“įˇš", - "home_page_delete_err_partner": "į„Ąæŗ•åˆĒ除同äŧ´įš„é …į›ŽīŧŒį•Ĩ過", + "home_page_delete_err_partner": "į„Ąæŗ•åˆĒ除čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", "home_page_delete_remote_err_local": "é™čˇé …į›ŽåˆĒé™¤æ¨ĄåŧīŧŒį•Ĩ過æœŦåœ°é …į›Ž", "home_page_favorite_err_local": "æšĢ不čƒŊæ”ļ藏æœŦåœ°é …į›ŽīŧŒį•Ĩ過", - "home_page_favorite_err_partner": "æšĢį„Ąæŗ•æ”ļč—åŒäŧ´įš„é …į›ŽīŧŒį•Ĩ過", - "home_page_first_time_notice": "åĻ‚æžœé€™æ˜¯æ‚¨įŦŦ一æŦĄäŊŋᔍæœŦፋåŧīŧŒčĢ‹įĸēäŋé¸æ“‡ä¸€å€‹čρ備äģŊįš„æœŦåœ°į›¸į°ŋīŧŒäģĨäžŋ可äģĨåœ¨æ™‚é–“įˇšä¸­é čĻŊčŠ˛į›¸į°ŋä¸­įš„į…§į‰‡å’ŒįŸ­į‰‡ã€‚", + "home_page_favorite_err_partner": "æšĢį„Ąæŗ•æ”ļ藏čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", + "home_page_first_time_notice": "åĻ‚æžœé€™æ˜¯æ‚¨įŦŦ一æŦĄäŊŋᔍæœŦፋåŧīŧŒčĢ‹įĸēäŋé¸æ“‡ä¸€å€‹čρ備äģŊįš„æœŦåœ°į›¸į°ŋīŧŒäģĨäžŋ可äģĨåœ¨æ™‚é–“įˇšä¸­é čĻŊčŠ˛į›¸į°ŋä¸­įš„į…§į‰‡å’ŒįŸ­į‰‡", "home_page_share_err_local": "æšĢį„Ąæŗ•é€šéŽéˆæŽĨå…ąäēĢæœŦåœ°é …į›ŽīŧŒį•Ĩ過", "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊä¸Šå‚ŗ 30 å€‹é …į›ŽīŧŒį•Ĩ過", "host": "ä¸ģ抟", @@ -1070,7 +1075,7 @@ "in_archive": "厞封存", "include_archived": "包åĢ厞封存", "include_shared_albums": "包åĢå…ąäēĢᛏį°ŋ", - "include_shared_partner_assets": "包æ‹Ŧå…ąäēĢå¤Ĩäŧ´æĒ”æĄˆ", + "include_shared_partner_assets": "包æ‹Ŧå…ąäēĢčĻĒæœ‹åĨŊ友æĒ”æĄˆ", "individual_share": "個åˆĨ分äēĢ", "individual_shares": "個åˆĨ分äēĢ", "info": "čŗ‡č¨Š", @@ -1117,10 +1122,9 @@ "list": "åˆ—čĄ¨", "loading": "čŧ‰å…Ĩ中", "loading_search_results_failed": "čŧ‰å…Ĩ搜尋įĩæžœå¤ąæ•—", - "local_network": "Local network", - "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", - "location_permission": "Location permission", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_network_sheet_info": "į•ļäŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įļ˛čˇ¯æ™‚īŧŒæ‡‰į”¨į¨‹åŧå°‡é€éŽæ­¤é€Ŗįĩé€Ŗįˇšč‡ŗäŧ翜å™¨", + "location_permission": "厚äŊæŦŠé™", + "location_permission_content": "į‚ēäē†äŊŋᔍč‡Ē動切換功čƒŊīŧŒImmich 需čĻį˛žįĸēįš„åŽšäŊæŦŠé™īŧŒäģĨäžŋčŽ€å–į›Žå‰æ‰€é€ŖæŽĨįš„ Wi-Fi įļ˛čˇ¯åį¨ą", "location_picker_choose_on_map": "在地圖上選擇", "location_picker_latitude_error": "čŧ¸å…Ĩæœ‰æ•ˆįš„įˇ¯åēĻå€ŧ", "location_picker_latitude_hint": "čĢ‹åœ¨æ­¤č™•čŧ¸å…Ĩæ‚¨įš„įˇ¯åēĻå€ŧ", @@ -1134,7 +1138,6 @@ "login_disabled": "厞įρᔍį™ģå…Ĩ", "login_form_api_exception": "API ᕰ叏īŧŒčĢ‹æĒĸæŸĨäŧ翜å™¨åœ°å€ä¸Ļ重čŠĻ。", "login_form_back_button_text": "垌退", - "login_form_email_hint": "youremail@email.com", "login_form_endpoint_hint": "http://æ‚¨įš„äŧ翜å™¨åœ°å€:įĢ¯åŖ", "login_form_endpoint_url": "äŧ翜å™¨éˆæŽĨ地址", "login_form_err_http": "čĢ‹æŗ¨æ˜Ž http:// 或 https://", @@ -1148,7 +1151,7 @@ "login_form_handshake_exception": "與äŧ翜å™¨é€šäŋĄæ™‚å‡ēįžæĄæ‰‹į•°å¸¸ã€‚åĻ‚æžœæ‚¨äŊŋį”¨įš„æ˜¯č‡Ēį°Ŋåč­‰æ›¸īŧŒčĢ‹åœ¨č¨­åŽšä¸­å•“į”¨č‡Ēį°Ŋåč­‰æ›¸æ”¯æŒã€‚", "login_form_password_hint": "密įĸŧ", "login_form_save_login": "äŋæŒį™ģå…Ĩ", - "login_form_server_empty": "čŧ¸å…Ĩäŧ翜å™¨åœ°å€", + "login_form_server_empty": "čŧ¸å…Ĩäŧ翜å™¨é€Ŗįĩã€‚", "login_form_server_error": "į„Ąæŗ•é€ŖæŽĨ到äŧ翜å™¨ã€‚", "login_has_been_disabled": "åˇ˛åœį”¨į™ģå…Ĩ功čƒŊ。", "login_password_changed_error": "密įĸŧæ›´æ–°å¤ąæ•—", @@ -1163,15 +1166,15 @@ "main_menu": "ä¸ģ頁éĸ", "make": "čŖŊ造商", "manage_shared_links": "įŽĄį†å…ąäēĢ逪įĩ", - "manage_sharing_with_partners": "įŽĄį†čˆ‡å¤Ĩäŧ´įš„分äēĢ", + "manage_sharing_with_partners": "įŽĄį†čˆ‡čĻĒæœ‹åĨŊå‹įš„åˆ†äēĢ", "manage_the_app_settings": "įŽĄį†æ‡‰į”¨į¨‹åŧč¨­åޚ", "manage_your_account": "įŽĄį†æ‚¨įš„å¸ŗč™Ÿ", "manage_your_api_keys": "įŽĄį†æ‚¨įš„ API 金鑰", "manage_your_devices": "įŽĄį†åˇ˛į™ģå…Ĩįš„čŖįŊŽ", "manage_your_oauth_connection": "įŽĄį†æ‚¨įš„ OAuth 逪æŽĨ", "map": "地圖", - "map_assets_in_bound": " {} åŧĩᅧቇ", - "map_assets_in_bounds": " {} åŧĩᅧቇ", + "map_assets_in_bound": "{count} åŧĩᅧቇ", + "map_assets_in_bounds": "{count} åŧĩᅧቇ", "map_cannot_get_user_location": "į„Ąæŗ•į˛å–į”¨æˆļäŊįŊŽ", "map_location_dialog_yes": "įĸē厚", "map_location_picker_page_use_location": "äŊŋį”¨æ­¤äŊįŊŽ", @@ -1185,15 +1188,18 @@ "map_settings": "åœ°åœ–č¨­åŽš", "map_settings_dark_mode": "æˇąč‰˛æ¨Ąåŧ", "map_settings_date_range_option_day": "過åŽģ24小時", - "map_settings_date_range_option_days": " {} 夊前", + "map_settings_date_range_option_days": "{days} 夊前", "map_settings_date_range_option_year": "1嚴前", - "map_settings_date_range_option_years": " {} 嚴前", + "map_settings_date_range_option_years": "{years} 嚴前", "map_settings_dialog_title": "åœ°åœ–č¨­åŽš", - "map_settings_include_show_archived": "包æ‹Ŧåˇ˛æ­¸æĒ”é …į›Ž", - "map_settings_include_show_partners": "包åĢå¤Ĩäŧ´", + "map_settings_include_show_archived": "包æ‹Ŧåˇ˛å°å­˜é …į›Ž", + "map_settings_include_show_partners": "包åĢčĻĒæœ‹åĨŊ友", "map_settings_only_show_favorites": "åƒ…éĄ¯į¤ēæ”ļč—įš„é …į›Ž", "map_settings_theme_settings": "地圖ä¸ģ題", "map_zoom_to_see_photos": "į¸Žå°äģĨæŸĨįœ‹é …į›Ž", + "mark_all_as_read": "å…¨éƒ¨æ¨™č¨˜į‚ēåˇ˛čŽ€", + "mark_as_read": "æ¨™č¨˜į‚ēåˇ˛čŽ€", + "marked_all_as_read": "åˇ˛å…¨éƒ¨æ¨™č¨˜į‚ēåˇ˛čŽ€", "matches": "ᛏįŦĻ", "media_type": "åĒ’éĢ”éĄžåž‹", "memories": "回æ†ļ", @@ -1202,8 +1208,6 @@ "memories_setting_description": "įŽĄį†æ‚¨įš„å›žæ†ļä¸­éĄ¯į¤ēįš„å…§åŽš", "memories_start_over": "å†įœ‹ä¸€æŦĄ", "memories_swipe_to_close": "上æģ‘關閉", - "memories_year_ago": "1嚴前", - "memories_years_ago": " {} 嚴前", "memory": "回æ†ļ", "memory_lane_title": "回æ†ļ長åģŠ{title}", "menu": "選喎", @@ -1218,8 +1222,9 @@ "missing": "éēå¤ąįš„", "model": "åž‹č™Ÿ", "month": "月", - "monthly_title_text_date_format": "MMMM y", "more": "更多", + "moved_to_archive": "厞封存 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", + "moved_to_library": "厞į§ģ動 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}} 臺ᛏį°ŋ", "moved_to_trash": "åˇ˛ä¸Ÿé€˛åžƒåœžæĄļ", "multiselect_grid_edit_date_time_err_read_only": "į„Ąæŗ•įˇ¨čŧ¯å”¯čŽ€é …į›Žįš„æ—Ĩ期īŧŒį•Ĩ過", "multiselect_grid_edit_gps_err_read_only": "į„Ąæŗ•įˇ¨čŧ¯å”¯čŽ€é …į›Žįš„äŊįŊŽčŗ‡č¨ŠīŧŒį•Ĩ過", @@ -1227,13 +1232,14 @@ "my_albums": "æˆ‘įš„į›¸į°ŋ", "name": "åį¨ą", "name_or_nickname": "åį¨ąæˆ–æšąį¨ą", - "networking_settings": "Networking", - "networking_subtitle": "Manage the server endpoint settings", + "networking_settings": "įļ˛čˇ¯", + "networking_subtitle": "įŽĄį†äŧ翜å™¨į̝éģžč¨­åޚ", "never": "æ°¸ä¸å¤ąæ•ˆ", "new_album": "æ–°į›¸į°ŋ", "new_api_key": "æ–°įš„ API 金鑰", "new_password": "新密įĸŧ", "new_person": "æ–°įš„äēēį‰Š", + "new_pin_code": "新 PIN įĸŧ", "new_user_created": "厞åģēįĢ‹æ–°äŊŋᔍ者", "new_version_available": "æ–°į‰ˆæœŦ厞į™ŧ布", "newest_first": "最新å„Ē先", @@ -1252,13 +1258,15 @@ "no_favorites_message": "加å…Ĩæ”ļ藏īŧŒåŠ é€Ÿå°‹æ‰žåŊąåƒ", "no_libraries_message": "åģēįĢ‹å¤–éƒ¨åœ–åēĢ來æŸĨįœ‹æ‚¨įš„į…§į‰‡å’ŒåŊąį‰‡", "no_name": "į„Ąå", + "no_notifications": "æ˛’æœ‰é€šįŸĨ", + "no_people_found": "扞不到įŦĻåˆįš„äēēį‰Š", "no_places": "æ˛’æœ‰åœ°éģž", "no_results": "æ˛’æœ‰įĩæžœ", "no_results_description": "čŠĻčŠĻåŒįžŠčŠžæˆ–æ›´é€šį”¨įš„é—œéĩ字吧", "no_shared_albums_message": "åģēį̋ᛏį°ŋ分äēĢį…§į‰‡å’ŒåŊąį‰‡", "not_in_any_album": "不在äģģäŊ•ᛏį°ŋ中", - "not_selected": "Not selected", - "note_apply_storage_label_to_previously_uploaded assets": "č¨ģīŧščĻå°‡å„˛å­˜æ¨™įą¤į”¨æ–ŧå…ˆå‰ä¸Šå‚ŗįš„æĒ”æĄˆīŧŒčĢ‹åŸˇčĄŒ", + "not_selected": "æœĒ選擇", + "note_apply_storage_label_to_previously_uploaded assets": "*č¨ģīŧšåŸˇčĄŒåĨ—į”¨å„˛å­˜æ¨™įą¤å‰å…ˆä¸Šå‚ŗé …į›Ž", "notes": "提į¤ē", "notification_permission_dialog_content": "čĻå•“į”¨é€šįŸĨīŧŒčĢ‹å‰åž€ã€Œč¨­åŽšã€īŧŒä¸Ļé¸æ“‡ã€Œå…č¨ąã€ã€‚", "notification_permission_list_tile_content": "授äēˆé€šįŸĨæŦŠé™ã€‚", @@ -1267,7 +1275,6 @@ "notification_toggle_setting_description": "å•Ÿį”¨é›ģ子éƒĩäģļ通įŸĨ", "notifications": "通įŸĨ", "notifications_setting_description": "įŽĄį†é€šįŸĨ", - "oauth": "OAuth", "official_immich_resources": "厘斚 Immich čŗ‡æē", "offline": "é›ĸ᎚", "offline_paths": "å¤ąæ•ˆčˇ¯åž‘", @@ -1282,6 +1289,7 @@ "onboarding_welcome_user": "æ­ĄčŋŽīŧŒ{user}", "online": "圍᎚", "only_favorites": "åƒ…éĄ¯į¤ēåˇąæ”ļ藏", + "open": "開啟", "open_in_map_view": "開啟地圖æĒĸčĻ–", "open_in_openstreetmap": "ᔍ OpenStreetMap 開啟", "open_the_search_filters": "é–‹å•Ÿæœå°‹į¯Šé¸å™¨", @@ -1294,20 +1302,20 @@ "other_variables": "å…ļäģ–čŽŠæ•¸", "owned": "æˆ‘įš„", "owner": "æ‰€æœ‰č€…", - "partner": "同äŧ´", + "partner": "čĻĒæœ‹åĨŊ友", "partner_can_access": "{partner} 可äģĨ存取", "partner_can_access_assets": "除äē†åˇ˛å°å­˜å’Œåˇ˛åˆĒ除䚋外īŧŒæ‚¨æ‰€æœ‰įš„į…§į‰‡å’ŒåŊąį‰‡", "partner_can_access_location": "æ‚¨į…§į‰‡æ‹æ”įš„äŊįŊŽ", "partner_list_user_photos": "{user} įš„į…§į‰‡", "partner_list_view_all": "åą•į¤ē全部", - "partner_page_empty_message": "æ‚¨įš„į…§į‰‡å°šæœĒ與äģģäŊ•同äŧ´å…ąäēĢ。", + "partner_page_empty_message": "æ‚¨įš„į…§į‰‡å°šæœĒ與äģģäŊ•čĻĒæœ‹åĨŊå‹å…ąäēĢ。", "partner_page_no_more_users": "į„Ąéœ€æ–°åĸžæ›´å¤šį”¨æˆļ", - "partner_page_partner_add_failed": "新åĸžåŒäŧ´å¤ąæ•—", - "partner_page_select_partner": "選擇同äŧ´", + "partner_page_partner_add_failed": "新åĸžčĻĒæœ‹åĨŊå‹å¤ąæ•—", + "partner_page_select_partner": "選擇čĻĒæœ‹åĨŊ友", "partner_page_shared_to_title": "å…ąäēĢįĩĻ", - "partner_page_stop_sharing_content": " {} å°‡į„Ąæŗ•å†å­˜å–æ‚¨įš„į…§į‰‡ã€‚", - "partner_sharing": "å¤Ĩäŧ´åˆ†äēĢ", - "partners": "å¤Ĩäŧ´", + "partner_page_stop_sharing_content": "{partner} å°‡į„Ąæŗ•å†å­˜å–æ‚¨įš„į…§į‰‡ã€‚", + "partner_sharing": "čĻĒæœ‹åĨŊ友分äēĢ", + "partners": "čĻĒæœ‹åĨŊ友", "password": "密įĸŧ", "password_does_not_match": "密įĸŧä¸į›¸įŦĻ", "password_required": "需čρ坆įĸŧ", @@ -1351,6 +1359,9 @@ "photos_count": "{count, plural, other {{count, number} åŧĩᅧቇ}}", "photos_from_previous_years": "åž€åš´įš„į…§į‰‡", "pick_a_location": "選擇äŊįŊŽ", + "pin_code_changed_successfully": "čŽŠæ›´ PIN įĸŧ成功", + "pin_code_reset_successfully": "重įŊŽ PIN įĸŧ成功", + "pin_code_setup_successfully": "č¨­åŽš PIN įĸŧ成功", "place": "地éģž", "places": "地éģž", "places_count": "{count, plural, one {{count, number} 個地éģž} other {{count, number} 個地éģž}}", @@ -1359,7 +1370,7 @@ "play_motion_photo": "æ’­æ”žå‹•æ…‹į…§į‰‡", "play_or_pause_video": "播攞或æšĢ停åŊąį‰‡", "port": "åŸ åŖ", - "preferences_settings_subtitle": "Manage the app's preferences", + "preferences_settings_subtitle": "įŽĄį†æ‡‰į”¨į¨‹åŧååĨŊč¨­åŽš", "preferences_settings_title": "偏åĨŊč¨­åŽš", "preset": "預設", "preview": "預čĻŊ", @@ -1372,7 +1383,6 @@ "profile_drawer_client_out_of_date_major": "åŽĸæˆļįĢ¯æœ‰å¤§į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_out_of_date_minor": "åŽĸæˆļįĢ¯æœ‰å°į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_server_up_to_date": "åŽĸæˆļįĢ¯å’Œæœå‹™į̝éƒŊæ˜¯æœ€æ–°įš„", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "服務įĢ¯æœ‰å¤§į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_server_out_of_date_minor": "服務įĢ¯æœ‰å°į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_image_of_user": "{user} įš„å€‹äēēčŗ‡æ–™åœ–į‰‡", @@ -1381,7 +1391,7 @@ "public_share": "å…Ŧ開分äēĢ", "purchase_account_info": "æ“č­ˇč€…", "purchase_activated_subtitle": "感čŦæ‚¨å° Immich 及開æēčģŸéĢ”įš„æ”¯æ´", - "purchase_activated_time": "æ–ŧ {date, date} å•Ÿį”¨", + "purchase_activated_time": "æ–ŧ {date} å•Ÿį”¨", "purchase_activated_title": "é‡‘é‘°æˆåŠŸå•Ÿį”¨äē†", "purchase_button_activate": "å•Ÿį”¨", "purchase_button_buy": "čŗŧįŊŽ", @@ -1426,6 +1436,8 @@ "recent_searches": "最čŋ‘æœå°‹é …į›Ž", "recently_added": "čŋ‘期新åĸž", "recently_added_page_title": "最čŋ‘æ–°åĸž", + "recently_taken": "最čŋ‘拍攝", + "recently_taken_page_title": "最čŋ‘拍攝", "refresh": "é‡æ–°æ•´į†", "refresh_encoded_videos": "é‡æ–°æ•´į†åˇ˛įˇ¨įĸŧįš„åŊąį‰‡", "refresh_faces": "重整éĸéƒ¨čŗ‡æ–™", @@ -1468,6 +1480,7 @@ "reset": "重設", "reset_password": "é‡č¨­å¯†įĸŧ", "reset_people_visibility": "重設äēēį‰Šå¯čĻ‹æ€§", + "reset_pin_code": "重įŊŽ PIN įĸŧ", "reset_to_default": "é‡č¨­å›žé č¨­", "resolve_duplicates": "č§Ŗæąē重複項", "resolved_all_duplicates": "厞觪æąēæ‰€æœ‰é‡č¤‡é …į›Ž", @@ -1510,7 +1523,7 @@ "search_filter_date_title": "選擇æ—ĨæœŸį¯„åœ", "search_filter_display_option_not_in_album": "ä¸åœ¨į›¸į°ŋ中", "search_filter_display_options": "éĄ¯į¤ē選項", - "search_filter_filename": "Search by file name", + "search_filter_filename": "䞝æĒ”æĄˆåį¨ąæœå°‹", "search_filter_location": "äŊįŊŽ", "search_filter_location_title": "選擇äŊįŊŽ", "search_filter_media_type": "åĒ’éĢ”éĄžåž‹", @@ -1518,17 +1531,17 @@ "search_filter_people_title": "選擇äēēį‰Š", "search_for": "搜尋", "search_for_existing_person": "æœå°‹įžæœ‰įš„äēēį‰Š", - "search_no_more_result": "No more results", + "search_no_more_result": "į„Ąæ›´å¤šįĩæžœ", "search_no_people": "æ˛’æœ‰äē翉žåˆ°", "search_no_people_named": "æ˛’æœ‰åį‚ē「{name}ã€įš„äēēį‰Š", - "search_no_result": "No results found, try a different search term or combination", + "search_no_result": "扞不到įĩæžœīŧŒčĢ‹å˜—čŠĻå…ļäģ–æœå°‹å­—čŠžæˆ–įĩ„合", "search_options": "搜尋選項", "search_page_categories": "類åˆĨ", - "search_page_motion_photos": "å‹•æ…‹į…§į‰‡\n", + "search_page_motion_photos": "å‹•æ…‹į…§į‰‡", "search_page_no_objects": "æ‰žä¸åˆ°į‰Šäģļčŗ‡č¨Š", "search_page_no_places": "扞不到地éģžčŗ‡č¨Š", "search_page_screenshots": "åąåš•æˆĒ圖", - "search_page_search_photos_videos": "Search for your photos and videos", + "search_page_search_photos_videos": "æœå°‹æ‚¨įš„į…§į‰‡å’ŒåŊąį‰‡", "search_page_selfies": "č‡Ē拍", "search_page_things": "äē‹į‰Š", "search_page_view_all_button": "æŸĨįœ‹å…¨éƒ¨", @@ -1540,7 +1553,7 @@ "search_result_page_new_search_hint": "æœå°‹æ–°įš„", "search_settings": "æœå°‹č¨­åŽš", "search_state": "搜尋地區â€Ļ", - "search_suggestion_list_smart_search_hint_1": "éģ˜čĒæƒ…æŗä¸‹å•“į”¨æ™ēčƒŊ搜尋īŧŒčĻæœå°‹ä¸­įšŧ數據īŧŒčĢ‹äŊŋį”¨į›¸é—œčĒžæŗ•", + "search_suggestion_list_smart_search_hint_1": "é č¨­æƒ…æŗä¸‹å•Ÿį”¨æ™ē慧搜尋īŧŒčĻæœå°‹ä¸­įšŧ數據īŧŒčĢ‹äŊŋį”¨į›¸é—œčĒžæŗ• ", "search_suggestion_list_smart_search_hint_2": "m:æ‚¨įš„æœå°‹é—œéĩ詞", "search_tags": "æœå°‹æ¨™įą¤...", "search_timezone": "搜尋時區â€Ļ", @@ -1552,7 +1565,7 @@ "select": "選擇", "select_album_cover": "é¸æ“‡į›¸į°ŋ封éĸ", "select_all": "選擇全部", - "select_all_duplicates": "é¸æ“‡æ‰€æœ‰é‡č¤‡é …", + "select_all_duplicates": "äŋį•™æ‰€æœ‰é‡č¤‡é …", "select_avatar_color": "選擇åŊĸ象顏色", "select_face": "é¸æ“‡č‡‰å­”", "select_featured_photo": "é¸æ“‡į‰šč‰˛į…§į‰‡", @@ -1560,6 +1573,7 @@ "select_keep_all": "全部äŋį•™", "select_library_owner": "選擇圖åēĢæ“æœ‰č€…", "select_new_face": "é¸æ“‡æ–°č‡‰å­”", + "select_person_to_tag": "選擇čĻæ¨™č¨˜įš„äēēį‰Š", "select_photos": "遏ᅧቇ", "select_trash_all": "全部åˆĒ除", "select_user_for_sharing_page_err_album": "新åĸžį›¸į°ŋå¤ąæ•—", @@ -1567,7 +1581,7 @@ "selected_count": "{count, plural, other {選äē† # 項}}", "send_message": "å‚ŗč¨Šæ¯", "send_welcome_email": "å‚ŗé€æ­ĄčŋŽé›ģ子éƒĩäģļ", - "server_endpoint": "Server Endpoint", + "server_endpoint": "äŧ翜å™¨į̝éģž", "server_info_box_app_version": "App į‰ˆæœŦ", "server_info_box_server_url": "äŧ翜å™¨åœ°å€", "server_offline": "äŧ翜å™¨åˇ˛é›ĸ᎚", @@ -1588,28 +1602,29 @@ "setting_image_viewer_preview_title": "čŧ‰å…Ĩ預čĻŊ圖", "setting_image_viewer_title": "åœ–į‰‡", "setting_languages_apply": "åĨ—ᔍ", - "setting_languages_subtitle": "Change the app's language", + "setting_languages_subtitle": "čŽŠæ›´æ‡‰į”¨į¨‹åŧčĒžč¨€", "setting_languages_title": "čĒžč¨€", - "setting_notifications_notify_failures_grace_period": "čƒŒæ™¯å‚™äģŊå¤ąæ•—é€šįŸĨīŧš {} ", - "setting_notifications_notify_hours": " {} 小時", + "setting_notifications_notify_failures_grace_period": "čƒŒæ™¯å‚™äģŊå¤ąæ•—é€šįŸĨīŧš{duration}", + "setting_notifications_notify_hours": "{count} 小時", "setting_notifications_notify_immediately": "įĢ‹åŗ", - "setting_notifications_notify_minutes": " {} 分鐘", + "setting_notifications_notify_minutes": "{count} 分鐘", "setting_notifications_notify_never": "垞不", - "setting_notifications_notify_seconds": " {} į§’", + "setting_notifications_notify_seconds": "{count} į§’", "setting_notifications_single_progress_subtitle": "æ¯é …įš„čŠŗį´°ä¸Šå‚ŗé€˛åēĻčŗ‡č¨Š", "setting_notifications_single_progress_title": "éĄ¯į¤ēčƒŒæ™¯å‚™äģŊčŠŗį´°é€˛åēĻ", "setting_notifications_subtitle": "čĒŋ整通įŸĨ選項", "setting_notifications_total_progress_subtitle": "į¸ŊéĢ”ä¸Šå‚ŗé€˛åēĻ(åˇ˛åŽŒæˆ/į¸Ŋ計)", "setting_notifications_total_progress_title": "éĄ¯į¤ēčƒŒæ™¯å‚™äģŊį¸Ŋ進åēĻ", "setting_video_viewer_looping_title": "åžĒį’°æ’­æ”ž", - "setting_video_viewer_original_video_subtitle": "When streaming a video from the server, play the original even when a transcode is available. May lead to buffering. Videos available locally are played in original quality regardless of this setting.", - "setting_video_viewer_original_video_title": "Force original video", + "setting_video_viewer_original_video_subtitle": "åžžäŧ翜å™¨ä¸˛æĩåŊąį‰‡æ™‚īŧŒæœƒå„Ē先播攞原始į•ĢčŗĒīŧŒåŗäŊŋåˇ˛æœ‰čŊ‰æĒ”į‰ˆæœŦå¯į”¨ã€‚é€™å¯čƒŊæœƒå°Žč‡´æ’­æ”žæ™‚å‡ēįžįˇŠčĄæƒ…æŗã€‚č‹ĨåŊąį‰‡åˇ˛å„˛å­˜åœ¨æœŦ抟īŧŒå‰‡ä¸€åž‹äģĨ原始į•ĢčŗĒ播攞īŧŒčˆ‡æ­¤č¨­åŽšį„Ąé—œã€‚", + "setting_video_viewer_original_video_title": "一型播攞原始åŊąį‰‡", "settings": "č¨­åŽš", "settings_require_restart": "čĢ‹é‡å•“ Immich äģĨäŊŋč¨­åŽšį”Ÿæ•ˆ", "settings_saved": "č¨­åŽšåˇ˛å„˛å­˜", + "setup_pin_code": "č¨­åŽš PIN įĸŧ", "share": "分äēĢ", "share_add_photos": "新åĸžé …į›Ž", - "share_assets_selected": " {} åˇ˛é¸æ“‡", + "share_assets_selected": "{count} åˇ˛é¸æ“‡", "share_dialog_preparing": "æ­Ŗåœ¨æē–å‚™...", "shared": "å…ąäēĢ", "shared_album_activities_input_disable": "厞įĻį”¨čŠ•čĢ–", @@ -1623,34 +1638,33 @@ "shared_by_user": "į”ą {user} 分äēĢ", "shared_by_you": "į”ąäŊ åˆ†äēĢ", "shared_from_partner": "來č‡Ē {partner} įš„į…§į‰‡", - "shared_intent_upload_button_progress_text": "{} / {} Uploaded", + "shared_intent_upload_button_progress_text": "{current} / {total} åˇ˛ä¸Šå‚ŗ", "shared_link_app_bar_title": "å…ąäēĢ鏈æŽĨ", "shared_link_clipboard_copied_massage": "複čŖŊ到å‰Ēč˛ŧæŋ", - "shared_link_clipboard_text": "鏈æŽĨīŧš {} \n密įĸŧīŧš {} ", + "shared_link_clipboard_text": "逪įĩīŧš {link}\n密įĸŧīŧš {password}", "shared_link_create_error": "新åĸžå…ąäēĢ鏈æŽĨå‡ē錯", "shared_link_edit_description_hint": "ᎍčŧ¯å…ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", - "shared_link_edit_expire_after_option_days": " {} 夊", + "shared_link_edit_expire_after_option_days": "{count} 夊", "shared_link_edit_expire_after_option_hour": "1小時", - "shared_link_edit_expire_after_option_hours": " {} 小時", + "shared_link_edit_expire_after_option_hours": "{count} 小時", "shared_link_edit_expire_after_option_minute": "1分鐘", - "shared_link_edit_expire_after_option_minutes": " {} 分鐘", - "shared_link_edit_expire_after_option_months": " {} 個月", - "shared_link_edit_expire_after_option_year": " {} åš´", + "shared_link_edit_expire_after_option_minutes": "{count} 分鐘", + "shared_link_edit_expire_after_option_months": "{count} 個月", + "shared_link_edit_expire_after_option_year": "{count} åš´", "shared_link_edit_password_hint": "čŧ¸å…Ĩå…ąäēĢ密įĸŧ", "shared_link_edit_submit_button": "更新鏈æŽĨ", "shared_link_error_server_url_fetch": "į„Ąæŗ•į˛å–äŧ翜å™¨åœ°å€", - "shared_link_expires_day": " {} 夊垌過期", - "shared_link_expires_days": " {} 夊垌過期", - "shared_link_expires_hour": " {} 小時垌過期", - "shared_link_expires_hours": " {} 小時垌過期", - "shared_link_expires_minute": " {} 分鐘垌過期", - "shared_link_expires_minutes": "將在 {} 分鐘垌過期", + "shared_link_expires_day": "{count} 夊垌過期", + "shared_link_expires_days": "{count} 夊垌過期", + "shared_link_expires_hour": "{count} 小時垌過期", + "shared_link_expires_hours": "{count} 小時垌過期", + "shared_link_expires_minute": "{count} 分鐘垌過期", + "shared_link_expires_minutes": "將在 {count} 分鐘垌過期", "shared_link_expires_never": "永不過期", - "shared_link_expires_second": " {} į§’åžŒéŽæœŸ", - "shared_link_expires_seconds": "將在 {} į§’åžŒéŽæœŸ", + "shared_link_expires_second": "將在 {count} į§’åžŒéŽæœŸ", + "shared_link_expires_seconds": "將在 {count} į§’åžŒéŽæœŸ", "shared_link_individual_shared": "個äēēå…ąäēĢ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "įŽĄį†å…ąäēĢ鏈æŽĨ", "shared_link_options": "å…ąäēĢ逪įĩé¸é …", "shared_links": "å…ąäēĢ逪įĩ", @@ -1665,7 +1679,7 @@ "sharing_page_empty_list": "įŠēį™Ŋ清喎", "sharing_sidebar_description": "在側邊æŦ„éĄ¯į¤ēå…ąäēĢ逪įĩ", "sharing_silver_appbar_create_shared_album": "新åĸžå…ąäēĢᛏį°ŋ", - "sharing_silver_appbar_share_partner": "å…ąäēĢįĩĻ同äŧ´", + "sharing_silver_appbar_share_partner": "å…ąäēĢįĩĻčĻĒæœ‹åĨŊ友", "shift_to_permanent_delete": "按 ⇧ 永䚅åˆĒ除æĒ”æĄˆ", "show_album_options": "éĄ¯į¤ēᛏį°ŋ選項", "show_albums": "éĄ¯į¤ēᛏį°ŋ", @@ -1723,6 +1737,7 @@ "stop_sharing_photos_with_user": "停æ­ĸčˆ‡æ­¤į”¨æˆļå…ąäēĢäŊ įš„ᅧቇ", "storage": "å„˛å­˜įŠē間", "storage_label": "å„˛å­˜æ¨™įą¤", + "storage_quota": "å„˛å­˜įŠē間", "storage_usage": "ᔍäē† {used} / å…ą {available}", "submit": "提äē¤", "suggestions": "åģēč­°", @@ -1735,7 +1750,7 @@ "sync_albums": "同æ­Ĩᛏį°ŋ", "sync_albums_manual_subtitle": "å°‡æ‰€æœ‰ä¸Šå‚ŗįš„įŸ­į‰‡å’Œį…§į‰‡åŒæ­Ĩåˆ°é¸åŽšįš„å‚™äģŊᛏį°ŋ", "sync_upload_album_setting_subtitle": "新åĸžį…§į‰‡å’ŒįŸ­į‰‡ä¸Ļä¸Šå‚ŗåˆ° Immich ä¸Šįš„é¸åŽšį›¸į°ŋ中", - "tag": "æ¨™č¨˜", + "tag": "æ¨™įą¤", "tag_assets": "æ¨™č¨˜æĒ”æĄˆ", "tag_created": "厞åģēįĢ‹æ¨™č¨˜īŧš{tag}", "tag_feature_description": "äģĨ邏čŧ¯æ¨™č¨˜čĻæ—¨åˆ†įĩ„į€čĻŊį…§į‰‡å’ŒåŊąį‰‡", @@ -1749,12 +1764,12 @@ "theme_selection": "ä¸ģ題選項", "theme_selection_description": "äžį€čĻŊ器įŗģįĩąååĨŊč‡Ēå‹•č¨­åŽšæˇąã€æˇē色ä¸ģ題", "theme_setting_asset_list_storage_indicator_title": "åœ¨é …į›Žæ¨™éĄŒä¸ŠéĄ¯į¤ēäŊŋį”¨äš‹å„˛å­˜įŠē間", - "theme_setting_asset_list_tiles_per_row_title": "æ¯čĄŒåą•į¤ē {} 項", - "theme_setting_colorful_interface_subtitle": "åĨ—ᔍä¸ģ色čĒŋåˆ°čƒŒæ™¯", + "theme_setting_asset_list_tiles_per_row_title": "æ¯čĄŒåą•į¤ē {count} 項", + "theme_setting_colorful_interface_subtitle": "åĨ—ᔍä¸ģ色čĒŋåˆ°čƒŒæ™¯ã€‚", "theme_setting_colorful_interface_title": "åŊŠč‰˛į•Œéĸ", "theme_setting_image_viewer_quality_subtitle": "čĒŋ整æŸĨįœ‹å¤§åœ–æ™‚įš„åœ–į‰‡čŗĒ量", "theme_setting_image_viewer_quality_title": "åœ–į‰‡čŗĒ量", - "theme_setting_primary_color_subtitle": "é¸æ“‡éĄč‰˛äŊœį‚ēä¸ģ色čĒŋ", + "theme_setting_primary_color_subtitle": "é¸æ“‡éĄč‰˛äŊœį‚ēä¸ģ色čĒŋ。", "theme_setting_primary_color_title": "ä¸ģ色čĒŋ", "theme_setting_system_primary_color_title": "äŊŋᔍįŗģįĩąéĄč‰˛", "theme_setting_system_theme_switch": "č‡Ē動īŧˆčˇŸéš¨įŗģįĩąč¨­åޚīŧ‰", @@ -1780,17 +1795,19 @@ "trash_all": "全部丟掉", "trash_count": "丟掉 {count, number} 個æĒ”æĄˆ", "trash_delete_asset": "將æĒ”æĄˆä¸Ÿé€˛åžƒåœžæĄļ / åˆĒ除", - "trash_emptied": "åˇ˛æ¸…įŠē回æ”ļæĄļ\n", + "trash_emptied": "åˇ˛æ¸…įŠē回æ”ļæĄļ", "trash_no_results_message": "垃圞æĄļä¸­įš„į…§į‰‡å’ŒåŊąį‰‡å°‡éĄ¯į¤ēåœ¨é€™čŖĄã€‚", "trash_page_delete_all": "åˆĒ除全部", "trash_page_empty_trash_dialog_content": "是åĻ清įŠē回æ”ļæĄļīŧŸé€™äē›é …į›Žå°‡čĸĢåžžImmich中永䚅åˆĒ除", - "trash_page_info": "回æ”ļæĄļä¸­é …į›Žå°‡åœ¨ {} 夊垌永䚅åˆĒ除", + "trash_page_info": "回æ”ļæĄļä¸­é …į›Žå°‡åœ¨ {days} 夊垌永䚅åˆĒ除", "trash_page_no_assets": "æšĢį„Ąåˇ˛åˆĒé™¤é …į›Ž", "trash_page_restore_all": "æĸ垊全部", "trash_page_select_assets_btn": "é¸æ“‡é …į›Ž", - "trash_page_title": "回æ”ļæĄļ ( {} )", + "trash_page_title": "垃圞æĄļ ({count})", "trashed_items_will_be_permanently_deleted_after": "垃圞æĄļä¸­įš„é …į›Žæœƒåœ¨ {days, plural, other {# 夊}}垌永䚅åˆĒ除。", "type": "éĄžåž‹", + "unable_to_change_pin_code": "į„Ąæŗ•čŽŠæ›´ PIN įĸŧ", + "unable_to_setup_pin_code": "į„Ąæŗ•č¨­åŽš PIN įĸŧ", "unarchive": "取æļˆå°å­˜", "unarchived_count": "{count, plural, other {åˇ˛å–æļˆå°å­˜ # å€‹é …į›Ž}}", "unfavorite": "取æļˆæ”ļ藏", @@ -1826,15 +1843,18 @@ "upload_status_errors": "錯čǤ", "upload_status_uploaded": "åˇ˛ä¸Šå‚ŗ", "upload_success": "ä¸Šå‚ŗæˆåŠŸīŧŒčρæŸĨįœ‹æ–°ä¸Šå‚ŗįš„æĒ”æĄˆčĢ‹é‡æ–°æ•´į†é éĸ。", - "upload_to_immich": "Upload to Immich ({})", - "uploading": "Uploading", + "upload_to_immich": "ä¸Šå‚ŗč‡ŗ Immich ({count})", + "uploading": "ä¸Šå‚ŗä¸­", "url": "įļ˛å€", "usage": "į”¨é‡", - "use_current_connection": "use current connection", + "use_current_connection": "äŊŋį”¨į›Žå‰įš„é€Ŗįˇš", "use_custom_date_range": "æ”šį”¨č‡Ē訂æ—ĨæœŸį¯„åœ", "user": "äŊŋᔍ者", + "user_has_been_deleted": "æ­¤į”¨æˆļäģĨčĸĢåˆĒ除", "user_id": "äŊŋᔍ者 ID", "user_liked": "{user} å–œæ­Ąäē† {type, select, photo {這åŧĩᅧቇ} video {這æŽĩåŊąį‰‡} asset {這個æĒ”æĄˆ} other {厃}}", + "user_pin_code_settings": "PIN įĸŧ", + "user_pin_code_settings_description": "įŽĄį†äŊ įš„ PIN įĸŧ", "user_purchase_settings": "čŗŧįŊŽ", "user_purchase_settings_description": "įŽĄį†äŊ įš„čŗŧ財", "user_role_set": "設 {user} į‚ē{role}", @@ -1845,15 +1865,15 @@ "users": "äŊŋᔍ者", "utilities": "åˇĨå…ˇ", "validate": "驗證", - "validate_endpoint_error": "Please enter a valid URL", + "validate_endpoint_error": "čĢ‹čŧ¸å…Ĩæœ‰æ•ˆįš„é€Ŗįĩ", "variables": "čŽŠæ•¸", "version": "į‰ˆæœŦ", "version_announcement_closing": "æ•ŦįĨé †åŋƒīŧŒAlex", "version_announcement_message": "嗨īŊžæ–°į‰ˆæœŦįš„ Immich 推å‡ēäē†ã€‚į‚ē防æ­ĸ配įŊŽå‡ē錯īŧŒčĢ‹čŠąéģžæ™‚間閹莀į™ŧ行čĒĒæ˜ŽīŧŒä¸Ļįĸēäŋč¨­åŽšæ˜¯æœ€æ–°įš„īŧŒį‰šåˆĨ是äŊŋᔍ WatchTower į­‰č‡Ē動更新åˇĨå…ˇæ™‚ã€‚", "version_announcement_overlay_release_notes": "į™ŧ行čĒĒæ˜Ž", "version_announcement_overlay_text_1": "åĨŊæļˆæ¯īŧŒæœ‰æ–°į‰ˆæœŦįš„", - "version_announcement_overlay_text_2": "čĢ‹čŠąéģžæ™‚é–“č¨Ē問", - "version_announcement_overlay_text_3": "ä¸ĻæĒĸæŸĨæ‚¨įš„ docker-compose 和 .env 是åĻį‚ēæœ€æ–°ä¸”æ­Ŗįĸēįš„č¨­åŽšīŧŒį‰šåˆĨ是您在äŊŋᔍ WatchTower æˆ–č€…å…ļäģ–č‡Ēå‹•æ›´æ–°įš„į¨‹åŧæ™‚īŧŒæ‚¨éœ€čĻæ›´åŠ į´°įˇģįš„æĒĸæŸĨ。", + "version_announcement_overlay_text_2": "čĢ‹čŠąéģžæ™‚é–“č¨Ē問 ", + "version_announcement_overlay_text_3": " ä¸ĻæĒĸæŸĨæ‚¨įš„ docker-compose 和 .env 是åĻį‚ēæœ€æ–°ä¸”æ­Ŗįĸēįš„č¨­åŽšīŧŒį‰šåˆĨ是您在äŊŋᔍ WatchTower æˆ–č€…å…ļäģ–č‡Ēå‹•æ›´æ–°įš„į¨‹åŧæ™‚īŧŒæ‚¨éœ€čĻæ›´åŠ äģ”į´°åœ°æĒĸæŸĨ。", "version_announcement_overlay_title": "服務įĢ¯æœ‰æ–°į‰ˆæœŦå•Ļ 🎉", "version_history": "į‰ˆæœŦį´€éŒ„", "version_history_item": "{date} åŽ‰čŖäē† {version}", @@ -1883,11 +1903,11 @@ "week": "週", "welcome": "æ­ĄčŋŽ", "welcome_to_immich": "æ­ĄčŋŽäŊŋᔍ Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Wi-Fi åį¨ą", "year": "åš´", "years_ago": "{years, plural, other {# åš´}}前", "yes": "是", "you_dont_have_any_shared_links": "æ‚¨æ˛’æœ‰äģģäŊ•å…ąäēĢ逪įĩ", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "æ‚¨įš„ Wi-Fi åį¨ą", "zoom_image": "į¸Žæ”žåœ–į‰‡" } diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 20b906ced3..1ba5b98e95 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -14,7 +14,7 @@ "add_a_location": "æˇģ加äŊįŊŽ", "add_a_name": "æˇģåŠ åį§°", "add_a_title": "æˇģ加标éĸ˜", - "add_endpoint": "æˇģåŠ æœåŠĄæŽĨåŖ", + "add_endpoint": "æˇģåŠ æœåŠĄå™¨ URL", "add_exclusion_pattern": "æˇģåŠ æŽ’é™¤č§„åˆ™", "add_import_path": "æˇģ加å¯ŧå…Ĩčˇ¯åž„", "add_location": "æˇģåŠ åœ°į‚š", @@ -26,8 +26,9 @@ "add_to_album": "æˇģåŠ åˆ°į›¸å†Œ", "add_to_album_bottom_sheet_added": "æˇģ加到 {album}", "add_to_album_bottom_sheet_already_exists": "厞圍 {album} 中", + "add_to_locked_folder": "æˇģ加到锁厚文äģļ多", "add_to_shared_album": "æˇģåŠ åˆ°å…ąäēĢį›¸å†Œ", - "add_url": "æˇģ加URL", + "add_url": "æˇģ加 URL", "added_to_archive": "æˇģ加到åŊ’æĄŖ", "added_to_favorites": "æˇģ加到æ”ļ藏", "added_to_favorites_count": "æˇģ加{count, number}éĄšåˆ°æ”ļ藏", @@ -39,11 +40,11 @@ "authentication_settings_disable_all": "įĄŽåŽščρįĻį”¨æ‰€æœ‰įš„į™ģåŊ•æ–šåŧīŧŸč¯Ĩ操äŊœå°†åŽŒå…¨įρæ­ĸį™ģåŊ•。", "authentication_settings_reenable": "åĻ‚éœ€å†æŦĄå¯į”¨īŧŒäŊŋᔍ æœåŠĄå™¨æŒ‡äģ¤ã€‚", "background_task_job": "后台äģģåŠĄ", - "backup_database": "备äģŊ数捎åē“", - "backup_database_enable_description": "å¯į”¨æ•°æŽåē“备äģŊ", - "backup_keep_last_amount": "čρäŋį•™įš„åŽ†å˛å¤‡äģŊ数量", - "backup_settings": "备äģŊ莞įŊŽ", - "backup_settings_description": "įŽĄį†æ•°æŽåē“备äģŊ莞įŊŽ", + "backup_database": "创åģēæ•°æŽåē“备äģŊ", + "backup_database_enable_description": "å¯į”¨æ•°æŽåē“å¯ŧå‡ē备äģŊ", + "backup_keep_last_amount": "čρäŋį•™įš„åŽ†å˛å¯ŧå‡ē数量", + "backup_settings": "数捎åē“å¯ŧå‡ē莞įŊŽ", + "backup_settings_description": "įŽĄį†æ•°æŽåē“备äģŊ莞įŊŽã€‚æŗ¨æ„īŧščŋ™äē›äģģåŠĄä¸äŧščĸĢį›‘æŽ§īŧŒå¤ąč´Ĩ也不äŧšé€šįŸĨ您。", "check_all": "æŖ€æŸĨ全部", "cleanup": "æ¸…į†", "cleared_jobs": "åˇ˛æ¸…į†äģģåŠĄīŧš{job}", @@ -53,6 +54,7 @@ "confirm_email_below": "č¯ˇčž“å…Ĩ“{email}”äģĨčŋ›čĄŒįĄŽčޤ", "confirm_reprocess_all_faces": "įĄŽåŽščĻå¯šå…¨éƒ¨į…§į‰‡é‡æ–°čŋ›čĄŒéĸéƒ¨č¯†åˆĢ吗īŧŸčŋ™å°†åŒæ—ᅬ…é™¤æ‰€æœ‰åˇ˛å‘Ŋ名äēēį‰Šã€‚", "confirm_user_password_reset": "įĄŽåŽščĻé‡įŊŽį”¨æˆˇâ€œ{user}â€įš„å¯†į å—īŧŸ", + "confirm_user_pin_code_reset": "įĄŽåŽščĻé‡įŊŽ{user}įš„PIN᠁吗īŧŸ", "create_job": "创åģēäģģåŠĄ", "cron_expression": "Cron 襨螞åŧ", "cron_expression_description": "äŊŋᔍ Cron æ ŧåŧčŽžįŊŽæ‰Ģ描间隔。更多č¯Ļįģ†äŋĄæ¯č¯ˇå‚阅 Crontab Guru", @@ -181,7 +183,7 @@ "notification_email_sent_test_email_button": "发送æĩ‹č¯•邎äģļåšļäŋå­˜", "notification_email_setting_description": "发送邎äģļ通įŸĨ莞įŊŽ", "notification_email_test_email": "发送æĩ‹č¯•邎äģļ", - "notification_email_test_email_failed": "发送æĩ‹č¯•邎äģļå¤ąč´ĨīŧŒč¯ˇæŖ€æŸĨäŊ čž“å…Ĩįš„äŋĄæ¯", + "notification_email_test_email_failed": "发送æĩ‹č¯•邎äģļå¤ąč´ĨīŧŒč¯ˇæŖ€æŸĨæ‚¨čž“å…Ĩįš„äŋĄæ¯", "notification_email_test_email_sent": "厞向 {email} 发送äē†ä¸€å°æĩ‹č¯•邎äģļīŧŒč¯ˇæŗ¨æ„æŸĨæ”ļ。", "notification_email_username_description": "与邮äģᅵåŠĄå™¨čŋ›čĄŒčēĢäģŊénj蝁æ—ļäŊŋį”¨įš„į”¨æˆˇå", "notification_enable_email_notifications": "å¯į”¨é‚Žäģļ通įŸĨ", @@ -192,26 +194,22 @@ "oauth_auto_register": "č‡ĒåŠ¨æŗ¨å†Œ", "oauth_auto_register_description": "äŊŋᔍ OAuth į™ģåŊ•后č‡ĒåŠ¨æŗ¨å†Œä¸ēæ–°į”¨æˆˇ", "oauth_button_text": "按钎文æœŦ", - "oauth_client_id": "åŽĸæˆˇį̝ ID", - "oauth_client_secret": "åŽĸæˆˇį̝坆é’Ĩ", + "oauth_client_secret_description": "åĻ‚æžœ OAuth 提䞛商不支持 PKCEīŧˆį”¨äēŽäģŖį ä礿ĸįš„č¯æ˜Žå¯†é’Ĩīŧ‰īŧŒåˆ™ä¸ēåŋ…åĄĢ饚", "oauth_enable_description": "äŊŋᔍ OAuth į™ģåŊ•", - "oauth_issuer_url": "提䞛斚 URL", "oauth_mobile_redirect_uri": "į§ģ动įĢ¯é‡åŽšå‘ URI", "oauth_mobile_redirect_uri_override": "į§ģ动įĢ¯é‡åŽšå‘ URI čφᛖ", "oauth_mobile_redirect_uri_override_description": "åŊ“ OAuth æäž›å•†ä¸å…čŽ¸äŊŋᔍį§ģ动 URI æ—ļ吝ᔍīŧŒåĻ‚â€œ'{callback}'”", - "oauth_profile_signing_algorithm": "配įŊŽæ–‡äģļį­žåįŽ—æŗ•", - "oauth_profile_signing_algorithm_description": "ᔍäēŽį­žįŊ˛į”¨æˆˇé…įŊŽæ–‡äģļįš„įŽ—æŗ•ã€‚", - "oauth_scope": "čŒƒå›´", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģåŊ•莞įŊŽ", "oauth_settings_more_details": "å…ŗäēŽæ­¤åŠŸčƒŊįš„æ›´å¤šč¯Ļįģ†äŋĄæ¯īŧŒč¯ˇæŸĨįœ‹į›¸å…ŗæ–‡æĄŖã€‚", - "oauth_signing_algorithm": "į­žåįŽ—æŗ•", "oauth_storage_label_claim": "å­˜å‚¨æ ‡į­žåŖ°æ˜Ž", "oauth_storage_label_claim_description": "č‡ĒåŠ¨å°†į”¨æˆˇįš„å­˜å‚¨æ ‡į­žčŽžįŊŽä¸ēæ­¤éĄšįš„å€ŧ。", "oauth_storage_quota_claim": "存储配éĸåŖ°æ˜Ž", "oauth_storage_quota_claim_description": "č‡ĒåŠ¨å°†į”¨æˆˇįš„å­˜å‚¨é…éĸčŽžįŊŽä¸ēæ­¤éĄšįš„å€ŧ。", "oauth_storage_quota_default": "éģ˜čŽ¤å­˜å‚¨é…éĸīŧˆGBīŧ‰", "oauth_storage_quota_default_description": "æœĒæäž›åŖ°æ˜Žæ—ļäŊŋį”¨įš„é…éĸīŧˆGBīŧ‰īŧˆ0襨į¤ē无限åˆļīŧ‰ã€‚", + "oauth_timeout": "č¯ˇæą‚čļ…æ—ļ", + "oauth_timeout_description": "č¯ˇæą‚čļ…æ—ļīŧˆæ¯Ģį§’īŧ‰", "offline_paths": "įĻģįēŋ文äģļ", "offline_paths_description": "čŋ™å¯čƒŊæ˜¯į”ąäēŽæ‰‹åŠ¨åˆ é™¤äē†ä¸åąžäēŽå¤–部回åē“įš„æ–‡äģļ。", "password_enable_description": "äŊŋį”¨é‚ŽįŽąå’Œå¯†į į™ģåŊ•", @@ -279,7 +277,7 @@ "thumbnail_generation_job": "į”ŸæˆįŧŠį•Ĩ回", "thumbnail_generation_job_description": "ä¸ē每ä¸ĒéĄšį›Žį”Ÿæˆä¸åŒå°ēå¯¸įš„įŧŠį•Ĩ回īŧŒåšļä¸ē每ä¸Ēäēēį‰Šį”ŸæˆįŧŠį•Ĩ回", "transcoding_acceleration_api": "加速器 API", - "transcoding_acceleration_api_description": "čŋ™ä¸Ē API 将äŧšä¸ŽäŊ įš„čŽžå¤‡čŋ›čĄŒäē¤äē’īŧŒäģĨ加速čŊŦ᠁čŋ‡į¨‹ã€‚æ­¤čŽžįŊŽä¸ē“å°ŊåŠ›č€Œä¸ē”——åĻ‚æžœčŊŦį å¤ąč´ĨīŧŒå°†äŧšå›žé€€åˆ°čŊ¯äģļčŊŦį ã€‚VP9 是åĻåˇĨäŊœå–冺äēŽäŊ įš„įĄŦäģļ配įŊŽã€‚", + "transcoding_acceleration_api_description": "čŋ™ä¸Ē API 将äŧšä¸Žæ‚¨įš„čŽžå¤‡čŋ›čĄŒäē¤äē’īŧŒäģĨ加速čŊŦ᠁čŋ‡į¨‹ã€‚æ­¤čŽžįŊŽä¸ē“å°ŊåŠ›č€Œä¸ē”——åĻ‚æžœčŊŦį å¤ąč´ĨīŧŒå°†äŧšå›žé€€åˆ°čŊ¯äģļčŊŦį ã€‚VP9 是åĻåˇĨäŊœå–冺äēŽæ‚¨įš„įĄŦäģļ配įŊŽã€‚", "transcoding_acceleration_nvenc": "NVENCīŧˆéœ€čρ NVIDIA GPUīŧ‰", "transcoding_acceleration_qsv": "Quick Syncīŧˆéœ€čρ Intel 7äģŖåŠäģĨä¸Šįš„ CPUīŧ‰", "transcoding_acceleration_rkmpp": "RKMPPīŧˆäģ…适ᔍäēŽ Rockchip SOCsīŧ‰", @@ -352,6 +350,7 @@ "user_delete_delay_settings_description": "永䚅删除č´ĻæˆˇåŠå…￉€æœ‰éĄšį›Žäš‹å‰æ‰€äŋį•™įš„å¤Šæ•°ã€‚į”¨æˆˇåˆ é™¤äŊœä¸šäŧšåœ¨åˆå¤œæŖ€æŸĨ是åĻæœ‰į”¨æˆˇå¯äģĨ删除。寚č¯Ĩ莞įŊŽįš„æ›´æ”šå°†åœ¨ä¸‹æŦĄæ‰§čĄŒæ—ļį”Ÿæ•ˆã€‚", "user_delete_immediately": "{user}įš„č´ĻæˆˇåŠéĄšį›Žå°†įĢ‹åŗæ°¸äš…åˆ é™¤ã€‚", "user_delete_immediately_checkbox": "įĢ‹åŗåˆ é™¤æŖ€į´ĸåˆ°įš„į”¨æˆˇåŠéĄšį›Ž", + "user_details": "į”¨æˆˇč¯Ļ情", "user_management": "į”¨æˆˇįŽĄį†", "user_password_has_been_reset": "č¯Ĩį”¨æˆˇįš„å¯†į čĸĢ重įŊŽīŧš", "user_password_reset_description": "č¯ˇå‘į”¨æˆˇæäž›ä¸´æ—ļ坆᠁īŧŒåšļ告įŸĨäģ–äģŦ下æŦĄį™ģåŊ•æ—ļ需čĻæ›´æ”šå¯†į ã€‚", @@ -371,17 +370,21 @@ "admin_password": "įŽĄį†å‘˜å¯†į ", "administration": "įŗģįģŸįŽĄį†", "advanced": "é̘įē§", - "advanced_settings_log_level_title": "æ—Ĩåŋ—į­‰įē§īŧš{}", - "advanced_settings_prefer_remote_subtitle": "在某äē›čŽžå¤‡ä¸ŠīŧŒäģŽæœŦåœ°įš„éĄšį›ŽåŠ čŊŊįŧŠį•Ĩå›žįš„é€ŸåēĻ非常æ…ĸ。\nå¯į”¨æ­¤é€‰éĄšäģĨ加čŊŊčŋœį¨‹éĄšį›Žã€‚", + "advanced_settings_enable_alternate_media_filter_subtitle": "äŊŋį”¨æ­¤é€‰éĄšå¯åœ¨åŒæ­Ĩčŋ‡į¨‹ä¸­æ šæŽå¤‡į”¨æĄäģļį­›é€‰éĄšį›Žã€‚äģ…åŊ“您在åē”ᔍፋåēæŖ€æĩ‹æ‰€æœ‰į›¸å†Œå‡é‡åˆ°é—Žéĸ˜æ—￉å°č¯•此功čƒŊ。", + "advanced_settings_enable_alternate_media_filter_title": "[厞énj] äŊŋį”¨å¤‡į”¨įš„čŽžå¤‡į›¸å†ŒåŒæ­Ĩį­›é€‰æĄäģļ", + "advanced_settings_log_level_title": "æ—Ĩåŋ—į­‰įē§: {level}", + "advanced_settings_prefer_remote_subtitle": "在某äē›čŽžå¤‡ä¸ŠīŧŒäģŽæœŦåœ°įš„éĄšį›ŽåŠ čŊŊįŧŠį•Ĩå›žįš„é€ŸåēĻ非常æ…ĸã€‚å¯į”¨æ­¤é€‰éĄšäģĨ加čŊŊčŋœį¨‹éĄšį›Žã€‚", "advanced_settings_prefer_remote_title": "äŧ˜å…ˆčŋœį¨‹éĄšį›Ž", - "advanced_settings_proxy_headers_subtitle": "厚䚉äģŖį†æ ‡å¤´īŧŒåē”ᔍäēŽImmichįš„æ¯æŦĄįŊ‘įģœč¯ˇæą‚", + "advanced_settings_proxy_headers_subtitle": "厚䚉äģŖį†æ ‡å¤´īŧŒåē”ᔍäēŽ Immich įš„æ¯æŦĄįŊ‘įģœč¯ˇæą‚", "advanced_settings_proxy_headers_title": "äģŖį†æ ‡å¤´", - "advanced_settings_self_signed_ssl_subtitle": "莺čŋ‡æœåŠĄå™¨įģˆįģ“į‚šįš„ SSL 蝁äšĻénj蝁īŧˆč¯Ĩé€‰éĄšé€‚į”¨äēŽäŊŋᔍč‡Ēį­žåč¯äšĻįš„æœåŠĄå™¨īŧ‰ã€‚", + "advanced_settings_self_signed_ssl_subtitle": "莺čŋ‡å¯šæœåŠĄå™¨ įš„ SSL 蝁äšĻénj蝁īŧˆč¯Ĩé€‰éĄšé€‚į”¨äēŽäŊŋᔍč‡Ēį­žåč¯äšĻįš„æœåŠĄå™¨īŧ‰ã€‚", "advanced_settings_self_signed_ssl_title": "å…čŽ¸č‡Ēį­žå SSL 蝁äšĻ", + "advanced_settings_sync_remote_deletions_subtitle": "在įŊ‘éĄĩä¸Šæ‰§čĄŒæ“äŊœæ—ļīŧŒč‡Ē动删除或čŋ˜åŽŸč¯ĨčŽžå¤‡ä¸­įš„éĄšį›Ž", + "advanced_settings_sync_remote_deletions_title": "čŋœį¨‹åŒæ­Ĩ删除 [厞énj]", "advanced_settings_tile_subtitle": "é̘įē§į”¨æˆˇčŽžįŊŽ", "advanced_settings_troubleshooting_subtitle": "吝ᔍᔍäēŽæ•…éšœæŽ’é™¤įš„éĸå¤–功čƒŊ", "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, one {#ä¸Ē月} other {#ä¸Ē月}}", + "age_months": "{months, plural, one {#月鞄} other {#月鞄}}", "age_year_months": "1垁{months, plural, one {#ä¸Ē月} other {#ä¸Ē月}}", "age_years": "{years, plural, other {#垁}}", "album_added": "čĸĢæˇģåŠ åˆ°į›¸å†Œ", @@ -397,12 +400,12 @@ "album_name": "į›¸å†Œåį§°", "album_options": "į›¸å†ŒčŽžįŊŽ", "album_remove_user": "į§ģé™¤į”¨æˆˇīŧŸ", - "album_remove_user_confirmation": "äŊ įĄŽåޚčρį§ģ除“{user}”吗īŧŸ", + "album_remove_user_confirmation": "įĄŽåŽščρį§ģ除“{user}”吗īŧŸ", "album_share_no_users": "įœ‹čĩˇæĨæ‚¨åˇ˛ä¸Žæ‰€æœ‰į”¨æˆˇå…ąäēĢä熿­¤į›¸å†ŒīŧŒæˆ–č€…æ‚¨æ šæœŦæ˛Ąæœ‰äģģäŊ•į”¨æˆˇå¯å…ąäēĢ。", "album_thumbnail_card_item": "1 饚", - "album_thumbnail_card_items": "{} 饚", + "album_thumbnail_card_items": "{count} 饚", "album_thumbnail_card_shared": " ¡ åˇ˛å…ąäēĢ", - "album_thumbnail_shared_by": "į”ą {} å…ąäēĢ", + "album_thumbnail_shared_by": "į”ą {user} å…ąäēĢ", "album_updated": "į›¸å†Œæœ‰æ›´æ–°", "album_updated_setting_description": "åŊ“å…ąäēĢį›¸å†Œæœ‰æ–°éĄšį›Žæ—ļæŽĨæ”ļ邮äģļ通įŸĨ", "album_user_left": "įĻģåŧ€â€œ{album}”", @@ -440,7 +443,7 @@ "archive": "åŊ’æĄŖ", "archive_or_unarchive_photo": "åŊ’æĄŖæˆ–å–æļˆåŊ’æĄŖį…§į‰‡", "archive_page_no_archived_assets": "æœĒ扞到åŊ’æĄŖéĄšį›Ž", - "archive_page_title": "åŊ’æĄŖīŧˆ{}īŧ‰", + "archive_page_title": "åŊ’æĄŖīŧˆ{count}īŧ‰", "archive_size": "åŊ’æĄŖå¤§å°", "archive_size_description": "配įŊŽä¸‹čŊŊåŊ’æĄŖå¤§å°īŧˆGBīŧ‰", "archived": "åˇ˛å­˜æĄŖ", @@ -477,27 +480,27 @@ "assets_added_to_album_count": "厞æˇģ加{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˆ°į›¸å†Œ", "assets_added_to_name_count": "厞æˇģ加{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}到{hasName, select, true {{name}} other {æ–°į›¸å†Œ}}", "assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", - "assets_deleted_permanently": "{}ä¸ĒéĄšį›Žåˇ˛čĸĢæ°¸äš…删除", - "assets_deleted_permanently_from_server": "厞äģŽæœåŠĄå™¨ä¸­æ°¸äš…į§ģ除{}ä¸ĒéĄšį›Ž", + "assets_deleted_permanently": "{count} ä¸ĒéĄšį›Žåˇ˛čĸĢæ°¸äš…删除", + "assets_deleted_permanently_from_server": "åˇ˛æ°¸äš…į§ģ除 {count} ä¸ĒéĄšį›Ž", "assets_moved_to_trash_count": "厞į§ģ动{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}到回æ”ļįĢ™", "assets_permanently_deleted_count": "åˇ˛æ°¸äš…åˆ é™¤{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "assets_removed_count": "厞į§ģ除{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", - "assets_removed_permanently_from_device": "厞äģŽčŽžå¤‡ä¸­æ°¸äš…į§ģ除{}ä¸ĒéĄšį›Ž", + "assets_removed_permanently_from_device": "厞äģŽčŽžå¤‡ä¸­æ°¸äš…į§ģ除 {count} ä¸ĒéĄšį›Ž", "assets_restore_confirmation": "įĄŽåŽščρæĸ复回æ”ļįĢ™ä¸­įš„æ‰€æœ‰éĄšį›Žå—īŧŸč¯Ĩ操äŊœæ— æŗ•æ’¤æļˆīŧč¯ˇæŗ¨æ„īŧŒč„ąæœēéĄšį›Žæ— æŗ•é€ščŋ‡čŋ™į§æ–šåŧæĸ复。", "assets_restored_count": "厞æĸ复{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", - "assets_restored_successfully": "åˇ˛æˆåŠŸæĸ复{}ä¸ĒéĄšį›Ž", - "assets_trashed": "{}ä¸Ē回æ”ļįĢ™éĄšį›Ž", + "assets_restored_successfully": "åˇ˛æˆåŠŸæĸ复{count}ä¸ĒéĄšį›Ž", + "assets_trashed": "{count} ä¸ĒéĄšį›Žæ”žå…Ĩ回æ”ļįĢ™", "assets_trashed_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛æ”žå…Ĩ回æ”ļįĢ™", - "assets_trashed_from_server": "{}ä¸ĒéĄšį›Žåˇ˛æ”žå…Ĩ回æ”ļįĢ™", + "assets_trashed_from_server": "{count} ä¸ĒéĄšį›Žåˇ˛æ”žå…Ĩ回æ”ļįĢ™", "assets_were_part_of_album_count": "{count, plural, one {éĄšį›Ž} other {éĄšį›Ž}}厞įģåœ¨į›¸å†Œä¸­", "authorized_devices": "åˇ˛æŽˆæƒčŽžå¤‡", - "automatic_endpoint_switching_subtitle": "åœ¨å¯į”¨įš„æƒ…å†ĩ下īŧŒé€ščŋ‡æŒ‡åŽšįš„ Wi-Fi čŋ›čĄŒæœŦ地čŋžæŽĨīŧŒåšļ在å…ļ厃地斚äŊŋᔍæ›ŋäģŖčŋžæŽĨ", - "automatic_endpoint_switching_title": "č‡Ē动切æĸURL", + "automatic_endpoint_switching_subtitle": "åŊ“čŋžæŽĨåˆ°æŒ‡åŽšįš„ Wi-Fi æ—ļäŊŋᔍæœŦ地čŋžæŽĨīŧŒåœ¨å…ļåŽƒįŽ¯åĸƒä¸‹äŊŋᔍæ›ŋäģŖčŋžæŽĨ", + "automatic_endpoint_switching_title": "č‡Ē动切æĸ URL", "back": "čŋ”回", "back_close_deselect": "čŋ”å›žã€å…ŗé—­æˆ–åé€‰", "background_location_permission": "后台厚äŊæƒé™", "background_location_permission_content": "ä¸ēäē†åœ¨åŽå°čŋčĄŒæ—ļ切æĸįŊ‘įģœīŧŒImmich åŋ…éĄģ*始įģˆ*æ‹Ĩæœ‰į˛žįĄŽįš„äŊįŊŽčŽŋ闎权限īŧŒčŋ™æ ˇåē”ᔍፋåēæ‰čƒŊč¯ģ取 Wi-Fi įŊ‘įģœįš„åį§°", - "backup_album_selection_page_albums_device": "čŽžå¤‡ä¸Šįš„į›¸å†Œīŧˆ{}īŧ‰", + "backup_album_selection_page_albums_device": "čŽžå¤‡ä¸Šįš„į›¸å†Œīŧˆ{count}īŧ‰", "backup_album_selection_page_albums_tap": "单å‡ģ选中īŧŒåŒå‡ģ取æļˆ", "backup_album_selection_page_assets_scatter": "éĄšį›Žäŧšåˆ†æ•Ŗåœ¨å¤šä¸Ēį›¸å†Œä¸­ã€‚å› æ­¤īŧŒå¯äģĨ在备äģŊčŋ‡į¨‹ä¸­åŒ…åĢæˆ–æŽ’é™¤į›¸å†Œã€‚", "backup_album_selection_page_select_albums": "é€‰æ‹Šį›¸å†Œ", @@ -506,11 +509,11 @@ "backup_all": "全部", "backup_background_service_backup_failed_message": "备äģŊå¤ąč´ĨīŧŒæ­Ŗåœ¨é‡č¯•â€Ļ", "backup_background_service_connection_failed_message": "čŋžæŽĨæœåŠĄå™¨å¤ąč´ĨīŧŒæ­Ŗåœ¨é‡č¯•â€Ļ", - "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šäŧ  {}", + "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šäŧ  {filename}", "backup_background_service_default_notification": "æ­Ŗåœ¨æŖ€æŸĨæ–°éĄšį›Žâ€Ļ", "backup_background_service_error_title": "备äģŊå¤ąč´Ĩ", "backup_background_service_in_progress_notification": "æ­Ŗåœ¨å¤‡äģŊâ€Ļ", - "backup_background_service_upload_failure_notification": "上äŧ å¤ąč´Ĩ {}", + "backup_background_service_upload_failure_notification": "{filename}上äŧ å¤ąč´Ĩ", "backup_controller_page_albums": "备äģŊį›¸å†Œ", "backup_controller_page_background_app_refresh_disabled_content": "čρäŊŋį”¨åŽå°å¤‡äģŊ功čƒŊīŧŒč¯ˇåœ¨â€œčŽžįŊŽâ€>â€œå¸¸č§„â€>“后台åē”į”¨åˆˇæ–°â€ä¸­å¯į”¨åŽå°åē”ᔍፋåēåˆˇæ–°ã€‚", "backup_controller_page_background_app_refresh_disabled_title": "后台åē”į”¨åˆˇæ–°åˇ˛įρᔍ", @@ -521,22 +524,22 @@ "backup_controller_page_background_battery_info_title": "į”ĩæą äŧ˜åŒ–", "backup_controller_page_background_charging": "äģ…å……į”ĩæ—ļ", "backup_controller_page_background_configure_error": "配įŊŽåŽå°æœåŠĄå¤ąč´Ĩ", - "backup_controller_page_background_delay": "åģļčŋŸ {} 后备äģŊ", + "backup_controller_page_background_delay": "åģļčŋŸå¤‡äģŊįš„æ–°éĄšį›Žīŧš{duration}", "backup_controller_page_background_description": "打åŧ€åŽå°æœåŠĄäģĨč‡Ē动备äģŊäģģäŊ•æ–°éĄšį›ŽīŧŒä¸”无需打åŧ€åē”ᔍ", "backup_controller_page_background_is_off": "后台č‡Ē动备äģŊ厞兺闭", "backup_controller_page_background_is_on": "后台č‡Ē动备äģŊ厞åŧ€å¯", "backup_controller_page_background_turn_off": "å…ŗé—­åŽå°æœåŠĄ", "backup_controller_page_background_turn_on": "åŧ€å¯åŽå°æœåŠĄ", - "backup_controller_page_background_wifi": "äģ… WiFi", + "backup_controller_page_background_wifi": "äģ… Wi-Fi", "backup_controller_page_backup": "备äģŊ", - "backup_controller_page_backup_selected": "厞选䏭īŧš", + "backup_controller_page_backup_selected": "厞选䏭īŧš ", "backup_controller_page_backup_sub": "厞备äģŊįš„į…§į‰‡å’Œč§†éĸ‘", - "backup_controller_page_created": "创åģēæ—ļ间: {}", + "backup_controller_page_created": "创åģēæ—ļ间īŧš{date}", "backup_controller_page_desc_backup": "打åŧ€å‰å°å¤‡äģŊīŧŒäģĨåœ¨į¨‹åēčŋčĄŒæ—ļč‡Ē动备äģŊæ–°éĄšį›Žã€‚", - "backup_controller_page_excluded": "åˇ˛æŽ’é™¤īŧš", - "backup_controller_page_failed": "å¤ąč´Ĩīŧˆ{}īŧ‰", - "backup_controller_page_filename": "文äģļåį§°: {} [{}]", - "backup_controller_page_id": "ID: {}", + "backup_controller_page_excluded": "åˇ˛æŽ’é™¤īŧš ", + "backup_controller_page_failed": "å¤ąč´Ĩīŧˆ{count}īŧ‰", + "backup_controller_page_filename": "文äģļåį§°īŧš{filename} [{size}]", + "backup_controller_page_id": "IDīŧš{id}", "backup_controller_page_info": "备äģŊäŋĄæ¯", "backup_controller_page_none_selected": "æœĒ选拊", "backup_controller_page_remainder": "削äŊ™", @@ -545,7 +548,7 @@ "backup_controller_page_start_backup": "åŧ€å§‹å¤‡äģŊ", "backup_controller_page_status_off": "前台č‡Ē动备äģŊ厞兺闭", "backup_controller_page_status_on": "前台č‡Ē动备äģŊ厞åŧ€å¯", - "backup_controller_page_storage_format": "{}/{} 厞äŊŋᔍ", + "backup_controller_page_storage_format": "{used}/{total} 厞äŊŋᔍ", "backup_controller_page_to_backup": "čρ备äģŊįš„į›¸å†Œ", "backup_controller_page_total_sub": "é€‰ä¸­į›¸å†Œä¸­æ‰€æœ‰ä¸é‡å¤įš„č§†éĸ‘和回像", "backup_controller_page_turn_off": "å…ŗé—­å‰å°å¤‡äģŊ", @@ -560,6 +563,10 @@ "backup_options_page_title": "备äģŊ选项", "backup_setting_subtitle": "įŽĄį†åŽå°å’Œå‰å°ä¸Šäŧ čŽžįŊŽ", "backward": "后退", + "biometric_auth_enabled": "į”Ÿį‰Šč¯†åˆĢčēĢäģŊéĒŒč¯åˇ˛å¯į”¨", + "biometric_locked_out": "您čĸĢé”åŽšåœ¨į”Ÿį‰Šč¯†åˆĢčēĢäģŊéĒŒč¯äš‹å¤–", + "biometric_no_options": "æ˛Ąæœ‰å¯į”¨įš„į”Ÿį‰Šč¯†åˆĢ选项", + "biometric_not_available": "į”Ÿį‰Šč¯†åˆĢčēĢäģŊéĒŒč¯åœ¨æ­¤čŽžå¤‡ä¸Šä¸å¯į”¨", "birthdate_saved": "å‡ēį”Ÿæ—Ĩ期äŋå­˜æˆåŠŸ", "birthdate_set_description": "å‡ēį”Ÿæ—ĨæœŸį”¨äēŽčŽĄįŽ—į…§į‰‡ä¸­č¯Ĩäēēį‰Šåœ¨æ‹į…§æ—ļįš„åš´éž„ã€‚", "blurred_background": "čƒŒæ™¯æ¨ĄįŗŠ", @@ -570,21 +577,21 @@ "bulk_keep_duplicates_confirmation": "æ‚¨įĄŽåŽščρäŋį•™{count, plural, one {#ä¸Ēé‡å¤éĄšį›Ž} other {#ä¸Ēé‡å¤éĄšį›Ž}}吗īŧŸčŋ™å°†æ¸…įŠēæ‰€æœ‰é‡å¤čŽ°åŊ•īŧŒäŊ†ä¸äŧšåˆ é™¤äģģäŊ•内厚。", "bulk_trash_duplicates_confirmation": "æ‚¨įĄŽåŽščĻæ‰šé‡åˆ é™¤{count, plural, one {#ä¸Ēé‡å¤éĄšį›Ž} other {#ä¸Ēé‡å¤éĄšį›Ž}}吗īŧŸčŋ™å°†äŋį•™æ¯įģ„ä¸­æœ€å¤§įš„éĄšį›Žåšļ删除所有å…ļåŽƒé‡å¤éĄšį›Žã€‚", "buy": "č´­äš° Immich", - "cache_settings_album_thumbnails": "回åē“įŧŠį•Ĩ回īŧˆ{} 饚īŧ‰", + "cache_settings_album_thumbnails": "回åē“éĄĩéĸįŧŠį•Ĩ回īŧˆ{count} 饚īŧ‰", "cache_settings_clear_cache_button": "清除įŧ“å­˜", "cache_settings_clear_cache_button_title": "清除åē”ᔍįŧ“å­˜ã€‚åœ¨é‡æ–°į”Ÿæˆįŧ“存䚋前īŧŒå°†æ˜žč‘—åŊąå“åē”į”¨įš„æ€§čƒŊ。", "cache_settings_duplicated_assets_clear_button": "清除", "cache_settings_duplicated_assets_subtitle": "厞加å…Ĩéģ‘åå•įš„į…§į‰‡å’Œč§†éĸ‘", - "cache_settings_duplicated_assets_title": "é‡å¤éĄšį›Žīŧˆ{}īŧ‰", - "cache_settings_image_cache_size": "回像įŧ“存大小īŧˆ{} 饚īŧ‰", + "cache_settings_duplicated_assets_title": "é‡å¤éĄšį›Žīŧˆ{count}īŧ‰", + "cache_settings_image_cache_size": "回像įŧ“存大小īŧˆ{count} 饚īŧ‰", "cache_settings_statistics_album": "回åē“įŧŠį•Ĩ回", - "cache_settings_statistics_assets": "{} 饚īŧˆ{}īŧ‰", + "cache_settings_statistics_assets": "{count} 饚īŧˆ{size}īŧ‰", "cache_settings_statistics_full": "厌整回像", "cache_settings_statistics_shared": "å…ąäēĢį›¸å†ŒįŧŠį•Ĩ回", "cache_settings_statistics_thumbnail": "įŧŠį•Ĩ回", "cache_settings_statistics_title": "įŧ“å­˜äŊŋį”¨æƒ…å†ĩ", "cache_settings_subtitle": "控åˆļ Immich app įš„įŧ“å­˜čĄŒä¸ē", - "cache_settings_thumbnail_size": "įŧŠį•Ĩ回įŧ“存大小īŧˆ{} 饚īŧ‰", + "cache_settings_thumbnail_size": "įŧŠį•Ĩ回įŧ“存大小īŧˆ{count} 饚īŧ‰", "cache_settings_tile_subtitle": "莞įŊŽæœŦåœ°å­˜å‚¨čĄŒä¸ē", "cache_settings_tile_title": "æœŦ地存储", "cache_settings_title": "įŧ“å­˜čŽžįŊŽ", @@ -597,25 +604,28 @@ "cannot_merge_people": "æ— æŗ•åˆåšļäēēį‰Š", "cannot_undo_this_action": "æŗ¨æ„īŧšč¯Ĩ操äŊœæ— æŗ•čĸĢæ’¤æļˆīŧ", "cannot_update_the_description": "æ— æŗ•æ›´æ–°æčŋ°", + "cast": "æŠ•åą", "change_date": "更攚æ—Ĩ期", + "change_description": "äŋŽæ”šæčŋ°", "change_display_order": "更攚昞į¤ēéĄēåē", "change_expiration_time": "更攚čŋ‡æœŸæ—ļ间", "change_location": "更攚äŊįŊŽ", "change_name": "æ›´æ”šåį§°", "change_name_successfully": "æ›´æ”šåį§°æˆåŠŸ", "change_password": "äŋŽæ”šå¯†į ", - "change_password_description": "čŋ™æ˜¯äŊ įš„įŦŦ一æŦĄį™ģåŊ•äēĻæˆ–有äēēčĻæą‚æ›´æ”šäŊ įš„å¯†į ã€‚č¯ˇåœ¨ä¸‹éĸ输å…Ĩæ–°å¯†į ã€‚", + "change_password_description": "čŋ™æ˜¯æ‚¨įš„įŦŦ一æŦĄį™ģåŊ•äēĻæˆ–有äēēčĻæą‚æ›´æ”šæ‚¨įš„å¯†į ã€‚č¯ˇåœ¨ä¸‹éĸ输å…Ĩæ–°å¯†į ã€‚", "change_password_form_confirm_password": "įĄŽčŽ¤å¯†į ", - "change_password_form_description": "{name} 您åĨŊīŧŒ\n\nčŋ™æ˜¯æ‚¨éĻ–æŦĄį™ģåŊ•įŗģįģŸīŧŒæˆ–čĸĢįŽĄį†å‘˜čĻæą‚æ›´æ”šå¯†į ã€‚\nč¯ˇåœ¨ä¸‹æ–ščž“å…Ĩæ–°å¯†į ã€‚", + "change_password_form_description": "{name} 您åĨŊīŧŒ\n\nčŋ™æ˜¯æ‚¨éĻ–æŦĄį™ģåŊ•įŗģįģŸīŧŒæˆ–čĸĢįŽĄį†å‘˜čĻæą‚æ›´æ”šå¯†į ã€‚č¯ˇåœ¨ä¸‹æ–ščž“å…Ĩæ–°å¯†į ã€‚", "change_password_form_new_password": "æ–°å¯†į ", "change_password_form_password_mismatch": "å¯†į ä¸åŒšé…", "change_password_form_reenter_new_password": "再æŦĄčž“å…Ĩæ–°å¯†į ", - "change_your_password": "äŋŽæ”šäŊ įš„坆᠁", + "change_pin_code": "äŋŽæ”šPIN᠁", + "change_your_password": "äŋŽæ”šæ‚¨įš„å¯†į ", "changed_visibility_successfully": "æ›´æ”šå¯č§æ€§æˆåŠŸ", "check_all": "æŖ€æŸĨ所有", "check_corrupt_asset_backup": "æŖ€æŸĨ备äģŊ是åĻ损坏", "check_corrupt_asset_backup_button": "æ‰§čĄŒæŖ€æŸĨ", - "check_corrupt_asset_backup_description": "äģ…在čŋžæŽĨ到Wi-FiåšļåŽŒæˆæ‰€æœ‰éĄšį›Žå¤‡äģŊåŽæ‰§čĄŒæ­¤æŖ€æŸĨ。č¯Ĩčŋ‡į¨‹å¯čƒŊ需čĻå‡ åˆ†é’Ÿã€‚", + "check_corrupt_asset_backup_description": "äģ…在čŋžæŽĨ到 Wi-Fi åšļåŽŒæˆæ‰€æœ‰éĄšį›Žå¤‡äģŊåŽæ‰§čĄŒæ­¤æŖ€æŸĨ。č¯Ĩčŋ‡į¨‹å¯čƒŊ需čĻå‡ åˆ†é’Ÿã€‚", "check_logs": "æŖ€æŸĨæ—Ĩåŋ—", "choose_matching_people_to_merge": "é€‰æ‹ŠåŒšé…įš„äēēčŋ›čĄŒåˆåšļ", "city": "城市", @@ -630,8 +640,8 @@ "client_cert_import_success_msg": "åŽĸæˆˇį̝蝁äšĻ厞å¯ŧå…Ĩ", "client_cert_invalid_msg": "æ— æ•ˆįš„č¯äšĻ文äģ￈–坆᠁错蝝", "client_cert_remove_msg": "åŽĸæˆˇį̝蝁äšĻ厞į§ģ除", - "client_cert_subtitle": "äģ…æ”¯æŒPKCS12 (.p12, .pfx)æ ŧåŧã€‚äģ…可在į™ģåŊ•前čŋ›čĄŒč¯äšĻįš„å¯ŧå…Ĩ和į§ģ除", - "client_cert_title": "SSLåŽĸæˆˇį̝蝁äšĻ", + "client_cert_subtitle": "äģ…æ”¯æŒ PKCS12 (.p12, .pfx) æ ŧåŧã€‚äģ…可在į™ģåŊ•前čŋ›čĄŒč¯äšĻįš„å¯ŧå…Ĩ和į§ģ除", + "client_cert_title": "SSL åŽĸæˆˇį̝蝁äšĻ", "clockwise": "éĄēæ—ļ针", "close": "å…ŗé—­", "collapse": "折叠", @@ -648,19 +658,21 @@ "confirm": "įĄŽčŽ¤", "confirm_admin_password": "įĄŽčŽ¤įŽĄį†å‘˜å¯†į ", "confirm_delete_face": "æ‚¨įĄŽåŽščρäģŽčĩ„äē§ä¸­åˆ é™¤ {name} įš„č„¸å—īŧŸ", - "confirm_delete_shared_link": "äŊ įĄŽåޚčĻåˆ é™¤æ­¤å…ąäēĢ链æŽĨ吗īŧŸ", - "confirm_keep_this_delete_others": "é™¤æ­¤éĄšį›Žå¤–īŧŒå †å ä¸­įš„æ‰€æœ‰å…ļåŽƒéĄšį›ŽéƒŊ将čĸĢ删除。äŊ įĄŽåޚčρįģ§įģ­å—īŧŸ", + "confirm_delete_shared_link": "įĄŽåŽščĻåˆ é™¤æ­¤å…ąäēĢ链æŽĨ吗īŧŸ", + "confirm_keep_this_delete_others": "é™¤æ­¤éĄšį›Žå¤–īŧŒå †å ä¸­įš„æ‰€æœ‰å…ļåŽƒéĄšį›ŽéƒŊ将čĸĢåˆ é™¤ã€‚įĄŽåŽščρįģ§įģ­å—īŧŸ", + "confirm_new_pin_code": "įĄŽčŽ¤æ–°įš„PIN᠁", "confirm_password": "įĄŽčŽ¤å¯†į ", + "connected_to": "厞čŋžæŽĨ到", "contain": "包åĢ", "context": "äģĨ文搜回", "continue": "įģ§įģ­", - "control_bottom_app_bar_album_info_shared": "{} 饚 ¡ åˇ˛å…ąäēĢ", + "control_bottom_app_bar_album_info_shared": "åˇ˛å…ąäēĢ {count} 饚", "control_bottom_app_bar_create_new_album": "新åģēį›¸å†Œ", - "control_bottom_app_bar_delete_from_immich": "äģŽImmichæœåŠĄå™¨ä¸­åˆ é™¤", + "control_bottom_app_bar_delete_from_immich": "äģŽ Immich æœåŠĄå™¨ä¸­åˆ é™¤", "control_bottom_app_bar_delete_from_local": "äģŽį§ģåŠ¨čŽžå¤‡ä¸­åˆ é™¤", "control_bottom_app_bar_edit_location": "įŧ–čž‘äŊįŊŽäŋĄæ¯", "control_bottom_app_bar_edit_time": "įŧ–čž‘æ—Ĩ期和æ—ļ间", - "control_bottom_app_bar_share_link": "Share Link", + "control_bottom_app_bar_share_link": "分äēĢ链æŽĨ", "control_bottom_app_bar_share_to": "发送įģ™", "control_bottom_app_bar_trash_from_immich": "攞å…Ĩ回æ”ļįĢ™", "copied_image_to_clipboard": "åˇ˛å¤åˆļå›žį‰‡č‡ŗå‰Ē切æŋ。", @@ -692,19 +704,18 @@ "create_tag_description": "创åģē一ä¸Ēæ–°æ ‡į­žã€‚å¯šäēŽåĩŒåĨ—æ ‡į­žīŧŒč¯ˇčž“å…Ĩæ ‡į­žįš„åŽŒæ•´čˇ¯åž„īŧŒåŒ…æ‹Ŧæ­Ŗæ–œæ īŧˆ/īŧ‰ã€‚", "create_user": "创åģēį”¨æˆˇ", "created": "åˇ˛åˆ›åģē", + "created_at": "åˇ˛åˆ›åģē", "crop": "誁å‰Ē", "curated_object_page_title": "äē‹į‰Š", "current_device": "åŊ“å‰čŽžå¤‡", + "current_pin_code": "åŊ“前PIN᠁", "current_server_address": "åŊ“å‰æœåŠĄå™¨åœ°å€", "custom_locale": "č‡Ē厚䚉地åŒē", "custom_locale_description": "æ—Ĩ期和数字昞į¤ēæ ŧåŧčˇŸéšč¯­č¨€å’Œåœ°åŒē", - "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "æˇąč‰˛", "date_after": "åŧ€å§‹æ—Ĩ期", "date_and_time": "æ—Ĩ期与æ—ļ间", "date_before": "į쓿Ÿæ—Ĩ期", - "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "å‡ēį”Ÿæ—Ĩ期äŋå­˜æˆåŠŸ", "date_range": "æ—ĨæœŸčŒƒå›´", "day": "æ—Ĩ", @@ -717,14 +728,14 @@ "default_locale_description": "æ šæŽæ‚¨įš„æĩč§ˆå™¨åœ°åŒē莞įŊŽæ—Ĩ期和数字昞į¤ēæ ŧåŧ", "delete": "删除", "delete_album": "åˆ é™¤į›¸å†Œ", - "delete_api_key_prompt": "įĄŽåŽšåˆ é™¤æ­¤API key吗īŧŸ", + "delete_api_key_prompt": "įĄŽåŽšåˆ é™¤æ­¤ API key 吗īŧŸ", "delete_dialog_alert": "čŋ™äē›éĄšį›Žå°†äģŽ Immich å’Œæ‚¨įš„čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", - "delete_dialog_alert_local": "čŋ™äē›éĄšį›Žå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤īŧŒäŊ†äģį„ļ可äģĨäģŽImmichæœåŠĄå™¨ä¸­å†æŦĄčŽˇå–", - "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†éĄšį›Žčŋ˜æœĒ备äģŊ臺ImmichæœåŠĄå™¨īŧŒå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", - "delete_dialog_alert_remote": "čŋ™äē›éĄšį›Žå°†äģŽImmichæœåŠĄå™¨ä¸­æ°¸äš…åˆ é™¤", + "delete_dialog_alert_local": "čŋ™äē›éĄšį›Žå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤īŧŒäŊ†äģį„ļ可äģĨäģŽ Immich æœåŠĄå™¨ä¸­å†æŦĄčŽˇå–", + "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†éĄšį›Žčŋ˜æœĒ备äģŊ臺 Immich æœåŠĄå™¨īŧŒå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", + "delete_dialog_alert_remote": "čŋ™äē›éĄšį›Žå°†äģŽ Immich æœåŠĄå™¨ä¸­æ°¸äš…åˆ é™¤", "delete_dialog_ok_force": "įĄŽčŽ¤åˆ é™¤", "delete_dialog_title": "永䚅删除", - "delete_duplicates_confirmation": "äŊ čĻæ°¸äš…åˆ é™¤čŋ™äē›é‡å¤éĄšå—īŧŸ", + "delete_duplicates_confirmation": "įĄŽåŽščĻæ°¸äš…åˆ é™¤čŋ™äē›é‡å¤éĄšå—īŧŸ", "delete_face": "删除äēē脸", "delete_key": "删除密é’Ĩ", "delete_library": "删除回åē“", @@ -763,7 +774,7 @@ "download_enqueue": "厞加å…Ĩ下čŊŊ队列", "download_error": "下čŊŊå‡ē错", "download_failed": "下čŊŊå¤ąč´Ĩ", - "download_filename": "文äģļīŧš{}", + "download_filename": "文äģļīŧš{filename}", "download_finished": "下čŊŊ厌成", "download_include_embedded_motion_videos": "内åĩŒč§†éĸ‘", "download_include_embedded_motion_videos_description": "将厞å†ĩį…§į‰‡ä¸­įš„å†…åĩŒč§†éĸ‘äŊœä¸ē单į‹Ŧ文äģļįēŗå…Ĩ", @@ -787,6 +798,8 @@ "edit_avatar": "įŧ–čž‘å¤´åƒ", "edit_date": "įŧ–čž‘æ—Ĩ期", "edit_date_and_time": "įŧ–čž‘æ—Ĩ期和æ—ļ间", + "edit_description": "äŋŽæ”šæčŋ°", + "edit_description_prompt": "č¯ˇé€‰æ‹Šæ–°įš„æčŋ°īŧš", "edit_exclusion_pattern": "įŧ–čž‘æŽ’é™¤č§„åˆ™", "edit_faces": "įŧ–čž‘äēē脸", "edit_import_path": "įŧ–čž‘å¯ŧå…Ĩčˇ¯åž„", @@ -807,19 +820,23 @@ "editor_crop_tool_h2_aspect_ratios": "é•ŋåŽŊ比", "editor_crop_tool_h2_rotation": "旋čŊŦ", "email": "é‚ŽįŽą", + "email_notifications": "邮äģļ通įŸĨ", "empty_folder": "文äģļ多ä¸ēįŠē", "empty_trash": "清įŠē回æ”ļįĢ™", "empty_trash_confirmation": "įĄŽåŽščĻæ¸…įŠē回æ”ļįĢ™īŧŸčŋ™å°†æ°¸äš…删除回æ”ļįĢ™ä¸­įš„æ‰€æœ‰éĄšį›Žã€‚\næŗ¨æ„īŧšč¯Ĩ操äŊœæ— æŗ•æ’¤æļˆīŧ", "enable": "吝ᔍ", + "enable_biometric_auth_description": "输å…Ĩæ‚¨įš„PIN᠁äģĨå¯į”¨į”Ÿį‰Šč¯†åˆĢčēĢäģŊénj蝁", "enabled": "厞吝ᔍ", "end_date": "į쓿Ÿæ—Ĩ期", "enqueued": "排队中", "enter_wifi_name": "输å…Ĩ Wi-Fi åį§°", + "enter_your_pin_code": "输å…Ĩæ‚¨įš„PIN᠁", + "enter_your_pin_code_subtitle": "输å…Ĩæ‚¨įš„PIN᠁äģĨčŽŋ闎此锁厚文äģļ多", "error": "错蝝", "error_change_sort_album": "æ›´æ”šį›¸å†ŒæŽ’åēå¤ąč´Ĩ", "error_delete_face": "删除äēēč„¸å¤ąč´Ĩ", "error_loading_image": "加čŊŊå›žį‰‡æ—ļå‡ē错", - "error_saving_image": "错蝝īŧš{}", + "error_saving_image": "错蝝īŧš{error}", "error_title": "错蝝 - åĨŊ像å‡ēäē†é—Žéĸ˜", "errors": { "cannot_navigate_next_asset": "æ— æŗ•å¯ŧčˆĒ到下一ä¸ĒéĄšį›Ž", @@ -849,10 +866,12 @@ "failed_to_keep_this_delete_others": "æ— æŗ•äŋį•™č¯ĨéĄšį›Žåšļ删除å…ļåŽƒéĄšį›Ž", "failed_to_load_asset": "加čŊŊéĄšį›Žå¤ąč´Ĩ", "failed_to_load_assets": "加čŊŊéĄšį›Žå¤ąč´Ĩ", + "failed_to_load_notifications": "加čŊŊ通įŸĨå¤ąč´Ĩ", "failed_to_load_people": "加čŊŊäēēį‰Šå¤ąč´Ĩ", "failed_to_remove_product_key": "į§ģ除äē§å“å¯†é’Ĩå¤ąč´Ĩ", "failed_to_stack_assets": "æ— æŗ•å †å éĄšį›Ž", "failed_to_unstack_assets": "æ— æŗ•å–æļˆå †å éĄšį›Ž", + "failed_to_update_notification_status": "更新通įŸĨįŠļæ€å¤ąč´Ĩ", "import_path_already_exists": "æ­¤å¯ŧå…Ĩčˇ¯åž„åˇ˛å­˜åœ¨ã€‚", "incorrect_email_or_password": "é‚ŽįŽąæˆ–å¯†į é”™č¯¯", "paths_validation_failed": "{paths, plural, one {#æĄčˇ¯åž„} other {#æĄčˇ¯åž„}} æ ĄéĒŒå¤ąč´Ĩ", @@ -870,6 +889,7 @@ "unable_to_archive_unarchive": "æ— æŗ•{archived, select, true {åŊ’æĄŖ} other {取æļˆåŊ’æĄŖ}}", "unable_to_change_album_user_role": "æ— æŗ•æ›´æ”šį›¸å†Œį”¨æˆˇč§„åˆ™", "unable_to_change_date": "æ— æŗ•æ›´æ”šæ—Ĩ期", + "unable_to_change_description": "æ— æŗ•äŋŽæ”šæčŋ°", "unable_to_change_favorite": "æ— æŗ•äŋŽæ”šéĄšį›Žįš„æ”ļč—åąžæ€§", "unable_to_change_location": "æ— æŗ•æ›´æ”šäŊįŊŽ", "unable_to_change_password": "æ— æŗ•äŋŽæ”šå¯†į ", @@ -877,7 +897,7 @@ "unable_to_complete_oauth_login": "æ— æŗ•åŽŒæˆ OAuth į™ģåŊ•", "unable_to_connect": "æ— æŗ•čŋžæŽĨ", "unable_to_connect_to_server": "æ— æŗ•čŋžæŽĨč‡ŗæœåŠĄå™¨", - "unable_to_copy_to_clipboard": "æ— æŗ•å¤åˆļ到å‰Ē切æŋīŧŒč¯ˇįĄŽäŋäŊ åœ¨äŊŋᔍhttpsčŽŋ问æœŦéĄĩ", + "unable_to_copy_to_clipboard": "æ— æŗ•å¤åˆļ到å‰Ē切æŋīŧŒč¯ˇįĄŽäŋæ‚¨åœ¨äŊŋᔍhttpsčŽŋ问æœŦéĄĩ", "unable_to_create_admin_account": "æ— æŗ•åˆ›åģēįŽĄį†å‘˜č´Ļæˆˇ", "unable_to_create_api_key": "æ— æŗ•åˆ›åģēæ–°įš„ API Key", "unable_to_create_library": "æ— æŗ•åˆ›åģē回åē“", @@ -907,6 +927,7 @@ "unable_to_log_out_all_devices": "æ— æŗ•äģŽæ‰€æœ‰čŽžå¤‡į™ģå‡ē", "unable_to_log_out_device": "æ— æŗ•äģŽčŽžå¤‡į™ģå‡ē", "unable_to_login_with_oauth": "æ— æŗ•äŊŋᔍ OAuth čŋ›čĄŒį™ģåŊ•", + "unable_to_move_to_locked_folder": "æ— æŗ•į§ģ动到锁厚文äģļ多", "unable_to_play_video": "æ— æŗ•æ’­æ”žč§†éĸ‘", "unable_to_reassign_assets_existing_person": "æ— æŗ•å°†éĄšį›ŽæŒ‡æ´žįģ™{name, select, null {åˇ˛å­˜åœ¨įš„äēēį‰Š} other {{name}}}", "unable_to_reassign_assets_new_person": "æ— æŗ•é‡æ–°æŒ‡æ´žéĄšį›Žį왿–°įš„äēēį‰Š", @@ -920,6 +941,7 @@ "unable_to_remove_reaction": "æ— æŗ•į§ģ除回åē”", "unable_to_repair_items": "æ— æŗ•äŋŽå¤éĄšį›Ž", "unable_to_reset_password": "æ— æŗ•é‡įŊŽå¯†į ", + "unable_to_reset_pin_code": "æ— æŗ•é‡įŊŽPIN᠁", "unable_to_resolve_duplicate": "æ— æŗ•č§Ŗå†ŗé‡å¤éĄš", "unable_to_restore_assets": "æ— æŗ•æĸå¤éĄšį›Ž", "unable_to_restore_trash": "æ— æŗ•æĸ复回æ”ļįĢ™", @@ -953,10 +975,10 @@ "exif_bottom_sheet_location": "äŊįŊŽ", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "æˇģ加姓名", - "exif_bottom_sheet_person_age": "åš´éž„ {}", - "exif_bottom_sheet_person_age_months": "Age {} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months", - "exif_bottom_sheet_person_age_years": "Age {}", + "exif_bottom_sheet_person_age": "{age} 垁", + "exif_bottom_sheet_person_age_months": "{months} 月鞄", + "exif_bottom_sheet_person_age_year_months": "1垁 {months} ä¸Ē月", + "exif_bottom_sheet_person_age_years": "{years} 垁", "exit_slideshow": "退å‡ēåšģį¯į‰‡æ”žæ˜ ", "expand_all": "å…¨éƒ¨åą•åŧ€", "experimental_settings_new_asset_list_subtitle": "æ­Ŗåœ¨å¤„į†", @@ -974,9 +996,10 @@ "external": "å¤–éƒ¨įš„", "external_libraries": "外部回åē“", "external_network": "外部įŊ‘įģœ", - "external_network_sheet_info": "åŊ“不在éĻ–é€‰įš„ Wi-Fi įŊ‘įģœä¸Šæ—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡ä¸‹æ–šįŦŦ一ä¸Ē可čŋžé€šįš„ URL čŋžæŽĨåˆ°æœåŠĄå™¨", + "external_network_sheet_info": "åŊ“æœĒčŋžæŽĨåˆ°æŒ‡åŽšįš„ Wi-Fi įŊ‘į윿—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡ä¸‹æ–šįŦŦ一ä¸Ē可čŋžé€šįš„ URL čŽŋé—ŽæœåŠĄå™¨", "face_unassigned": "æœĒ指洞", "failed": "å¤ąč´Ĩ", + "failed_to_authenticate": "čēĢäģŊéĒŒč¯å¤ąč´Ĩ", "failed_to_load_assets": "加čŊŊéĄšį›Žå¤ąč´Ĩ", "failed_to_load_folder": "加čŊŊ文äģļå¤šå¤ąč´Ĩ", "favorite": "æ”ļ藏", @@ -992,6 +1015,7 @@ "filetype": "文äģļįąģ型", "filter": "᭛选", "filter_people": "čŋ‡æģ¤äēēį‰Š", + "filter_places": "į­›é€‰åœ°į‚š", "find_them_fast": "æŒ‰åį§°åŋĢ速搜į´ĸ", "fix_incorrect_match": "äŋŽå¤ä¸æ­ŖįĄŽįš„匚配", "folder": "文äģļ多", @@ -1022,16 +1046,16 @@ "header_settings_header_value_input": "标头å€ŧ", "headers_settings_tile_subtitle": "厚䚉äģŖį†æ ‡å¤´īŧŒåē”ᔍäēŽæ¯æŦĄįŊ‘įģœč¯ˇæą‚", "headers_settings_tile_title": "č‡Ē厚䚉äģŖį†æ ‡å¤´", - "hi_user": "äŊ åĨŊīŧŒ{name}īŧˆ{email}īŧ‰", + "hi_user": "您åĨŊīŧŒ{name}īŧˆ{email}īŧ‰", "hide_all_people": "éšč—æ‰€æœ‰äēēį‰Š", "hide_gallery": "éšč—į›¸å†Œ", "hide_named_person": "隐藏äēēį‰Šâ€œ{name}”", "hide_password": "éšč—å¯†į ", "hide_person": "隐藏äēēį‰Š", "hide_unnamed_people": "隐藏æœĒå‘Ŋåįš„äēēį‰Š", - "home_page_add_to_album_conflicts": "åˇ˛å‘į›¸å†Œ {album} 中æˇģ加 {added} éĄšã€‚\nå…ļ中 {failed} éĄšåœ¨į›¸å†Œä¸­åˇ˛å­˜åœ¨ã€‚", + "home_page_add_to_album_conflicts": "åˇ˛å‘į›¸å†Œ {album} 中æˇģ加 {added} éĄšã€‚å…ļ中 {failed} éĄšåœ¨į›¸å†Œä¸­åˇ˛å­˜åœ¨ã€‚", "home_page_add_to_album_err_local": "暂不čƒŊ将æœŦåœ°éĄšį›ŽæˇģåŠ åˆ°į›¸å†Œä¸­īŧŒčˇŗčŋ‡", - "home_page_add_to_album_success": "åˇ˛å‘į›¸å†Œ {album} 中æˇģ加 {added} éĄšã€‚", + "home_page_add_to_album_success": "åˇ˛å‘į›¸å†Œ {album} 中æˇģ加 {added} éĄšã€‚", "home_page_album_err_partner": "æš‚æ— æŗ•å°†åŒäŧ´įš„éĄšį›ŽæˇģåŠ åˆ°į›¸å†ŒīŧŒčˇŗčŋ‡", "home_page_archive_err_local": "æš‚æ— æŗ•åŊ’æĄŖæœŦåœ°éĄšį›ŽīŧŒčˇŗčŋ‡", "home_page_archive_err_partner": "æ— æŗ•å­˜æĄŖåŒäŧ´įš„éĄšį›ŽīŧŒčˇŗčŋ‡", @@ -1040,13 +1064,15 @@ "home_page_delete_remote_err_local": "čŋœį¨‹éĄšį›Žåˆ é™¤æ¨ĄåŧīŧŒčˇŗčŋ‡æœŦåœ°éĄšį›Ž", "home_page_favorite_err_local": "暂不čƒŊæ”ļ藏æœŦåœ°éĄšį›ŽīŧŒčˇŗčŋ‡", "home_page_favorite_err_partner": "æš‚æ— æŗ•æ”ļč—åŒäŧ´įš„éĄšį›ŽīŧŒčˇŗčŋ‡", - "home_page_first_time_notice": "åĻ‚æžœčŋ™æ˜¯æ‚¨įŦŦ一æŦĄäŊŋᔍč¯Ĩåē”ᔍፋåēīŧŒč¯ˇįĄŽäŋé€‰æ‹Šä¸€ä¸Ēčρ备äģŊįš„æœŦåœ°į›¸å†ŒīŧŒäģĨäžŋ可äģĨ在æ—ļ间įēŋ中éĸ„č§ˆč¯Ĩį›¸å†Œä¸­įš„į…§į‰‡å’Œč§†éĸ‘。", + "home_page_first_time_notice": "åĻ‚æžœčŋ™æ˜¯æ‚¨įŦŦ一æŦĄäŊŋᔍč¯Ĩåē”ᔍፋåēīŧŒč¯ˇįĄŽäŋé€‰æ‹Šä¸€ä¸Ēčρ备äģŊįš„æœŦåœ°į›¸å†ŒīŧŒäģĨäžŋ可äģĨ在æ—ļ间įēŋ中éĸ„č§ˆč¯Ĩį›¸å†Œä¸­įš„į…§į‰‡å’Œč§†éĸ‘", + "home_page_locked_error_local": "æ— æŗ•å°†æœŦåœ°éĄšį›Žį§ģ动到锁厚文äģļ多īŧŒčˇŗčŋ‡", + "home_page_locked_error_partner": "æ— æŗ•å°†åŒäŧ´įš„éĄšį›Žį§ģ动到锁厚文äģļ多īŧŒčˇŗčŋ‡", "home_page_share_err_local": "æš‚æ— æŗ•é€ščŋ‡é“žæŽĨå…ąäēĢæœŦåœ°éĄšį›ŽīŧŒčˇŗčŋ‡", "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊ上äŧ  30 ä¸ĒéĄšį›ŽīŧŒčˇŗčŋ‡", "host": "æœåŠĄå™¨", "hour": "æ—ļ", - "ignore_icloud_photos": "åŋŊį•ĨiCloudᅧቇ", - "ignore_icloud_photos_description": "存储在iCloudä¸­įš„į…§į‰‡ä¸äŧšä¸Šäŧ č‡ŗImmichæœåŠĄå™¨", + "ignore_icloud_photos": "åŋŊį•Ĩ iCloud ᅧቇ", + "ignore_icloud_photos_description": "存储在 iCloud ä¸­įš„į…§į‰‡ä¸äŧšä¸Šäŧ č‡ŗ Immich æœåŠĄå™¨", "image": "å›žį‰‡", "image_alt_text_date": "在{date}æ‹æ‘„įš„{isVideo, select, true {视éĸ‘} other {ᅧቇ}}", "image_alt_text_date_1_person": "{date}æ‹æ‘„įš„åŒ…åĢ{person1}įš„{isVideo, select, true {视éĸ‘} other {ᅧቇ}}", @@ -1062,7 +1088,6 @@ "image_viewer_page_state_provider_download_started": "下čŊŊ启动", "image_viewer_page_state_provider_download_success": "下čŊŊ成功", "image_viewer_page_state_provider_share_error": "å…ąäēĢå‡ē错", - "immich_logo": "Immich Logo", "immich_web_interface": "Immich Web į•Œéĸ", "import_from_json": "äģŽ JSON å¯ŧå…Ĩ", "import_path": "å¯ŧå…Ĩčˇ¯åž„", @@ -1118,14 +1143,16 @@ "loading": "加čŊŊ中", "loading_search_results_failed": "加čŊŊ搜į´ĸį쓿žœå¤ąč´Ĩ", "local_network": "æœŦ地įŊ‘įģœ", - "local_network_sheet_info": "äŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įŊ‘į윿—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡æ­¤ URL čŋžæŽĨåˆ°æœåŠĄå™¨", + "local_network_sheet_info": "åŊ“äŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įŊ‘į윿—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡æ­¤ URL čŽŋé—ŽæœåŠĄå™¨", "location_permission": "厚äŊæƒé™", - "location_permission_content": "ä¸ēäē†äŊŋᔍč‡Ē动切æĸ功čƒŊīŧŒImmich 需čĻį˛žįĄŽįš„åŽšäŊæƒé™īŧŒčŋ™æ ˇæ‰čƒŊč¯ģ取åŊ“前 Wi-Fi įŊ‘įģœįš„åį§°", + "location_permission_content": "ä¸ēäŊŋᔍč‡Ē动切æĸ功čƒŊīŧŒImmich 需čĻį˛žįĄŽįš„åŽšäŊæƒé™īŧŒčŋ™æ ˇæ‰čƒŊč¯ģ取åŊ“前 Wi-Fi įŊ‘įģœįš„åį§°", "location_picker_choose_on_map": "在地回上选拊", "location_picker_latitude_error": "输å…Ĩæœ‰æ•ˆįš„įēŦåēĻå€ŧ", "location_picker_latitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…Ĩæ‚¨įš„įēŦåēĻå€ŧ", "location_picker_longitude_error": "输å…Ĩæœ‰æ•ˆįš„įģåēĻå€ŧ", "location_picker_longitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…Ĩæ‚¨įš„įģåēĻå€ŧ", + "lock": "锁厚", + "locked_folder": "锁厚文äģļ多", "log_out": "æŗ¨é”€", "log_out_all_devices": "æŗ¨é”€æ‰€æœ‰čŽžå¤‡", "logged_out_all_devices": "äģŽæ‰€æœ‰čŽžå¤‡æŗ¨é”€", @@ -1134,7 +1161,6 @@ "login_disabled": "į™ģåŊ•厞čĸĢįρᔍ", "login_form_api_exception": "API åŧ‚常īŧŒč¯ˇæŖ€æŸĨæœåŠĄå™¨åœ°å€åšļé‡č¯•ã€‚", "login_form_back_button_text": "后退", - "login_form_email_hint": "youremail@email.com", "login_form_endpoint_hint": "http://æ‚¨įš„æœåŠĄå™¨åœ°å€:įĢ¯åŖ", "login_form_endpoint_url": "æœåŠĄå™¨é“žæŽĨ地址", "login_form_err_http": "č¯ˇæŗ¨æ˜Ž http:// 或 https://", @@ -1148,10 +1174,10 @@ "login_form_handshake_exception": "ä¸ŽæœåŠĄå™¨é€šäŋĄæ—ļå‡ēįŽ°æĄæ‰‹åŧ‚常。åĻ‚æžœæ‚¨äŊŋį”¨įš„æ˜¯č‡Ēį­žåč¯äšĻīŧŒč¯ˇåœ¨čŽžįŊŽä¸­å¯į”¨č‡Ēį­žåč¯äšĻ支持。", "login_form_password_hint": "坆᠁", "login_form_save_login": "äŋæŒį™ģåŊ•", - "login_form_server_empty": "输å…ĨæœåŠĄå™¨åœ°å€", + "login_form_server_empty": "输å…ĨæœåŠĄå™¨åœ°å€ã€‚", "login_form_server_error": "æ— æŗ•čŋžæŽĨåˆ°æœåŠĄå™¨ã€‚", "login_has_been_disabled": "į™ģåŊ•厞įĻį”¨ã€‚", - "login_password_changed_error": "æ›´æ–°å¯†į æ—ļå‡ē错\n", + "login_password_changed_error": "æ›´æ–°å¯†į æ—ļå‡ē错", "login_password_changed_success": "å¯†į æ›´æ–°æˆåŠŸ", "logout_all_device_confirmation": "įĄŽåŽščρäģŽæ‰€æœ‰čŽžå¤‡æŗ¨é”€īŧŸ", "logout_this_device_confirmation": "įĄŽåŽščρäģŽæœŦčŽžå¤‡æŗ¨é”€īŧŸ", @@ -1165,13 +1191,13 @@ "manage_shared_links": "įŽĄį†å…ąäēĢ链æŽĨ", "manage_sharing_with_partners": "įŽĄį†ä¸ŽåŒäŧ´įš„å…ąäēĢ", "manage_the_app_settings": "įŽĄį†åē”į”¨čŽžįŊŽ", - "manage_your_account": "įŽĄį†äŊ įš„č´Ļæˆˇ", - "manage_your_api_keys": "įŽĄį†äŊ įš„ API 密é’Ĩ", + "manage_your_account": "įŽĄį†æ‚¨įš„č´Ļæˆˇ", + "manage_your_api_keys": "įŽĄį†æ‚¨įš„ API 密é’Ĩ", "manage_your_devices": "įŽĄį†åˇ˛į™ģåŊ•čŽžå¤‡", - "manage_your_oauth_connection": "įŽĄį†äŊ įš„ OAuth įģ‘åޚ", + "manage_your_oauth_connection": "įŽĄį†æ‚¨įš„ OAuth įģ‘åޚ", "map": "地回", - "map_assets_in_bound": "{}åŧ į…§į‰‡", - "map_assets_in_bounds": "{}åŧ į…§į‰‡", + "map_assets_in_bound": "{count} åŧ į…§į‰‡", + "map_assets_in_bounds": "{count} åŧ į…§į‰‡", "map_cannot_get_user_location": "æ— æŗ•čŽˇå–į”¨æˆˇäŊįŊŽ", "map_location_dialog_yes": "是", "map_location_picker_page_use_location": "äŊŋį”¨æ­¤äŊįŊŽ", @@ -1185,15 +1211,18 @@ "map_settings": "åœ°å›žčŽžįŊŽ", "map_settings_dark_mode": "æˇąč‰˛æ¨Ąåŧ", "map_settings_date_range_option_day": "čŋ‡åŽģ24小æ—ļ", - "map_settings_date_range_option_days": "{}夊前", + "map_settings_date_range_option_days": "{days} 夊前", "map_settings_date_range_option_year": "1嚴前", - "map_settings_date_range_option_years": "{}嚴前", + "map_settings_date_range_option_years": "{years} 嚴前", "map_settings_dialog_title": "åœ°å›žčŽžįŊŽ", "map_settings_include_show_archived": "包æ‹Ŧ厞åŊ’æĄŖéĄšį›Ž", "map_settings_include_show_partners": "包åĢäŧ™äŧ´", "map_settings_only_show_favorites": "äģ…æ˜žį¤ēæ”ļč—įš„éĄšį›Ž", "map_settings_theme_settings": "地回ä¸ģéĸ˜", "map_zoom_to_see_photos": "įŧŠå°äģĨæŸĨįœ‹éĄšį›Ž", + "mark_all_as_read": "å…¨éƒ¨æ ‡čŽ°ä¸ē厞č¯ģ", + "mark_as_read": "æ ‡čŽ°ä¸ē厞č¯ģ", + "marked_all_as_read": "åˇ˛å…¨éƒ¨æ ‡čŽ°ä¸ē厞č¯ģ", "matches": "匚配", "media_type": "åĒ’äŊ“įąģ型", "memories": "回åŋ†", @@ -1202,15 +1231,13 @@ "memories_setting_description": "įŽĄį†å›žåŋ†ä¸­įš„内厚", "memories_start_over": "å†įœ‹ä¸€æŦĄ", "memories_swipe_to_close": "ä¸Šåˆ’å…ŗé—­", - "memories_year_ago": "1嚴前", - "memories_years_ago": "{}嚴前", "memory": "回åŋ†", "memory_lane_title": "莰åŋ†įēŋ{title}", "menu": "čœå•", "merge": "合åšļ", "merge_people": "合åšļäēēį‰Š", "merge_people_limit": "每æŦĄæœ€å¤šåĒčƒŊ合åšļ 5 ä¸Ēäēē", - "merge_people_prompt": "äŊ æƒŗåˆåšļčŋ™äē›äēē吗īŧŸč¯Ĩ操äŊœä¸å¯é€†īŧˆæ— æŗ•čĸĢæ’¤é”€īŧ‰ã€‚", + "merge_people_prompt": "įĄŽåŽščρ合åšļčŋ™äē›äēē吗īŧŸč¯Ĩ操äŊœä¸å¯é€†īŧˆæ— æŗ•čĸĢæ’¤é”€īŧ‰ã€‚", "merge_people_successfully": "合åšļäēēį‰ŠæˆåŠŸ", "merged_people_count": "厞合åšļ{count, plural, one {#ä¸Ēäēē} other {#ä¸Ēäēē}}", "minimize": "最小化", @@ -1218,8 +1245,13 @@ "missing": "įŧēå¤ą", "model": "åž‹åˇ", "month": "月", - "monthly_title_text_date_format": "MMMM y", "more": "更多", + "move": "į§ģ动", + "move_off_locked_folder": "į§ģå‡ē锁厚文äģļ多", + "move_to_locked_folder": "į§ģ动到锁厚文äģļ多", + "move_to_locked_folder_confirmation": "čŋ™äē›į…§į‰‡å’Œč§†éĸ‘å°†äģŽæ‰€æœ‰į›¸å†Œä¸­į§ģ除īŧŒåĒčƒŊ在锁厚文äģļ多中æŸĨįœ‹", + "moved_to_archive": "厞åŊ’æĄŖ {count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}", + "moved_to_library": "厞į§ģ动 {count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}} 到回åē“", "moved_to_trash": "åˇ˛æ”žå…Ĩ回æ”ļįĢ™", "multiselect_grid_edit_date_time_err_read_only": "æ— æŗ•įŧ–čž‘åĒč¯ģéĄšį›Žįš„æ—Ĩ期īŧŒčˇŗčŋ‡", "multiselect_grid_edit_gps_err_read_only": "æ— æŗ•įŧ–čž‘åĒč¯ģéĄšį›Žįš„äŊįŊŽäŋĄæ¯īŧŒčˇŗčŋ‡", @@ -1228,12 +1260,14 @@ "name": "åį§°", "name_or_nickname": "åį§°æˆ–æ˜ĩį§°", "networking_settings": "įŊ‘įģœ", - "networking_subtitle": "įŽĄį†æœåŠĄæŽĨåŖčŽžįŊŽ", + "networking_subtitle": "įŽĄį†æœåŠĄå™¨æŽĨåŖčŽžįŊŽ", "never": "永不čŋ‡æœŸ", "new_album": "æ–°į›¸å†Œ", "new_api_key": "新åĸž API Key", "new_password": "æ–°å¯†į ", "new_person": "新äēēį‰Š", + "new_pin_code": "æ–°įš„PIN᠁", + "new_pin_code_subtitle": "čŋ™æ˜¯æ‚¨įŦŦ一æŦĄčŽŋ闎此锁厚文äģļ多。创åģē一ä¸ĒPIN᠁äģĨ厉全čŽŋé—Žæ­¤éĄĩéĸ", "new_user_created": "åˇ˛åˆ›åģēæ–°į”¨æˆˇ", "new_version_available": "æœ‰æ–°į‰ˆæœŦ发布å•Ļ", "newest_first": "最新äŧ˜å…ˆ", @@ -1241,8 +1275,8 @@ "next_memory": "下一ä¸Ē", "no": "åĻ", "no_albums_message": "创åģēį›¸å†ŒäģĨæ•´į†į…§į‰‡å’Œč§†éĸ‘", - "no_albums_with_name_yet": "螌äŧŧäŊ čŋ˜æ˛Ąæœ‰æ­¤åå­—įš„į›¸å†Œã€‚", - "no_albums_yet": "螌äŧŧäŊ čŋ˜æ˛Ąæœ‰åˆ›åģēį›¸å†Œã€‚", + "no_albums_with_name_yet": "螌äŧŧ您čŋ˜æ˛Ąæœ‰æ­¤åå­—įš„į›¸å†Œã€‚", + "no_albums_yet": "螌äŧŧ您čŋ˜æ˛Ąæœ‰åˆ›åģēį›¸å†Œã€‚", "no_archived_assets_message": "åŊ’æĄŖį…§į‰‡å’Œč§†éĸ‘äģĨäžŋåœ¨į…§į‰‡č§†å›žä¸­éšč—åŽƒäģŦ", "no_assets_message": "į‚šå‡ģ上äŧ æ‚¨įš„įŦŦ一åŧ į…§į‰‡", "no_assets_to_show": "æ— éĄšį›Žåą•į¤ē", @@ -1250,8 +1284,11 @@ "no_exif_info_available": "æ˛Ąæœ‰å¯į”¨įš„ EXIF äŋĄæ¯", "no_explore_results_message": "上äŧ æ›´å¤šį…§į‰‡æĨæŽĸį´ĸ。", "no_favorites_message": "æˇģ加到æ”ļč—å¤šīŧŒåŋĢ速æŸĨ扞最äŊŗå›žį‰‡å’Œč§†éĸ‘", - "no_libraries_message": "创åģē外部回å瓿ĨæŸĨįœ‹äŊ įš„į…§į‰‡å’Œč§†éĸ‘", + "no_libraries_message": "创åģē外部回å瓿ĨæŸĨįœ‹æ‚¨įš„į…§į‰‡å’Œč§†éĸ‘", + "no_locked_photos_message": "锁厚文äģļå¤šä¸­įš„į…§į‰‡å’Œč§†éĸ‘å°†čĸĢ隐藏īŧŒä¸äŧšåœ¨æ‚¨æĩč§ˆæ‚¨įš„回å瓿—ļå‡ēįŽ°ã€‚", "no_name": "æœĒå‘Ŋ名", + "no_notifications": "æ˛Ąæœ‰é€šįŸĨ", + "no_people_found": "æœĒæ‰žåˆ°åŒšé…įš„äēēį‰Š", "no_places": "无äŊįŊŽ", "no_results": "无į쓿žœ", "no_results_description": "å°č¯•äŊŋį”¨åŒäš‰č¯æˆ–æ›´é€šį”¨įš„å…ŗé”Žč¯", @@ -1260,6 +1297,7 @@ "not_selected": "æœĒ选拊", "note_apply_storage_label_to_previously_uploaded assets": "提į¤ēīŧščĻå°†å­˜å‚¨æ ‡į­žåē”ᔍäēŽäš‹å‰ä¸Šäŧ įš„éĄšį›ŽīŧŒéœ€čρčŋčĄŒ", "notes": "提į¤ē", + "nothing_here_yet": "čŋ™é‡Œäģ€äšˆéƒŊæ˛Ąæœ‰", "notification_permission_dialog_content": "čĻå¯į”¨é€šįŸĨīŧŒč¯ˇčŊŦåˆ°â€œčŽžįŊŽâ€īŧŒåšļé€‰æ‹Šâ€œå…čŽ¸â€ã€‚", "notification_permission_list_tile_content": "授äēˆé€šįŸĨ权限。", "notification_permission_list_tile_enable_button": "å¯į”¨é€šįŸĨ", @@ -1267,7 +1305,6 @@ "notification_toggle_setting_description": "å¯į”¨é‚Žäģļ通įŸĨ", "notifications": "通įŸĨ", "notifications_setting_description": "įŽĄį†é€šįŸĨ", - "oauth": "OAuth", "official_immich_resources": "Immich 厘斚čĩ„æē", "offline": "įĻģįēŋ", "offline_paths": "įĻģįēŋ文äģļ", @@ -1279,15 +1316,16 @@ "onboarding_privacy_description": "äģĨ下īŧˆå¯é€‰īŧ‰åŠŸčƒŊ䞝čĩ–å¤–éƒ¨æœåŠĄīŧŒå¯éšæ—ļåœ¨įŽĄį†čŽžįŊŽä¸­įĻį”¨ã€‚", "onboarding_theme_description": "é€‰æ‹ŠæœåŠĄįš„éĸœč‰˛ä¸ģéĸ˜ã€‚į¨åŽå¯äģĨåœ¨čŽžįŊŽä¸­čŋ›čĄŒäŋŽæ”šã€‚", "onboarding_welcome_description": "我äģŦåœ¨å¯į”¨æœåŠĄå‰å…ˆåšä¸€äē›é€šį”¨čŽžįŊŽã€‚", - "onboarding_welcome_user": "æŦĸčŋŽäŊ īŧŒ{user}", + "onboarding_welcome_user": "æŦĸčŋŽīŧŒ{user}", "online": "在įēŋ", "only_favorites": "äģ…æ˜žį¤ē厞æ”ļ藏", + "open": "打åŧ€", "open_in_map_view": "åœ¨åœ°å›žč§†å›žä¸­æ‰“åŧ€", "open_in_openstreetmap": "在 OpenStreetMap 中打åŧ€", "open_the_search_filters": "打åŧ€æœį´ĸčŋ‡æģ¤å™¨", "options": "选项", "or": "或", - "organize_your_library": "æ•´į†äŊ įš„回åē“", + "organize_your_library": "æ•´į†æ‚¨įš„å›žåē“", "original": "原回", "other": "å…ļ厃", "other_devices": "å…ļåŽƒčŽžå¤‡", @@ -1305,7 +1343,7 @@ "partner_page_partner_add_failed": "æˇģ加同äŧ´å¤ąč´Ĩ", "partner_page_select_partner": "选拊同äŧ´", "partner_page_shared_to_title": "å…ąäēĢįģ™", - "partner_page_stop_sharing_content": "{} å°†æ— æŗ•å†čŽŋé—Žæ‚¨įš„į…§į‰‡ã€‚", + "partner_page_stop_sharing_content": "{partner} å°†æ— æŗ•å†čŽŋé—Žæ‚¨įš„į…§į‰‡ã€‚", "partner_sharing": "同äŧ´å…ąäēĢ", "partners": "同äŧ´", "password": "坆᠁", @@ -1351,6 +1389,10 @@ "photos_count": "{count, plural, one {{count, number}åŧ į…§į‰‡} other {{count, number}åŧ į…§į‰‡}}", "photos_from_previous_years": "čŋ‡åž€įš„ä슿˜”įžŦ间", "pick_a_location": "选拊äŊįŊŽ", + "pin_code_changed_successfully": "äŋŽæ”šPINį æˆåŠŸ", + "pin_code_reset_successfully": "重įŊŽPINį æˆåŠŸ", + "pin_code_setup_successfully": "莞įŊŽPINį æˆåŠŸ", + "pin_verification": "PIN᠁énj蝁", "place": "åœ°į‚š", "places": "åœ°į‚š", "places_count": "{count, plural, one {{count, number} ä¸Ēåœ°į‚š} other {{count, number} ä¸Ēåœ°į‚š}}", @@ -1358,6 +1400,7 @@ "play_memories": "播攞回åŋ†", "play_motion_photo": "æ’­æ”žåŠ¨æ€å›žį‰‡", "play_or_pause_video": "æ’­æ”žæˆ–æš‚åœč§†éĸ‘", + "please_auth_to_access": "蝎čŋ›čĄŒčēĢäģŊénj蝁äģĨčŽŋ问", "port": "įĢ¯åŖ", "preferences_settings_subtitle": "įŽĄį†åē”į”¨įš„ååĨŊ莞įŊŽ", "preferences_settings_title": "偏åĨŊ莞įŊŽ", @@ -1368,11 +1411,11 @@ "previous_or_next_photo": "上一åŧ æˆ–下一åŧ į…§į‰‡", "primary": "éĻ–čρ", "privacy": "隐ᧁ", + "profile": "č¯Ļ情", "profile_drawer_app_logs": "æ—Ĩåŋ—", "profile_drawer_client_out_of_date_major": "åŽĸæˆˇįĢ¯æœ‰å¤§į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_out_of_date_minor": "åŽĸæˆˇįĢ¯æœ‰å°į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_server_up_to_date": "åŽĸæˆˇįĢ¯å’ŒæœåŠĄį̝éƒŊæ˜¯æœ€æ–°įš„", - "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "æœåŠĄįĢ¯æœ‰å¤§į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_server_out_of_date_minor": "æœåŠĄįĢ¯æœ‰å°į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_image_of_user": "{user}įš„ä¸Ēäēēčĩ„æ–™å›žį‰‡", @@ -1381,7 +1424,7 @@ "public_share": "å…Ŧåŧ€å…ąäēĢ", "purchase_account_info": "æ”¯æŒč€…", "purchase_activated_subtitle": "感č°ĸ您寚 Immich 和åŧ€æēčŊ¯äģļįš„æ”¯æŒ", - "purchase_activated_time": "æŋ€æ´ģäēŽ{date, date}", + "purchase_activated_time": "æŋ€æ´ģäēŽ{date}", "purchase_activated_title": "æ‚¨įš„å¯†é’Ĩåˇ˛æˆåŠŸæŋ€æ´ģ", "purchase_button_activate": "æŋ€æ´ģ", "purchase_button_buy": "č´­äš°", @@ -1426,6 +1469,8 @@ "recent_searches": "最čŋ‘搜į´ĸ", "recently_added": "čŋ‘期æˇģ加", "recently_added_page_title": "最čŋ‘æˇģ加", + "recently_taken": "čŋ‘期拍摄", + "recently_taken_page_title": "最čŋ‘拍摄", "refresh": "åˆˇæ–°", "refresh_encoded_videos": "åˆˇæ–°åˇ˛įŧ–į įš„č§†éĸ‘", "refresh_faces": "åˆˇæ–°äēē脸", @@ -1445,6 +1490,8 @@ "remove_deleted_assets": "åŊģåē•删除文äģļ", "remove_from_album": "äģŽį›¸å†Œä¸­į§ģ除", "remove_from_favorites": "į§ģå‡ēæ”ļ藏", + "remove_from_locked_folder": "äģŽé”åŽšæ–‡äģļ多中į§ģ除", + "remove_from_locked_folder_confirmation": "æ‚¨įĄŽåŽščρ将čŋ™äē›į…§į‰‡å’Œč§†éĸ‘į§ģå‡ē锁厚文äģļ多吗īŧŸį§ģå‡ē后厃äģŦ将äŧšåœ¨æ‚¨įš„回åē“ä¸­å¯č§", "remove_from_shared_link": "äģŽå…ąäēĢ链æŽĨ中į§ģ除", "remove_memory": "į§ģå‡ē回åŋ†åŒē", "remove_photo_from_memory": "äģŽåŊ“前回åŋ†åŒēį§ģ除ᅧቇ", @@ -1468,6 +1515,7 @@ "reset": "重įŊŽ", "reset_password": "重įŊŽå¯†į ", "reset_people_visibility": "重įŊŽäēēį‰Šč¯†åˆĢ", + "reset_pin_code": "重įŊŽPIN᠁", "reset_to_default": "æĸ复éģ˜čޤå€ŧ", "resolve_duplicates": "å¤„į†é‡å¤éĄš", "resolved_all_duplicates": "å¤„į†æ‰€æœ‰é‡å¤éĄš", @@ -1540,7 +1588,7 @@ "search_result_page_new_search_hint": "搜į´ĸæ–°įš„", "search_settings": "搜į´ĸ莞įŊŽ", "search_state": "æŒ‰įœäģŊæŸĨ扞...", - "search_suggestion_list_smart_search_hint_1": "éģ˜čŽ¤æƒ…å†ĩ䏋吝ᔍæ™ēčƒŊ搜į´ĸīŧŒčĻæœį´ĸ元数捎īŧŒč¯ˇäŊŋį”¨į›¸å…ŗč¯­æŗ•", + "search_suggestion_list_smart_search_hint_1": "éģ˜čŽ¤æƒ…å†ĩ䏋吝ᔍæ™ēčƒŊ搜į´ĸīŧŒčĻæœį´ĸ元数捎īŧŒč¯ˇäŊŋį”¨į›¸å…ŗč¯­æŗ• ", "search_suggestion_list_smart_search_hint_2": "m:æ‚¨įš„æœį´ĸå…ŗé”Žč¯", "search_tags": "æŒ‰æ ‡į­žæŸĨ扞â€Ļ", "search_timezone": "按æ—ļåŒēæŸĨ扞...", @@ -1560,6 +1608,7 @@ "select_keep_all": "全部äŋį•™", "select_library_owner": "选拊回å瓿‰€æœ‰č€…", "select_new_face": "选拊新éĸ孔", + "select_person_to_tag": "选拊čĻæ ‡čŽ°įš„äēēį‰Š", "select_photos": "é€‰æ‹Šį…§į‰‡", "select_trash_all": "全部删除", "select_user_for_sharing_page_err_album": "创åģēį›¸å†Œå¤ąč´Ĩ", @@ -1567,7 +1616,7 @@ "selected_count": "{count, plural, other {#éĄšåˇ˛é€‰æ‹Š}}", "send_message": "发送æļˆæ¯", "send_welcome_email": "发送æŦĸčŋŽé‚Žäģļ", - "server_endpoint": "æœåŠĄæŽĨåŖ", + "server_endpoint": "æœåŠĄå™¨ URL", "server_info_box_app_version": "App į‰ˆæœŦ", "server_info_box_server_url": "æœåŠĄå™¨åœ°å€", "server_offline": "æœåŠĄå™¨įĻģįēŋ", @@ -1590,12 +1639,12 @@ "setting_languages_apply": "åē”ᔍ", "setting_languages_subtitle": "更攚åē”ᔍ蝭荀", "setting_languages_title": "蝭荀", - "setting_notifications_notify_failures_grace_period": "后台备äģŊå¤ąč´Ĩ通įŸĨīŧš{}", - "setting_notifications_notify_hours": "{} 小æ—ļ", + "setting_notifications_notify_failures_grace_period": "后台备äģŊå¤ąč´Ĩ通įŸĨīŧš{duration}", + "setting_notifications_notify_hours": "{count} 小æ—ļ", "setting_notifications_notify_immediately": "įĢ‹åŗ", - "setting_notifications_notify_minutes": "{} 分钟", + "setting_notifications_notify_minutes": "{count} 分钟", "setting_notifications_notify_never": "äģŽä¸", - "setting_notifications_notify_seconds": "{} į§’", + "setting_notifications_notify_seconds": "{count} į§’", "setting_notifications_single_progress_subtitle": "æ¯éĄšįš„č¯Ļįģ†ä¸Šäŧ čŋ›åēĻäŋĄæ¯", "setting_notifications_single_progress_title": "昞į¤ē后台备äģŊč¯Ļįģ†čŋ›åēĻ", "setting_notifications_subtitle": "č°ƒæ•´é€šįŸĨéĻ–é€‰éĄš", @@ -1607,10 +1656,12 @@ "settings": "莞įŊŽ", "settings_require_restart": "č¯ˇé‡å¯ Immich äģĨäŊŋ莞įŊŽį”Ÿæ•ˆ", "settings_saved": "莞įŊŽåˇ˛äŋå­˜", + "setup_pin_code": "莞įŊŽPIN᠁", "share": "å…ąäēĢ", "share_add_photos": "æˇģåŠ éĄšį›Ž", - "share_assets_selected": "{} åˇ˛é€‰æ‹Š", + "share_assets_selected": "{count} åˇ˛é€‰æ‹Š", "share_dialog_preparing": "æ­Ŗåœ¨å‡†å¤‡...", + "share_link": "分äēĢ链æŽĨ", "shared": "å…ąäēĢ", "shared_album_activities_input_disable": "蝄čŽē厞įρᔍ", "shared_album_activity_remove_content": "æ‚¨įĄŽåŽščĻåˆ é™¤æ­¤æ´ģ动吗īŧŸ", @@ -1621,36 +1672,35 @@ "shared_album_section_people_title": "äēēį‰Š", "shared_by": "å…ąäēĢč‡Ē", "shared_by_user": "į”ąâ€œ{user}â€å…ąäēĢ", - "shared_by_you": "äŊ įš„å…ąäēĢ", + "shared_by_you": "æ‚¨įš„å…ąäēĢ", "shared_from_partner": "æĨč‡Ē“{partner}â€įš„į…§į‰‡", - "shared_intent_upload_button_progress_text": "{} / {} 厞䏊äŧ ", + "shared_intent_upload_button_progress_text": "{current} / {total} 厞䏊äŧ ", "shared_link_app_bar_title": "å…ąäēĢ链æŽĨ", "shared_link_clipboard_copied_massage": "复åˆļ到å‰Ēč´´æŋ", - "shared_link_clipboard_text": "链æŽĨīŧš{}\n坆᠁īŧš{}", + "shared_link_clipboard_text": "链æŽĨīŧš{link}\n坆᠁īŧš{password}", "shared_link_create_error": "创åģēå…ąäēĢ链æŽĨå‡ē错", "shared_link_edit_description_hint": "įŧ–čž‘å…ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", - "shared_link_edit_expire_after_option_days": "{}夊", + "shared_link_edit_expire_after_option_days": "{count} 夊", "shared_link_edit_expire_after_option_hour": "1小æ—ļ", - "shared_link_edit_expire_after_option_hours": "{}小æ—ļ", + "shared_link_edit_expire_after_option_hours": "{count} 小æ—ļ", "shared_link_edit_expire_after_option_minute": "1分钟", - "shared_link_edit_expire_after_option_minutes": "{}分钟", - "shared_link_edit_expire_after_option_months": "{} ä¸Ē月", - "shared_link_edit_expire_after_option_year": "{} åš´", + "shared_link_edit_expire_after_option_minutes": "{count} 分钟", + "shared_link_edit_expire_after_option_months": "{count} 月鞄", + "shared_link_edit_expire_after_option_year": "{count} åš´", "shared_link_edit_password_hint": "输å…Ĩå…ąäēĢ坆᠁", "shared_link_edit_submit_button": "更新铞æŽĨ", "shared_link_error_server_url_fetch": "æ— æŗ•čŽˇå–æœåŠĄå™¨åœ°å€", - "shared_link_expires_day": "{}夊后čŋ‡æœŸ", - "shared_link_expires_days": "{}夊后čŋ‡æœŸ", - "shared_link_expires_hour": "{}小æ—ļ后čŋ‡æœŸ", - "shared_link_expires_hours": "{}小æ—ļ后čŋ‡æœŸ", - "shared_link_expires_minute": "{}分钟后čŋ‡æœŸ", - "shared_link_expires_minutes": "将在{}分钟后čŋ‡æœŸ", + "shared_link_expires_day": "{count} 夊后čŋ‡æœŸ", + "shared_link_expires_days": "{count} 夊后čŋ‡æœŸ", + "shared_link_expires_hour": "{count} 小æ—ļ后čŋ‡æœŸ", + "shared_link_expires_hours": "{count} 小æ—ļ后čŋ‡æœŸ", + "shared_link_expires_minute": "{count} 分钟后čŋ‡æœŸ", + "shared_link_expires_minutes": "{count} 分钟后čŋ‡æœŸ", "shared_link_expires_never": "čŋ‡æœŸæ—ļ间 ∞", - "shared_link_expires_second": "{}į§’åŽčŋ‡æœŸ", - "shared_link_expires_seconds": "将在{}į§’åŽčŋ‡æœŸ", + "shared_link_expires_second": "{count} į§’åŽčŋ‡æœŸ", + "shared_link_expires_seconds": "{count} į§’åŽčŋ‡æœŸ", "shared_link_individual_shared": "ä¸Ēäēēå…ąäēĢ", - "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "įŽĄį†å…ąäēĢ链æŽĨ", "shared_link_options": "å…ąäēĢ链æŽĨ选项", "shared_links": "å…ąäēĢ链æŽĨ", @@ -1719,17 +1769,18 @@ "status": "įŠļ态", "stop_motion_photo": "厚æ ŧᅧቇ", "stop_photo_sharing": "停æ­ĸå…ąäēĢᅧቇīŧŸ", - "stop_photo_sharing_description": "“{partner}”将不čƒŊčŽŋ问äŊ įš„į…§į‰‡ã€‚", + "stop_photo_sharing_description": "“{partner}”将不čƒŊčŽŋé—Žæ‚¨įš„į…§į‰‡ã€‚", "stop_sharing_photos_with_user": "停æ­ĸä¸Žæ­¤į”¨æˆˇå…ąäēĢᅧቇ", "storage": "存储įŠē间", "storage_label": "å­˜å‚¨æ ‡į­ž", + "storage_quota": "存储配éĸ", "storage_usage": "厞ᔍīŧš{used}/{available}", "submit": "提äē¤", "suggestions": "åģē莎", "sunrise_on_the_beach": "æĩˇæģŠä¸Šįš„æ—Ĩå‡ē", "support": "支持", "support_and_feedback": "支持和反éψ", - "support_third_party_description": "æ‚¨įš„ Immich åŽ‰čŖ…į¨‹åēæ˜¯į”ąįŦŦä¸‰æ–šæ‰“åŒ…įš„ã€‚æ‚¨é‡åˆ°įš„é—Žéĸ˜å¯čƒŊæ˜¯į”ąčŊ¯äģļ包åŧ•čĩˇįš„īŧŒå› æ­¤č¯ˇäŧ˜å…ˆäŊŋᔍ䏋éĸįš„é“žæŽĨ提å‡ēIssue或Bug。", + "support_third_party_description": "æ‚¨įš„ Immich åŽ‰čŖ…į¨‹åēæ˜¯į”ąįŦŦä¸‰æ–šæ‰“åŒ…įš„ã€‚æ‚¨é‡åˆ°įš„é—Žéĸ˜å¯čƒŊæ˜¯į”ąčŊ¯äģļ包åŧ•čĩˇįš„īŧŒå› æ­¤č¯ˇäŧ˜å…ˆäŊŋᔍ䏋éĸįš„é“žæŽĨ提å‡ē Issue 或 Bug。", "swap_merge_direction": "äē’æĸ合åšļ斚向", "sync": "同æ­Ĩ", "sync_albums": "同æ­Ĩį›¸å†Œ", @@ -1749,12 +1800,12 @@ "theme_selection": "ä¸ģéĸ˜é€‰éĄš", "theme_selection_description": "跟随æĩč§ˆå™¨č‡ĒåŠ¨čŽžįŊŽä¸ģéĸ˜éĸœč‰˛", "theme_setting_asset_list_storage_indicator_title": "åœ¨éĄšį›Žæ ‡éĸ˜ä¸Šæ˜žį¤ēå­˜å‚¨å į”¨", - "theme_setting_asset_list_tiles_per_row_title": "æ¯čĄŒåą•į¤ē {} 饚", - "theme_setting_colorful_interface_subtitle": "åē”ᔍä¸ģč‰˛č°ƒåˆ°čƒŒæ™¯", + "theme_setting_asset_list_tiles_per_row_title": "æ¯čĄŒåą•į¤ē {count} 饚", + "theme_setting_colorful_interface_subtitle": "åē”ᔍä¸ģč‰˛č°ƒåˆ°čƒŒæ™¯ã€‚", "theme_setting_colorful_interface_title": "åŊŠč‰˛į•Œéĸ", "theme_setting_image_viewer_quality_subtitle": "č°ƒæ•´æŸĨįœ‹å¤§å›žæ—ļįš„å›žåƒč´¨é‡", "theme_setting_image_viewer_quality_title": "å›žåƒč´¨é‡", - "theme_setting_primary_color_subtitle": "选拊éĸœč‰˛äŊœä¸ēä¸ģ色调", + "theme_setting_primary_color_subtitle": "选拊éĸœč‰˛äŊœä¸ēä¸ģč‰˛č°ƒã€‚", "theme_setting_primary_color_title": "ä¸ģ色调", "theme_setting_system_primary_color_title": "äŊŋᔍįŗģįģŸéĸœč‰˛", "theme_setting_system_theme_switch": "č‡Ē动īŧˆčˇŸéšįŗģįģŸčŽžįŊŽīŧ‰", @@ -1783,14 +1834,16 @@ "trash_emptied": "įŠē回æ”ļįĢ™", "trash_no_results_message": "åˆ é™¤įš„į…§į‰‡å’Œč§†éĸ‘å°†åœ¨æ­¤å¤„åą•į¤ē。", "trash_page_delete_all": "删除全部", - "trash_page_empty_trash_dialog_content": "是åĻ清įŠē回æ”ļįĢ™īŧŸčŋ™äē›éĄšį›Žå°†čĸĢäģŽImmich中永䚅删除", - "trash_page_info": "回æ”ļįĢ™ä¸­éĄšį›Žå°†åœ¨{}夊后永䚅删除", + "trash_page_empty_trash_dialog_content": "是åĻ清įŠē回æ”ļįĢ™īŧŸčŋ™äē›éĄšį›Žå°†čĸĢäģŽ Immich 中永䚅删除", + "trash_page_info": "回æ”ļįĢ™ä¸­éĄšį›Žå°†åœ¨ {days} 夊后永䚅删除", "trash_page_no_assets": "æš‚æ— åˇ˛åˆ é™¤éĄšį›Ž", "trash_page_restore_all": "æĸ复全部", "trash_page_select_assets_btn": "é€‰æ‹ŠéĄšį›Ž", - "trash_page_title": "回æ”ļįĢ™ ({})", + "trash_page_title": "回æ”ļįĢ™ ({count})", "trashed_items_will_be_permanently_deleted_after": "回æ”ļįĢ™ä¸­įš„éĄšį›Žå°†åœ¨{days, plural, one {#夊} other {#夊}}后čĸĢæ°¸äš…删除。", "type": "įąģ型", + "unable_to_change_pin_code": "æ— æŗ•äŋŽæ”šPIN᠁", + "unable_to_setup_pin_code": "æ— æŗ•čŽžįŊŽPIN᠁", "unarchive": "取æļˆåŊ’æĄŖ", "unarchived_count": "{count, plural, other {取æļˆåŊ’æĄŖ # 饚}}", "unfavorite": "取æļˆæ”ļ藏", @@ -1814,6 +1867,7 @@ "untracked_files": "æœĒ莟č¸Ēįš„æ–‡äģļ", "untracked_files_decription": "åē”ᔍፋåēä¸äŧščˇŸč¸Ēčŋ™ä盿–‡äģļ。厃äģŦ可čƒŊæ˜¯į”ąäēŽį§ģåŠ¨å¤ąč´Ĩ、上äŧ ä¸­æ–­æˆ–å› é”™č¯¯č€Œé—į•™", "up_next": "下一ä¸Ē", + "updated_at": "åˇ˛æ›´æ–°", "updated_password": "æ›´æ–°å¯†į ", "upload": "上äŧ ", "upload_concurrency": "上äŧ åšļ发", @@ -1826,15 +1880,18 @@ "upload_status_errors": "错蝝", "upload_status_uploaded": "厞䏊äŧ ", "upload_success": "上äŧ æˆåŠŸīŧŒåˆˇæ–°éĄĩéĸæŸĨįœ‹æ–°ä¸Šäŧ įš„éĄšį›Žã€‚", - "upload_to_immich": "上äŧ č‡ŗImmich ({})", + "upload_to_immich": "上äŧ č‡ŗ Immichīŧˆ{count}īŧ‰", "uploading": "æ­Ŗåœ¨ä¸Šäŧ ", - "url": "URL", "usage": "į”¨é‡", + "use_biometric": "äŊŋį”¨į”Ÿį‰Šč¯†åˆĢ", "use_current_connection": "äŊŋᔍåŊ“前čŋžæŽĨ", "use_custom_date_range": "č‡Ē厚䚉æ—ĨæœŸčŒƒå›´", "user": "į”¨æˆˇ", + "user_has_been_deleted": "æ­¤į”¨æˆˇåˇ˛čĸĢ删除。", "user_id": "į”¨æˆˇ ID", "user_liked": "“{user}â€į‚ščĩžäē†{type, select, photo {č¯Ĩᅧቇ} video {č¯Ĩ视éĸ‘} asset {č¯ĨéĄšį›Ž} other {厃}}", + "user_pin_code_settings": "PIN᠁", + "user_pin_code_settings_description": "įŽĄį†äŊ įš„PIN᠁", "user_purchase_settings": "č´­äš°", "user_purchase_settings_description": "įŽĄį†č´­äš°čŽĸ单", "user_role_set": "莞įŊŽâ€œ{user}”ä¸ē“{role}”", @@ -1845,15 +1902,15 @@ "users": "į”¨æˆˇ", "utilities": "åŽžį”¨åˇĨå…ˇ", "validate": "énj蝁", - "validate_endpoint_error": "č¯ˇčž“å…Ĩæœ‰æ•ˆįš„URL", + "validate_endpoint_error": "č¯ˇčž“å…Ĩæœ‰æ•ˆįš„ URL", "variables": "变量", "version": "į‰ˆæœŦ", - "version_announcement_closing": "äŊ įš„æœ‹å‹īŧŒAlex", - "version_announcement_message": "äŊ åĨŊīŧåˇ˛įģæŖ€æĩ‹åˆ°Immichæœ‰æ–°į‰ˆæœŦã€‚č¯ˇæŠŊįŠē阅č¯ģä¸€ä¸‹å‘čĄŒč¯´æ˜ŽīŧŒäģĨįĄŽäŋæ‚¨įš„配įŊŽæ–‡äģ￘¯æœ€æ–°įš„īŧŒéŋ免存在配įŊŽé”™č¯¯īŧŒį‰šåˆĢ是åŊ“äŊ æ˜¯äŊŋᔍWatchTower或å…ļ厃įąģäŧŧįš„č‡Ē动升įē§åˇĨå…ˇæ—ļ。", + "version_announcement_closing": "æ‚¨įš„æœ‹å‹īŧŒAlex", + "version_announcement_message": "您åĨŊīŧåˇ˛įģæŖ€æĩ‹åˆ° Immich æœ‰æ–°į‰ˆæœŦã€‚č¯ˇæŠŊįŠē阅č¯ģä¸€ä¸‹å‘čĄŒč¯´æ˜ŽīŧŒäģĨįĄŽäŋæ‚¨įš„配įŊŽæ–‡äģ￘¯æœ€æ–°įš„īŧŒéŋ免存在配įŊŽé”™č¯¯īŧŒį‰šåˆĢ是åŊ“您是äŊŋᔍ WatchTower 或å…ļ厃įąģäŧŧįš„č‡Ē动升įē§åˇĨå…ˇæ—ļ。", "version_announcement_overlay_release_notes": "å‘čĄŒč¯´æ˜Ž", "version_announcement_overlay_text_1": "åˇå¤–åˇå¤–īŧŒæœ‰æ–°į‰ˆæœŦįš„", - "version_announcement_overlay_text_2": "č¯ˇčŠąį‚šæ—ļ间čŽŋ问", - "version_announcement_overlay_text_3": "åšļæŖ€æŸĨæ‚¨įš„ docker-compose 和 .env 是åĻä¸ēæœ€æ–°ä¸”æ­ŖįĄŽįš„é…įŊŽīŧŒį‰šåˆĢ是您在äŊŋᔍ WatchTower æˆ–č€…å…ļäģ–č‡ĒåŠ¨æ›´æ–°įš„į¨‹åēæ—ļīŧŒæ‚¨éœ€čĻæ›´åŠ įģ†č‡´įš„æŖ€æŸĨ。", + "version_announcement_overlay_text_2": "č¯ˇčŠąį‚šæ—ļ间čŽŋ问 ", + "version_announcement_overlay_text_3": " åšļæŖ€æŸĨæ‚¨įš„ docker-compose 和 .env 是åĻä¸ēæœ€æ–°ä¸”æ­ŖįĄŽįš„é…įŊŽīŧŒį‰šåˆĢ是您在äŊŋᔍ WatchTower æˆ–č€…å…ļäģ–č‡ĒåŠ¨æ›´æ–°įš„į¨‹åēæ—ļīŧŒæ‚¨éœ€čĻæ›´åŠ įģ†č‡´įš„æŖ€æŸĨ。", "version_announcement_overlay_title": "æœåŠĄįĢ¯æœ‰æ–°į‰ˆæœŦå•Ļ 🎉", "version_history": "į‰ˆæœŦæ›´æ–°åŽ†å˛čŽ°åŊ•", "version_history_item": "在 {date} åŽ‰čŖ… {version} į‰ˆæœŦ", @@ -1884,6 +1941,7 @@ "welcome": "æŦĸčŋŽ", "welcome_to_immich": "æŦĸčŋŽäŊŋᔍ Immich", "wifi_name": "Wi-Fi åį§°", + "wrong_pin_code": "é”™č¯¯įš„PIN᠁", "year": "åš´", "years_ago": "{years, plural, one {#åš´} other {#åš´}}前", "yes": "是", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 25f3c44d9e..a462b5e696 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:0a9d314ae6e976351bd37b702bf6b0a89bb58e6304e5df35b960059b12531419 AS builder-cpu +FROM python:3.11-bookworm@sha256:ab60e444e04215a62671149f24c59cc2893b49cb5dad26f9d139077a86be760e AS builder-cpu FROM builder-cpu AS builder-openvino @@ -8,10 +8,13 @@ FROM builder-cpu AS builder-cuda FROM builder-cpu AS builder-armnn +# renovate: datasource=github-releases depName=ARM-software/armnn +ARG ARMNN_VERSION="v24.05" + ENV ARMNN_PATH=/opt/armnn COPY ann /opt/ann RUN mkdir /opt/armnn && \ - curl -SL "https://github.com/ARM-software/armnn/releases/download/v24.05/ArmNN-linux-aarch64.tar.gz" | tar -zx -C /opt/armnn && \ + curl -SL "https://github.com/ARM-software/armnn/releases/download/${ARMNN_VERSION}/ArmNN-linux-aarch64.tar.gz" | tar -zx -C /opt/armnn && \ cd /opt/ann && \ sh build.sh @@ -21,6 +24,8 @@ FROM builder-cpu AS builder-rknn # TODO: find a way to reduce the image size FROM rocm/dev-ubuntu-22.04:6.3.4-complete@sha256:1f7e92ca7e3a3785680473329ed1091fc99db3e90fcb3a1688f2933e870ed76b AS builder-rocm +# renovate: datasource=github-releases depName=Microsoft/onnxruntime +ARG ONNXRUNTIME_VERSION="v1.20.1" WORKDIR /code RUN apt-get update && apt-get install -y --no-install-recommends wget git python3.10-venv @@ -32,7 +37,7 @@ RUN wget -nv https://github.com/Kitware/CMake/releases/download/v3.30.1/cmake-3. ENV PATH=/code/cmake-3.30.1-linux-x86_64/bin:${PATH} -RUN git clone --single-branch --branch v1.20.1 --recursive "https://github.com/Microsoft/onnxruntime" onnxruntime +RUN git clone --single-branch --branch "${ONNXRUNTIME_VERSION}" --recursive "https://github.com/Microsoft/onnxruntime" onnxruntime WORKDIR /code/onnxruntime # Fix for multi-threading based on comments in https://github.com/microsoft/onnxruntime/pull/19567 # TODO: find a way to fix this without disabling algo caching @@ -42,7 +47,7 @@ RUN git apply /tmp/*.patch RUN /bin/sh ./dockerfiles/scripts/install_common_deps.sh # Note: the `parallel` setting uses a substantial amount of RAM RUN ./build.sh --allow_running_as_root --config Release --build_wheel --update --build --parallel 17 --cmake_extra_defines\ - ONNXRUNTIME_VERSION=1.20.1 --skip_tests --use_rocm --rocm_home=/opt/rocm + ONNXRUNTIME_VERSION="${ONNXRUNTIME_VERSION}" --skip_tests --use_rocm --rocm_home=/opt/rocm RUN mv /code/onnxruntime/build/Linux/Release/dist/*.whl /opt/ FROM builder-${DEVICE} AS builder @@ -54,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ RUN apt-get update && apt-get install -y --no-install-recommends g++ -COPY --from=ghcr.io/astral-sh/uv:latest@sha256:0b6dc79013b689f3bc0cbf12807cb1c901beaafe80f2ee10a1d76aa3842afb92 /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4a6c9444b126bd325fba904bff796bf91fb777bf6148d60109c4cb1de2ffc497 /uv /uvx /bin/ RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ @@ -63,17 +68,18 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:49d73c49616929b0a4f37c50fee0056eb4b0f15de624591e8d9bf84b4dfdd3ce AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:49d73c49616929b0a4f37c50fee0056eb4b0f15de624591e8d9bf84b4dfdd3ce AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-core_1.0.17384.11_amd64.deb && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-opencl_1.0.17384.11_amd64.deb && \ wget -nv https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/intel-opencl-icd_24.31.30508.7_amd64.deb && \ + # TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file wget -nv https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/libigdgmm12_22.4.1_amd64.deb && \ dpkg -i *.deb && \ rm *.deb && \ @@ -118,9 +124,12 @@ COPY --from=builder-armnn \ FROM prod-cpu AS prod-rknn +# renovate: datasource=github-tags depName=airockchip/rknn-toolkit2 +ARG RKNN_TOOLKIT_VERSION="v2.3.0" + ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -ADD --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/v2.3.0/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so /usr/lib/ +ADD --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 "https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/${RKNN_TOOLKIT_VERSION}/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so" /usr/lib/ FROM prod-${DEVICE} AS prod diff --git a/machine-learning/immich_ml/sessions/rknn/rknnpool.py b/machine-learning/immich_ml/sessions/rknn/rknnpool.py index fdcd053e71..fd0af8bcc4 100644 --- a/machine-learning/immich_ml/sessions/rknn/rknnpool.py +++ b/machine-learning/immich_ml/sessions/rknn/rknnpool.py @@ -53,7 +53,7 @@ def init_rknn(model_path: str) -> "RKNNLite": ret = rknn_lite.init_runtime() # Please do not set this parameter on other platforms. if ret != 0: - raise RuntimeError("Failed to inititalize RKNN runtime environment") + raise RuntimeError("Failed to initialize RKNN runtime environment") return rknn_lite diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 65011f9cab..22c6fad153 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -23,9 +23,9 @@ resolution-markers = [ name = "aiocache" version = "0.12.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199 }, + { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, ] [[package]] @@ -40,18 +40,18 @@ dependencies = [ { name = "scikit-image" }, { name = "scipy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371 } +sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371, upload-time = "2023-06-10T07:44:32.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706 }, + { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706, upload-time = "2023-06-10T07:44:30.373Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -64,9 +64,18 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770 } +sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770, upload-time = "2023-12-16T17:06:57.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481 }, + { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481, upload-time = "2023-12-16T17:06:55.989Z" }, +] + +[[package]] +name = "bidict" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093, upload-time = "2024-02-18T19:09:05.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload-time = "2024-02-18T19:09:04.156Z" }, ] [[package]] @@ -82,113 +91,113 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419 }, - { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080 }, - { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886 }, - { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404 }, - { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372 }, - { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865 }, - { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699 }, - { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028 }, - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, + { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, + { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, + { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, ] [[package]] name = "blinker" version = "1.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134, upload-time = "2023-11-01T22:06:01.588Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068 }, + { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068, upload-time = "2023-11-01T22:06:00.162Z" }, ] [[package]] name = "brotli" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045 }, - { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218 }, - { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872 }, - { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254 }, - { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293 }, - { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385 }, - { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104 }, - { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981 }, - { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297 }, - { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735 }, - { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107 }, - { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400 }, - { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985 }, - { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099 }, - { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172 }, - { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255 }, - { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068 }, - { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244 }, - { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500 }, - { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950 }, - { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527 }, - { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489 }, - { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080 }, - { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051 }, - { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172 }, - { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023 }, - { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871 }, - { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784 }, - { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905 }, - { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467 }, - { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169 }, - { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253 }, - { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693 }, - { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489 }, - { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081 }, - { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244 }, - { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505 }, - { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152 }, - { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252 }, - { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955 }, - { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304 }, - { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452 }, - { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751 }, - { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757 }, - { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146 }, - { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055 }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102 }, - { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029 }, - { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276 }, - { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255 }, - { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681 }, - { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475 }, - { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173 }, - { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803 }, - { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946 }, - { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707 }, - { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231 }, - { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157 }, - { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122 }, - { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206 }, - { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804 }, - { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517 }, + { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045, upload-time = "2023-09-07T14:03:16.894Z" }, + { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218, upload-time = "2023-09-07T14:03:18.917Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872, upload-time = "2023-09-07T14:03:20.398Z" }, + { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254, upload-time = "2023-09-07T14:03:21.914Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293, upload-time = "2023-09-07T14:03:24Z" }, + { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385, upload-time = "2023-09-07T14:03:26.248Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104, upload-time = "2023-09-07T14:03:27.849Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981, upload-time = "2023-09-07T14:03:29.92Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297, upload-time = "2023-09-07T14:03:32.035Z" }, + { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735, upload-time = "2023-09-07T14:03:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107, upload-time = "2024-10-18T12:32:09.016Z" }, + { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400, upload-time = "2024-10-18T12:32:11.134Z" }, + { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985, upload-time = "2024-10-18T12:32:12.813Z" }, + { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099, upload-time = "2024-10-18T12:32:14.733Z" }, + { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172, upload-time = "2023-09-07T14:03:35.212Z" }, + { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255, upload-time = "2023-09-07T14:03:36.447Z" }, + { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload-time = "2023-09-07T14:03:37.779Z" }, + { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload-time = "2023-09-07T14:03:39.223Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload-time = "2023-09-07T14:03:40.858Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload-time = "2023-09-07T14:03:42.896Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload-time = "2023-09-07T14:03:44.552Z" }, + { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload-time = "2023-09-07T14:03:46.594Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload-time = "2023-09-07T14:03:48.204Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload-time = "2023-09-07T14:03:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload-time = "2023-09-07T14:03:52.395Z" }, + { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload-time = "2023-09-07T14:03:53.96Z" }, + { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload-time = "2024-10-18T12:32:16.688Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload-time = "2024-10-18T12:32:18.459Z" }, + { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload-time = "2024-10-18T12:32:20.192Z" }, + { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload-time = "2024-10-18T12:32:21.774Z" }, + { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload-time = "2023-09-07T14:03:55.404Z" }, + { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload-time = "2023-09-07T14:03:56.643Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693, upload-time = "2024-10-18T12:32:23.824Z" }, + { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489, upload-time = "2024-10-18T12:32:25.641Z" }, + { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081, upload-time = "2023-09-07T14:03:57.967Z" }, + { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244, upload-time = "2023-09-07T14:03:59.319Z" }, + { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505, upload-time = "2023-09-07T14:04:01.327Z" }, + { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152, upload-time = "2023-09-07T14:04:03.033Z" }, + { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252, upload-time = "2023-09-07T14:04:04.675Z" }, + { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955, upload-time = "2023-09-07T14:04:06.585Z" }, + { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304, upload-time = "2023-09-07T14:04:08.668Z" }, + { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452, upload-time = "2023-09-07T14:04:10.736Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751, upload-time = "2023-09-07T14:04:12.875Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757, upload-time = "2023-09-07T14:04:14.551Z" }, + { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146, upload-time = "2024-10-18T12:32:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055, upload-time = "2024-10-18T12:32:29.376Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102, upload-time = "2024-10-18T12:32:31.371Z" }, + { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029, upload-time = "2024-10-18T12:32:33.293Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276, upload-time = "2023-09-07T14:04:16.49Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255, upload-time = "2023-09-07T14:04:17.83Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681, upload-time = "2024-10-18T12:32:34.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475, upload-time = "2024-10-18T12:32:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173, upload-time = "2024-10-18T12:32:37.978Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803, upload-time = "2024-10-18T12:32:39.606Z" }, + { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946, upload-time = "2024-10-18T12:32:41.679Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707, upload-time = "2024-10-18T12:32:43.478Z" }, + { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231, upload-time = "2024-10-18T12:32:45.224Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157, upload-time = "2024-10-18T12:32:46.894Z" }, + { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122, upload-time = "2024-10-18T12:32:48.844Z" }, + { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206, upload-time = "2024-10-18T12:32:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804, upload-time = "2024-10-18T12:32:52.661Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517, upload-time = "2024-10-18T12:32:54.066Z" }, ] [[package]] name = "certifi" version = "2023.11.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637, upload-time = "2023-11-18T02:54:02.397Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530 }, + { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530, upload-time = "2023-11-18T02:54:00.083Z" }, ] [[package]] @@ -198,108 +207,108 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, ] [[package]] name = "charset-normalizer" version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, - { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, - { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, - { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, - { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, - { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, - { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, - { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, - { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, - { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, - { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, - { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, - { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, - { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, - { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, - { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, - { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, - { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, - { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, - { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, - { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, - { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, - { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, - { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, + { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219, upload-time = "2023-11-01T04:02:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521, upload-time = "2023-11-01T04:02:32.452Z" }, + { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383, upload-time = "2023-11-01T04:02:34.11Z" }, + { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223, upload-time = "2023-11-01T04:02:36.213Z" }, + { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101, upload-time = "2023-11-01T04:02:38.067Z" }, + { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699, upload-time = "2023-11-01T04:02:39.436Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065, upload-time = "2023-11-01T04:02:41.357Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505, upload-time = "2023-11-01T04:02:43.108Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425, upload-time = "2023-11-01T04:02:45.427Z" }, + { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287, upload-time = "2023-11-01T04:02:46.705Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929, upload-time = "2023-11-01T04:02:48.098Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605, upload-time = "2023-11-01T04:02:49.605Z" }, + { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646, upload-time = "2023-11-01T04:02:51.35Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846, upload-time = "2023-11-01T04:02:52.679Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343, upload-time = "2023-11-01T04:02:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647, upload-time = "2023-11-01T04:02:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434, upload-time = "2023-11-01T04:02:57.173Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979, upload-time = "2023-11-01T04:02:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload-time = "2023-11-01T04:02:59.776Z" }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload-time = "2023-11-01T04:03:02.186Z" }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload-time = "2023-11-01T04:03:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload-time = "2023-11-01T04:03:05.983Z" }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload-time = "2023-11-01T04:03:07.567Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload-time = "2023-11-01T04:03:08.886Z" }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload-time = "2023-11-01T04:03:10.613Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload-time = "2023-11-01T04:03:11.973Z" }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload-time = "2023-11-01T04:03:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload-time = "2023-11-01T04:03:17.362Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509, upload-time = "2023-11-01T04:03:21.453Z" }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870, upload-time = "2023-11-01T04:03:22.723Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892, upload-time = "2023-11-01T04:03:24.135Z" }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213, upload-time = "2023-11-01T04:03:25.66Z" }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404, upload-time = "2023-11-01T04:03:27.04Z" }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275, upload-time = "2023-11-01T04:03:28.466Z" }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518, upload-time = "2023-11-01T04:03:29.82Z" }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182, upload-time = "2023-11-01T04:03:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869, upload-time = "2023-11-01T04:03:32.887Z" }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042, upload-time = "2023-11-01T04:03:34.412Z" }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275, upload-time = "2023-11-01T04:03:35.759Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819, upload-time = "2023-11-01T04:03:37.216Z" }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415, upload-time = "2023-11-01T04:03:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212, upload-time = "2023-11-01T04:03:40.07Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167, upload-time = "2023-11-01T04:03:41.491Z" }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041, upload-time = "2023-11-01T04:03:42.836Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397, upload-time = "2023-11-01T04:03:44.467Z" }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" }, ] [[package]] @@ -309,18 +318,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -330,18 +339,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, ] [[package]] name = "configargparse" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817 } +sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817, upload-time = "2023-07-23T16:20:04.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489 }, + { url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489, upload-time = "2023-07-23T16:20:03.27Z" }, ] [[package]] @@ -351,97 +360,97 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881 } +sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881, upload-time = "2023-11-03T17:01:03.144Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873 }, - { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211 }, - { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195 }, - { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279 }, - { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326 }, - { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732 }, - { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420 }, - { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204 }, - { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434 }, - { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652 }, - { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243 }, - { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408 }, - { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142 }, - { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129 }, - { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461 }, - { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352 }, - { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127 }, - { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561 }, - { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197 }, - { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556 }, - { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253 }, - { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555 }, - { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108 }, - { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810 }, - { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290 }, - { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937 }, - { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977 }, - { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442 }, - { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363 }, - { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731 }, + { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873, upload-time = "2023-11-03T16:56:34.548Z" }, + { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211, upload-time = "2023-11-03T16:56:38.028Z" }, + { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195, upload-time = "2023-11-03T16:56:41.598Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279, upload-time = "2023-11-03T16:56:46.08Z" }, + { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326, upload-time = "2023-11-03T16:56:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732, upload-time = "2023-11-03T16:56:53.773Z" }, + { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420, upload-time = "2023-11-03T16:57:00.669Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204, upload-time = "2023-11-03T16:57:07.813Z" }, + { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434, upload-time = "2023-11-03T16:57:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652, upload-time = "2023-11-03T16:57:13.57Z" }, + { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243, upload-time = "2023-11-03T16:57:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408, upload-time = "2023-11-03T16:57:20.021Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142, upload-time = "2023-11-03T16:57:23.48Z" }, + { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129, upload-time = "2023-11-03T16:57:27.141Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461, upload-time = "2023-11-03T16:57:30.537Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352, upload-time = "2023-11-03T16:57:34.937Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127, upload-time = "2023-11-03T16:57:42.201Z" }, + { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561, upload-time = "2023-11-03T16:57:49.667Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197, upload-time = "2023-11-03T16:57:52.682Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556, upload-time = "2023-11-03T16:57:55.286Z" }, + { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253, upload-time = "2023-11-03T16:57:58.572Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555, upload-time = "2023-11-03T16:58:01.48Z" }, + { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108, upload-time = "2023-11-03T16:58:05.546Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810, upload-time = "2023-11-03T16:58:09.568Z" }, + { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290, upload-time = "2023-11-03T16:58:13.017Z" }, + { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937, upload-time = "2023-11-03T16:58:16.426Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977, upload-time = "2023-11-03T16:58:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442, upload-time = "2023-11-03T16:58:30.724Z" }, + { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363, upload-time = "2023-11-03T16:58:33.54Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731, upload-time = "2023-11-03T16:58:36.585Z" }, ] [[package]] name = "coverage" version = "7.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716 } +sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716, upload-time = "2024-10-20T22:57:39.682Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713 }, - { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149 }, - { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584 }, - { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486 }, - { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649 }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744 }, - { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204 }, - { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335 }, - { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435 }, - { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243 }, - { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819 }, - { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263 }, - { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205 }, - { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612 }, - { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479 }, - { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405 }, - { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038 }, - { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812 }, - { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400 }, - { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243 }, - { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013 }, - { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251 }, - { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268 }, - { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298 }, - { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367 }, - { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853 }, - { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160 }, - { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824 }, - { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639 }, - { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428 }, - { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039 }, - { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298 }, - { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813 }, - { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959 }, - { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950 }, - { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610 }, - { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697 }, - { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541 }, - { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707 }, - { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439 }, - { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784 }, - { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058 }, - { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772 }, - { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490 }, - { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848 }, - { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340 }, - { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229 }, - { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510 }, - { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353 }, - { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502 }, - { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954 }, + { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713, upload-time = "2024-10-20T22:56:03.877Z" }, + { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149, upload-time = "2024-10-20T22:56:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584, upload-time = "2024-10-20T22:56:07.678Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486, upload-time = "2024-10-20T22:56:09.496Z" }, + { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649, upload-time = "2024-10-20T22:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744, upload-time = "2024-10-20T22:56:12.481Z" }, + { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204, upload-time = "2024-10-20T22:56:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335, upload-time = "2024-10-20T22:56:15.521Z" }, + { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435, upload-time = "2024-10-20T22:56:17.309Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243, upload-time = "2024-10-20T22:56:18.366Z" }, + { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819, upload-time = "2024-10-20T22:56:20.132Z" }, + { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263, upload-time = "2024-10-20T22:56:21.88Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205, upload-time = "2024-10-20T22:56:23.03Z" }, + { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612, upload-time = "2024-10-20T22:56:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479, upload-time = "2024-10-20T22:56:26.749Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405, upload-time = "2024-10-20T22:56:27.958Z" }, + { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038, upload-time = "2024-10-20T22:56:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812, upload-time = "2024-10-20T22:56:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400, upload-time = "2024-10-20T22:56:33.569Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243, upload-time = "2024-10-20T22:56:34.863Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013, upload-time = "2024-10-20T22:56:36.034Z" }, + { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251, upload-time = "2024-10-20T22:56:38.054Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268, upload-time = "2024-10-20T22:56:40.051Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298, upload-time = "2024-10-20T22:56:41.929Z" }, + { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367, upload-time = "2024-10-20T22:56:43.141Z" }, + { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853, upload-time = "2024-10-20T22:56:44.33Z" }, + { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160, upload-time = "2024-10-20T22:56:46.258Z" }, + { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824, upload-time = "2024-10-20T22:56:48.666Z" }, + { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639, upload-time = "2024-10-20T22:56:50.664Z" }, + { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428, upload-time = "2024-10-20T22:56:52.468Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039, upload-time = "2024-10-20T22:56:53.656Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298, upload-time = "2024-10-20T22:56:54.979Z" }, + { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813, upload-time = "2024-10-20T22:56:56.209Z" }, + { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959, upload-time = "2024-10-20T22:56:58.06Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950, upload-time = "2024-10-20T22:56:59.329Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610, upload-time = "2024-10-20T22:57:00.645Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697, upload-time = "2024-10-20T22:57:01.944Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541, upload-time = "2024-10-20T22:57:03.848Z" }, + { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707, upload-time = "2024-10-20T22:57:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439, upload-time = "2024-10-20T22:57:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784, upload-time = "2024-10-20T22:57:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058, upload-time = "2024-10-20T22:57:09.845Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772, upload-time = "2024-10-20T22:57:11.147Z" }, + { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490, upload-time = "2024-10-20T22:57:13.02Z" }, + { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848, upload-time = "2024-10-20T22:57:14.927Z" }, + { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340, upload-time = "2024-10-20T22:57:16.246Z" }, + { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229, upload-time = "2024-10-20T22:57:17.546Z" }, + { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510, upload-time = "2024-10-20T22:57:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353, upload-time = "2024-10-20T22:57:20.891Z" }, + { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502, upload-time = "2024-10-20T22:57:22.21Z" }, + { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954, upload-time = "2024-10-20T22:57:38.28Z" }, ] [package.optional-dependencies] @@ -453,57 +462,57 @@ toml = [ name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] [[package]] name = "cython" version = "3.0.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096 } +sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096, upload-time = "2024-01-10T11:01:02.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459 }, - { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626 }, - { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379 }, - { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873 }, - { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832 }, - { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325 }, - { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305 }, - { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113 }, - { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615 }, - { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320 }, - { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755 }, - { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099 }, - { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119 }, - { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418 }, - { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819 }, - { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167 }, - { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638 }, - { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052 }, - { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079 }, - { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972 }, - { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158 }, - { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312 }, - { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579 }, - { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268 }, - { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213 }, + { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459, upload-time = "2024-01-10T11:33:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626, upload-time = "2024-01-10T11:01:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379, upload-time = "2024-01-10T11:01:48.777Z" }, + { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873, upload-time = "2024-01-10T11:01:51.858Z" }, + { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832, upload-time = "2024-01-10T11:01:55.364Z" }, + { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325, upload-time = "2024-01-10T11:01:59.03Z" }, + { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305, upload-time = "2024-01-10T11:02:02.589Z" }, + { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113, upload-time = "2024-01-10T11:02:05.581Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615, upload-time = "2024-01-10T11:34:05.899Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320, upload-time = "2024-01-10T11:02:08.689Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755, upload-time = "2024-01-10T11:02:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099, upload-time = "2024-01-10T11:02:15.191Z" }, + { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119, upload-time = "2024-01-10T11:02:19.103Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418, upload-time = "2024-01-10T11:02:22.732Z" }, + { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819, upload-time = "2024-01-10T11:02:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167, upload-time = "2024-01-10T11:02:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638, upload-time = "2024-01-10T11:34:22.889Z" }, + { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052, upload-time = "2024-01-10T11:02:32.471Z" }, + { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079, upload-time = "2024-01-10T11:02:35.312Z" }, + { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972, upload-time = "2024-01-10T11:02:39.044Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158, upload-time = "2024-01-10T11:02:42.125Z" }, + { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312, upload-time = "2024-01-10T11:02:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579, upload-time = "2024-01-10T11:02:48.368Z" }, + { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268, upload-time = "2024-01-10T11:02:51.483Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213, upload-time = "2024-01-10T11:00:56.857Z" }, ] [[package]] name = "easydict" version = "1.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644, upload-time = "2023-10-23T23:01:37.686Z" } [[package]] name = "exceptiongroup" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264, upload-time = "2023-11-21T08:42:17.407Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }, + { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210, upload-time = "2023-11-21T08:42:15.525Z" }, ] [[package]] @@ -515,18 +524,18 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, + { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, ] [[package]] name = "filelock" version = "3.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553 } +sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553, upload-time = "2023-10-30T18:29:39.035Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740 }, + { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740, upload-time = "2023-10-30T18:29:37.267Z" }, ] [[package]] @@ -540,9 +549,9 @@ dependencies = [ { name = "jinja2" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171, upload-time = "2023-09-30T14:36:12.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724 }, + { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724, upload-time = "2023-09-30T14:36:10.961Z" }, ] [[package]] @@ -552,9 +561,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306 } +sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306, upload-time = "2024-05-04T19:49:43.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290 }, + { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290, upload-time = "2024-05-04T19:49:41.721Z" }, ] [[package]] @@ -565,60 +574,60 @@ dependencies = [ { name = "flask" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834 } +sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834, upload-time = "2023-10-30T14:53:21.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303 }, + { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303, upload-time = "2023-10-30T14:53:19.636Z" }, ] [[package]] name = "flatbuffers" version = "23.5.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114, upload-time = "2023-05-26T17:35:16.034Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744 }, + { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744, upload-time = "2023-05-26T17:35:14.269Z" }, ] [[package]] name = "fonttools" version = "4.47.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067, upload-time = "2024-01-11T11:22:45.293Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949 }, - { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336 }, - { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692 }, - { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529 }, - { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215 }, - { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778 }, - { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876 }, - { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937 }, - { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908 }, - { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501 }, - { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039 }, - { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166 }, - { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529 }, - { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414 }, - { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073 }, - { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744 }, - { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076 }, - { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784 }, - { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142 }, - { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241 }, - { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447 }, - { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265 }, - { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363 }, - { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866 }, - { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011 }, + { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949, upload-time = "2024-01-11T11:19:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336, upload-time = "2024-01-11T11:20:08.835Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692, upload-time = "2024-01-11T11:20:13.378Z" }, + { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529, upload-time = "2024-01-11T11:20:17.27Z" }, + { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215, upload-time = "2024-01-11T11:20:21.061Z" }, + { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778, upload-time = "2024-01-11T11:20:25.815Z" }, + { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876, upload-time = "2024-01-11T11:20:30.261Z" }, + { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937, upload-time = "2024-01-11T11:20:33.814Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908, upload-time = "2024-01-11T11:20:37.495Z" }, + { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501, upload-time = "2024-01-11T11:20:42.027Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039, upload-time = "2024-01-11T11:20:47.038Z" }, + { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166, upload-time = "2024-01-11T11:20:50.855Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529, upload-time = "2024-01-11T11:20:54.696Z" }, + { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414, upload-time = "2024-01-11T11:20:58.435Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073, upload-time = "2024-01-11T11:21:02.056Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744, upload-time = "2024-01-11T11:21:05.88Z" }, + { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076, upload-time = "2024-01-11T11:21:09.745Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784, upload-time = "2024-01-11T11:21:13.367Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142, upload-time = "2024-01-11T11:21:17.615Z" }, + { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241, upload-time = "2024-01-11T11:21:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447, upload-time = "2024-01-11T11:21:24.755Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265, upload-time = "2024-01-11T11:21:28.586Z" }, + { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363, upload-time = "2024-01-11T11:21:33.245Z" }, + { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866, upload-time = "2024-01-11T11:21:37.23Z" }, + { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011, upload-time = "2024-01-11T11:22:41.676Z" }, ] [[package]] name = "fsspec" version = "2023.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507 } +sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507, upload-time = "2023-12-11T21:19:54.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979 }, + { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979, upload-time = "2023-12-11T21:19:52.446Z" }, ] [[package]] @@ -628,9 +637,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, ] [[package]] @@ -643,41 +652,41 @@ dependencies = [ { name = "zope-event" }, { name = "zope-interface" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837 } +sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837, upload-time = "2024-10-18T16:06:25.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382 }, - { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460 }, - { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636 }, - { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031 }, - { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694 }, - { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063 }, - { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462 }, - { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631 }, - { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909 }, - { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247 }, - { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036 }, - { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299 }, - { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625 }, - { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079 }, - { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323 }, - { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468 }, - { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952 }, - { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230 }, - { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394 }, - { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989 }, - { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539 }, - { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842 }, - { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374 }, - { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701 }, - { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857 }, - { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676 }, - { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248 }, - { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304 }, - { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624 }, - { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356 }, - { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191 }, - { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117 }, - { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541 }, + { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382, upload-time = "2024-10-18T15:37:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460, upload-time = "2024-10-18T16:19:39.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636, upload-time = "2024-10-18T16:18:45.464Z" }, + { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031, upload-time = "2024-10-18T16:23:10.719Z" }, + { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694, upload-time = "2024-10-18T15:59:35.475Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063, upload-time = "2024-10-18T16:38:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462, upload-time = "2024-10-18T16:02:48.427Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631, upload-time = "2024-10-18T16:08:38.489Z" }, + { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909, upload-time = "2024-10-18T15:37:31.43Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247, upload-time = "2024-10-18T16:19:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036, upload-time = "2024-10-18T16:18:47.419Z" }, + { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299, upload-time = "2024-10-18T16:23:12.296Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625, upload-time = "2024-10-18T15:59:38.226Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079, upload-time = "2024-10-18T16:38:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323, upload-time = "2024-10-18T16:02:50.066Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468, upload-time = "2024-10-18T16:01:30.331Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952, upload-time = "2024-10-18T15:37:31.389Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230, upload-time = "2024-10-18T16:19:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394, upload-time = "2024-10-18T16:18:49.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989, upload-time = "2024-10-18T16:23:13.851Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539, upload-time = "2024-10-18T15:59:40.489Z" }, + { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842, upload-time = "2024-10-18T16:38:29.538Z" }, + { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374, upload-time = "2024-10-18T16:02:51.669Z" }, + { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701, upload-time = "2024-10-18T15:54:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857, upload-time = "2024-10-18T15:37:33.098Z" }, + { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676, upload-time = "2024-10-18T16:19:45.484Z" }, + { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248, upload-time = "2024-10-18T16:18:51.175Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304, upload-time = "2024-10-18T16:23:15.348Z" }, + { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624, upload-time = "2024-10-18T15:59:42.068Z" }, + { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356, upload-time = "2024-10-18T16:38:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191, upload-time = "2024-10-18T16:02:53.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117, upload-time = "2024-10-18T15:45:47.375Z" }, + { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541, upload-time = "2024-10-18T15:37:53.146Z" }, ] [[package]] @@ -690,103 +699,103 @@ dependencies = [ { name = "gevent" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345, upload-time = "2024-04-18T21:39:50.83Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729 }, - { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062 }, - { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645 }, - { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838 }, - { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272 }, - { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319 }, - { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705 }, - { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236 }, - { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859 }, - { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268 }, - { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426 }, - { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599 }, - { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302 }, - { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733 }, - { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060 }, - { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649 }, - { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987 }, - { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356 }, - { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460 }, - { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808 }, - { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049 }, - { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755 }, - { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053 }, - { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316 }, - { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598 }, - { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301 }, - { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742 }, - { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070 }, - { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650 }, - { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507 }, - { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061 }, - { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060 }, - { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762 }, - { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018 }, - { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884 }, - { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224 }, - { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758 }, - { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595 }, - { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271 }, - { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827 }, - { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017 }, - { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359 }, - { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262 }, - { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343 }, + { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729, upload-time = "2024-04-18T21:38:06.866Z" }, + { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062, upload-time = "2024-04-18T21:38:08.433Z" }, + { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645, upload-time = "2024-04-18T21:38:10.139Z" }, + { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838, upload-time = "2024-04-18T21:38:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272, upload-time = "2024-04-18T21:38:13.704Z" }, + { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319, upload-time = "2024-04-18T21:38:15.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705, upload-time = "2024-04-18T21:38:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236, upload-time = "2024-04-18T21:38:18.831Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859, upload-time = "2024-04-18T21:38:20.917Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268, upload-time = "2024-04-18T21:38:22.676Z" }, + { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426, upload-time = "2024-04-18T21:38:24.228Z" }, + { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599, upload-time = "2024-04-18T21:38:26.385Z" }, + { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302, upload-time = "2024-04-18T21:38:28.297Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733, upload-time = "2024-04-18T21:38:30.357Z" }, + { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060, upload-time = "2024-04-18T21:38:32.561Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649, upload-time = "2024-04-18T21:38:34.265Z" }, + { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987, upload-time = "2024-04-18T21:38:37.661Z" }, + { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356, upload-time = "2024-04-18T21:38:39.705Z" }, + { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460, upload-time = "2024-04-18T21:38:40.947Z" }, + { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808, upload-time = "2024-04-18T21:38:42.684Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049, upload-time = "2024-04-18T21:38:44.184Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755, upload-time = "2024-04-18T21:38:45.654Z" }, + { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053, upload-time = "2024-04-18T21:38:47.247Z" }, + { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316, upload-time = "2024-04-18T21:38:49.086Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598, upload-time = "2024-04-18T21:38:50.919Z" }, + { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301, upload-time = "2024-04-18T21:38:52.14Z" }, + { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742, upload-time = "2024-04-18T21:38:54.167Z" }, + { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070, upload-time = "2024-04-18T21:38:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650, upload-time = "2024-04-18T21:38:57.022Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507, upload-time = "2024-04-18T21:38:58.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061, upload-time = "2024-04-18T21:39:00.473Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060, upload-time = "2024-04-18T21:39:02.323Z" }, + { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762, upload-time = "2024-04-18T21:39:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018, upload-time = "2024-04-18T21:39:05.781Z" }, + { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884, upload-time = "2024-04-18T21:39:08.001Z" }, + { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224, upload-time = "2024-04-18T21:39:09.31Z" }, + { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758, upload-time = "2024-04-18T21:39:11.287Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595, upload-time = "2024-04-18T21:39:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271, upload-time = "2024-04-18T21:39:14.479Z" }, + { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827, upload-time = "2024-04-18T21:39:36.14Z" }, + { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017, upload-time = "2024-04-18T21:39:37.577Z" }, + { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359, upload-time = "2024-04-18T21:39:39.437Z" }, + { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262, upload-time = "2024-04-18T21:39:40.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343, upload-time = "2024-04-18T21:39:42.173Z" }, ] [[package]] name = "greenlet" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022, upload-time = "2024-09-20T18:21:04.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, - { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, - { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, - { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, - { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, - { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, - { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, - { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, - { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, - { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, - { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, - { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, - { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, - { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, - { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, - { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, - { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, - { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, - { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, - { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, - { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, - { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, - { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, - { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, - { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, - { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, - { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, - { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, - { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, - { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, - { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, - { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, - { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, - { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, - { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, - { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, - { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, - { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, - { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, - { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, - { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, - { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, - { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235, upload-time = "2024-09-20T17:07:18.761Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168, upload-time = "2024-09-20T17:36:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826, upload-time = "2024-09-20T17:39:16.921Z" }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443, upload-time = "2024-09-20T17:44:21.896Z" }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295, upload-time = "2024-09-20T17:08:37.951Z" }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544, upload-time = "2024-09-20T17:08:27.894Z" }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456, upload-time = "2024-09-20T17:44:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111, upload-time = "2024-09-20T17:09:22.104Z" }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392, upload-time = "2024-09-20T17:28:51.988Z" }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479, upload-time = "2024-09-20T17:07:22.332Z" }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404, upload-time = "2024-09-20T17:36:45.588Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813, upload-time = "2024-09-20T17:39:19.052Z" }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517, upload-time = "2024-09-20T17:44:24.101Z" }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831, upload-time = "2024-09-20T17:08:40.577Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413, upload-time = "2024-09-20T17:08:31.728Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619, upload-time = "2024-09-20T17:44:14.222Z" }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198, upload-time = "2024-09-20T17:09:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930, upload-time = "2024-09-20T17:25:18.656Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260, upload-time = "2024-09-20T17:08:07.301Z" }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064, upload-time = "2024-09-20T17:36:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420, upload-time = "2024-09-20T17:39:21.258Z" }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035, upload-time = "2024-09-20T17:44:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105, upload-time = "2024-09-20T17:08:42.048Z" }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077, upload-time = "2024-09-20T17:08:33.707Z" }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975, upload-time = "2024-09-20T17:44:15.989Z" }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955, upload-time = "2024-09-20T17:09:25.539Z" }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655, upload-time = "2024-09-20T17:21:22.427Z" }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990, upload-time = "2024-09-20T17:08:26.312Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175, upload-time = "2024-09-20T17:36:48.983Z" }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425, upload-time = "2024-09-20T17:39:22.705Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736, upload-time = "2024-09-20T17:44:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347, upload-time = "2024-09-20T17:08:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583, upload-time = "2024-09-20T17:08:36.85Z" }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039, upload-time = "2024-09-20T17:44:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716, upload-time = "2024-09-20T17:09:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490, upload-time = "2024-09-20T17:17:09.501Z" }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731, upload-time = "2024-09-20T17:36:50.376Z" }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304, upload-time = "2024-09-20T17:39:24.55Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537, upload-time = "2024-09-20T17:44:31.102Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506, upload-time = "2024-09-20T17:08:47.852Z" }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753, upload-time = "2024-09-20T17:08:38.079Z" }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731, upload-time = "2024-09-20T17:44:20.556Z" }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112, upload-time = "2024-09-20T17:09:28.753Z" }, ] [[package]] @@ -796,18 +805,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, ] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/be/58f20728a5b445f8b064e74f0618897b3439f5ef90934da1916b9dfac76f/hf_xet-1.1.2.tar.gz", hash = "sha256:3712d6d4819d3976a1c18e36db9f503e296283f9363af818f50703506ed63da3", size = 467009, upload-time = "2025-05-16T20:44:34.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/ae/f1a63f75d9886f18a80220ba31a1c7b9c4752f03aae452f358f538c6a991/hf_xet-1.1.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dfd1873fd648488c70735cb60f7728512bca0e459e61fcd107069143cd798469", size = 2642559, upload-time = "2025-05-16T20:44:30.217Z" }, + { url = "https://files.pythonhosted.org/packages/50/ab/d2c83ae18f1015d926defd5bfbe94c62d15e93f900e6a192e318ee947105/hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:29b584983b2d977c44157d9241dcf0fd50acde0b7bff8897fe4386912330090d", size = 2541360, upload-time = "2025-05-16T20:44:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a7/693dc9f34f979e30a378125e2150a0b2d8d166e6d83ce3950eeb81e560aa/hf_xet-1.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b29ac84298147fe9164cc55ad994ba47399f90b5d045b0b803b99cf5f06d8ec", size = 5183081, upload-time = "2025-05-16T20:44:27.505Z" }, + { url = "https://files.pythonhosted.org/packages/3d/23/c48607883f692a36c0a7735f47f98bad32dbe459a32d1568c0f21576985d/hf_xet-1.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d921ba32615676e436a0d15e162331abc9ed43d440916b1d836dc27ce1546173", size = 5356100, upload-time = "2025-05-16T20:44:25.681Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5b/b2316c7f1076da0582b52ea228f68bea95e243c388440d1dc80297c9d813/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d9b03c34e13c44893ab6e8fea18ee8d2a6878c15328dd3aabedbdd83ee9f2ed3", size = 5647688, upload-time = "2025-05-16T20:44:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/2c/98/e6995f0fa579929da7795c961f403f4ee84af36c625963f52741d56f242c/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01b18608955b3d826307d37da8bd38b28a46cd2d9908b3a3655d1363274f941a", size = 5322627, upload-time = "2025-05-16T20:44:33.677Z" }, + { url = "https://files.pythonhosted.org/packages/59/40/8f1d5a44a64d8bf9e3c19576e789f716af54875b46daae65426714e75db1/hf_xet-1.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:3562902c81299b09f3582ddfb324400c6a901a2f3bc854f83556495755f4954c", size = 2739542, upload-time = "2025-05-16T20:44:36.287Z" }, ] [[package]] @@ -818,45 +842,45 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036 } +sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036, upload-time = "2023-11-10T13:37:42.496Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943 }, + { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943, upload-time = "2023-11-10T13:37:40.937Z" }, ] [[package]] name = "httptools" version = "0.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780 }, - { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297 }, - { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130 }, - { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148 }, - { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949 }, - { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591 }, - { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344 }, - { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029 }, - { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492 }, - { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891 }, - { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788 }, - { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214 }, - { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120 }, - { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565 }, - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 }, - { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 }, - { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 }, - { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 }, - { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 }, - { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 }, - { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 }, - { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 }, + { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload-time = "2024-10-16T19:44:06.882Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297, upload-time = "2024-10-16T19:44:08.129Z" }, + { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130, upload-time = "2024-10-16T19:44:09.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148, upload-time = "2024-10-16T19:44:11.539Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949, upload-time = "2024-10-16T19:44:13.388Z" }, + { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591, upload-time = "2024-10-16T19:44:15.258Z" }, + { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344, upload-time = "2024-10-16T19:44:16.54Z" }, + { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload-time = "2024-10-16T19:44:18.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload-time = "2024-10-16T19:44:19.515Z" }, + { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload-time = "2024-10-16T19:44:21.067Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload-time = "2024-10-16T19:44:22.958Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload-time = "2024-10-16T19:44:24.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload-time = "2024-10-16T19:44:26.295Z" }, + { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload-time = "2024-10-16T19:44:29.188Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, + { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, + { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, + { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, ] [[package]] @@ -869,27 +893,28 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "huggingface-hub" -version = "0.30.2" +version = "0.32.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "packaging" }, { name = "pyyaml" }, { name = "requests" }, { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868 } +sdist = { url = "https://files.pythonhosted.org/packages/d0/76/44f7025d1b3f29336aeb7324a57dd7c19f7c69f6612b7637b39ac7c17302/huggingface_hub-0.32.2.tar.gz", hash = "sha256:64a288b1eadad6b60bbfd50f0e52fd6cfa2ef77ab13c3e8a834a038ae929de54", size = 422847, upload-time = "2025-05-27T09:23:00.306Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433 }, + { url = "https://files.pythonhosted.org/packages/32/30/532fe57467a6cc7ff2e39f088db1cb6d6bf522f724a4a5c7beda1282d5a6/huggingface_hub-0.32.2-py3-none-any.whl", hash = "sha256:f8fcf14603237eadf96dbe577d30b330f8c27b4a0a31e8f6c94fdc25e021fdb8", size = 509968, upload-time = "2025-05-27T09:22:57.967Z" }, ] [[package]] @@ -899,18 +924,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] [[package]] name = "idna" version = "3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 } +sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426, upload-time = "2023-11-25T15:40:54.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 }, + { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567, upload-time = "2023-11-25T15:40:52.604Z" }, ] [[package]] @@ -921,9 +946,9 @@ dependencies = [ { name = "numpy" }, { name = "pillow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374 } +sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374, upload-time = "2023-12-11T02:26:44.715Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345 }, + { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345, upload-time = "2023-12-11T02:26:42.724Z" }, ] [[package]] @@ -1080,9 +1105,9 @@ types = [ name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, ] [[package]] @@ -1104,15 +1129,15 @@ dependencies = [ { name = "scipy" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490, upload-time = "2023-04-02T08:01:54.541Z" } [[package]] name = "itsdangerous" version = "2.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143, upload-time = "2022-03-24T15:12:15.102Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749 }, + { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749, upload-time = "2022-03-24T15:12:13.2Z" }, ] [[package]] @@ -1122,93 +1147,94 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245, upload-time = "2024-05-05T23:42:02.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271, upload-time = "2024-05-05T23:41:59.928Z" }, ] [[package]] name = "joblib" version = "1.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720 } +sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720, upload-time = "2023-08-09T09:23:40.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207 }, + { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207, upload-time = "2023-08-09T09:23:34.583Z" }, ] [[package]] name = "kiwisolver" version = "1.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552, upload-time = "2023-08-24T09:30:39.861Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397 }, - { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125 }, - { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211 }, - { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145 }, - { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849 }, - { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921 }, - { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009 }, - { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819 }, - { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054 }, - { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613 }, - { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650 }, - { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415 }, - { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094 }, - { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585 }, - { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095 }, - { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403 }, - { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156 }, - { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166 }, - { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300 }, - { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579 }, - { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360 }, - { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091 }, - { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259 }, - { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516 }, - { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228 }, - { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716 }, - { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871 }, - { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265 }, - { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649 }, - { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116 }, - { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484 }, - { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332 }, - { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987 }, - { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613 }, - { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183 }, - { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248 }, - { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815 }, - { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042 }, - { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159 }, - { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694 }, - { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579 }, - { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168 }, - { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464 }, - { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473 }, - { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004 }, + { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397, upload-time = "2023-08-24T09:28:18.105Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125, upload-time = "2023-08-24T09:28:19.218Z" }, + { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211, upload-time = "2023-08-24T09:28:20.241Z" }, + { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145, upload-time = "2023-08-24T09:28:21.439Z" }, + { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849, upload-time = "2023-08-24T09:28:23.004Z" }, + { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921, upload-time = "2023-08-24T09:28:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009, upload-time = "2023-08-24T09:28:25.636Z" }, + { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819, upload-time = "2023-08-24T09:28:27.547Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054, upload-time = "2023-08-24T09:28:28.839Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613, upload-time = "2023-08-24T09:28:30.351Z" }, + { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650, upload-time = "2023-08-24T09:28:32.303Z" }, + { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415, upload-time = "2023-08-24T09:28:34.141Z" }, + { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094, upload-time = "2023-08-24T09:28:35.97Z" }, + { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585, upload-time = "2023-08-24T09:28:37.326Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095, upload-time = "2023-08-24T09:28:38.325Z" }, + { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403, upload-time = "2023-08-24T09:28:39.3Z" }, + { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156, upload-time = "2023-08-24T09:28:40.301Z" }, + { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166, upload-time = "2023-08-24T09:28:41.235Z" }, + { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300, upload-time = "2023-08-24T09:28:42.409Z" }, + { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579, upload-time = "2023-08-24T09:28:43.677Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360, upload-time = "2023-08-24T09:28:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091, upload-time = "2023-08-24T09:28:47.959Z" }, + { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259, upload-time = "2023-08-24T09:28:49.224Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516, upload-time = "2023-08-24T09:28:50.979Z" }, + { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228, upload-time = "2023-08-24T09:28:52.812Z" }, + { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716, upload-time = "2023-08-24T09:28:54.115Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871, upload-time = "2023-08-24T09:28:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265, upload-time = "2023-08-24T09:28:56.855Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649, upload-time = "2023-08-24T09:28:58.021Z" }, + { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116, upload-time = "2023-08-24T09:28:58.994Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484, upload-time = "2023-08-24T09:28:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332, upload-time = "2023-08-24T09:29:01.733Z" }, + { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987, upload-time = "2023-08-24T09:29:02.789Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613, upload-time = "2023-08-24T09:29:03.912Z" }, + { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183, upload-time = "2023-08-24T09:29:05.244Z" }, + { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248, upload-time = "2023-08-24T09:29:06.531Z" }, + { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815, upload-time = "2023-08-24T09:29:07.867Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042, upload-time = "2023-08-24T09:29:09.403Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159, upload-time = "2023-08-24T09:29:10.66Z" }, + { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694, upload-time = "2023-08-24T09:29:12.469Z" }, + { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579, upload-time = "2023-08-24T09:29:13.743Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168, upload-time = "2023-08-24T09:29:15.097Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464, upload-time = "2023-08-24T09:29:16.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473, upload-time = "2023-08-24T09:29:17.956Z" }, + { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004, upload-time = "2023-08-24T09:29:19.329Z" }, ] [[package]] name = "lazy-loader" version = "0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268, upload-time = "2023-06-30T21:12:55.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087 }, + { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087, upload-time = "2023-06-30T21:12:51.09Z" }, ] [[package]] name = "locust" -version = "2.34.1" +version = "2.37.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, { name = "flask" }, { name = "flask-cors" }, { name = "flask-login" }, - { name = "gevent", marker = "python_full_version != '3.13.*'" }, + { name = "gevent" }, { name = "geventhttpclient" }, + { name = "locust-cloud" }, { name = "msgpack" }, { name = "psutil" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, @@ -1219,9 +1245,25 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/21/c2bfe4f9482f8754e9a1ff2b1840a1abe63640576fc918a67a02fff7d961/locust-2.34.1.tar.gz", hash = "sha256:184a6ffcb0d6c543bbeae4de65cbb198c7e0739d569d48a2b8bf5db962077733", size = 2240533 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/d1/60d5fddac2baa47314c091636868b50178a38fc71ce39d68afd847448028/locust-2.37.5.tar.gz", hash = "sha256:c90824c4cb6a01cdede220684c7c8381253fcca47fc689dbca4f6c46d740c99f", size = 2252000, upload-time = "2025-05-22T08:54:58.676Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/e4/0944fbfb1ce0bf09cb400ed9349d4cbaed1230114e4018ac28805097f1c6/locust-2.34.1-py3-none-any.whl", hash = "sha256:487bfadd584e3320f9862adf5aa1cfa1023e030a6af414f4e0a92e62617ce451", size = 2257910 }, + { url = "https://files.pythonhosted.org/packages/e0/a0/32a51fb48f96b0de6bb6ea7308f68b7ae1bae53e6b975672f8c4ef7f8c08/locust-2.37.5-py3-none-any.whl", hash = "sha256:9922a2718b42f1c57a05c822e47b66555b3c61292694ec5edaf7a166fac6d112", size = 2268626, upload-time = "2025-05-22T08:54:55.938Z" }, +] + +[[package]] +name = "locust-cloud" +version = "1.21.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "configargparse" }, + { name = "gevent" }, + { name = "platformdirs" }, + { name = "python-socketio", extra = ["client"] }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/d4/64a169b4831d26ab9dceacb192ea30c749501d87b4958e628cf1f7ef45c4/locust_cloud-1.21.8.tar.gz", hash = "sha256:e8bde0da013c8731a45cc834cdf9fec2fc21738a5f2807d93c2c5eeb3008a80e", size = 450414, upload-time = "2025-05-22T08:30:27.458Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/76/aa8b2f73bdf7de5ee344e5d0c4749e8d62ff38257b41d9df37b0b7ac84e2/locust_cloud-1.21.8-py3-none-any.whl", hash = "sha256:4f06b5d8a26ba91840a768008f4870965b13cc71481de9797409556de2edc007", size = 407879, upload-time = "2025-05-22T08:30:25.512Z" }, ] [[package]] @@ -1231,47 +1273,47 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "markupsafe" version = "2.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132, upload-time = "2023-06-02T21:43:45.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846 }, - { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720 }, - { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498 }, - { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691 }, - { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366 }, - { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505 }, - { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616 }, - { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891 }, - { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525 }, - { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083 }, - { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862 }, - { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738 }, - { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891 }, - { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096 }, - { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631 }, - { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863 }, - { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591 }, - { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186 }, - { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537 }, - { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089 }, - { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849 }, - { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700 }, - { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319 }, - { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314 }, - { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696 }, - { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746 }, - { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131 }, - { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878 }, - { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426 }, - { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979 }, + { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846, upload-time = "2023-06-02T21:42:33.954Z" }, + { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720, upload-time = "2023-06-02T21:42:35.102Z" }, + { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498, upload-time = "2023-06-02T21:42:36.608Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691, upload-time = "2023-06-02T21:42:37.778Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366, upload-time = "2023-06-02T21:42:39.441Z" }, + { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505, upload-time = "2023-06-02T21:42:41.088Z" }, + { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616, upload-time = "2023-06-02T21:42:42.273Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891, upload-time = "2023-06-02T21:42:43.635Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525, upload-time = "2023-06-02T21:42:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083, upload-time = "2023-06-02T21:42:46.948Z" }, + { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862, upload-time = "2023-06-02T21:42:48.569Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738, upload-time = "2023-06-02T21:42:49.727Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891, upload-time = "2023-06-02T21:42:51.33Z" }, + { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096, upload-time = "2023-06-02T21:42:52.966Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631, upload-time = "2023-06-02T21:42:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863, upload-time = "2023-06-02T21:42:55.777Z" }, + { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591, upload-time = "2023-06-02T21:42:57.415Z" }, + { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186, upload-time = "2023-06-02T21:42:59.107Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537, upload-time = "2023-06-02T21:43:00.927Z" }, + { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089, upload-time = "2023-06-02T21:43:02.355Z" }, + { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849, upload-time = "2023-09-07T16:00:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700, upload-time = "2023-09-07T16:00:45.384Z" }, + { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319, upload-time = "2023-09-07T16:00:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314, upload-time = "2023-09-07T16:00:47.64Z" }, + { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696, upload-time = "2023-09-07T16:00:48.92Z" }, + { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746, upload-time = "2023-09-07T16:00:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131, upload-time = "2023-09-07T16:00:51.822Z" }, + { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878, upload-time = "2023-09-07T16:00:53.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426, upload-time = "2023-09-07T16:00:55.987Z" }, + { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979, upload-time = "2023-09-07T16:00:57.77Z" }, ] [[package]] @@ -1289,85 +1331,85 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957, upload-time = "2023-11-17T21:16:40.15Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640 }, - { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350 }, - { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388 }, - { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959 }, - { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938 }, - { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836 }, - { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458 }, - { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840 }, - { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332 }, - { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911 }, - { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260 }, - { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742 }, - { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988 }, - { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594 }, - { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843 }, - { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608 }, - { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252 }, - { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067 }, + { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640, upload-time = "2023-11-17T21:17:02.834Z" }, + { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350, upload-time = "2023-11-17T21:17:12.281Z" }, + { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388, upload-time = "2023-11-17T21:17:26.461Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959, upload-time = "2023-11-17T21:17:40.541Z" }, + { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938, upload-time = "2023-11-17T21:17:49.925Z" }, + { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836, upload-time = "2023-11-17T21:17:58.379Z" }, + { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458, upload-time = "2023-11-17T21:18:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840, upload-time = "2023-11-17T21:18:13.706Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332, upload-time = "2023-11-17T21:18:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911, upload-time = "2023-11-17T21:18:35.27Z" }, + { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260, upload-time = "2023-11-17T21:18:44.836Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742, upload-time = "2023-11-17T21:18:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988, upload-time = "2023-11-17T21:19:01.119Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594, upload-time = "2023-11-17T21:19:09.865Z" }, + { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843, upload-time = "2023-11-17T21:19:20.46Z" }, + { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608, upload-time = "2023-11-17T21:19:31.363Z" }, + { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252, upload-time = "2023-11-17T21:19:42.271Z" }, + { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067, upload-time = "2023-11-17T21:19:50.091Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] name = "msgpack" version = "1.0.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311, upload-time = "2023-09-28T13:20:36.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827 }, - { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959 }, - { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970 }, - { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440 }, - { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797 }, - { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372 }, - { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287 }, - { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715 }, - { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614 }, - { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340 }, - { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828 }, - { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096 }, - { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210 }, - { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952 }, - { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511 }, - { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980 }, - { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547 }, - { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669 }, - { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353 }, - { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455 }, - { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367 }, - { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860 }, - { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759 }, - { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330 }, - { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537 }, - { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561 }, - { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009 }, - { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882 }, - { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949 }, - { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836 }, - { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587 }, - { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509 }, - { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287 }, + { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827, upload-time = "2023-09-28T13:18:30.258Z" }, + { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959, upload-time = "2023-09-28T13:18:32.146Z" }, + { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970, upload-time = "2023-09-28T13:18:34.134Z" }, + { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440, upload-time = "2023-09-28T13:18:35.866Z" }, + { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797, upload-time = "2023-09-28T13:18:37.653Z" }, + { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372, upload-time = "2023-09-28T13:18:39.685Z" }, + { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287, upload-time = "2023-09-28T13:18:41.051Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715, upload-time = "2023-09-28T13:18:42.883Z" }, + { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614, upload-time = "2023-09-28T13:18:44.679Z" }, + { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340, upload-time = "2023-09-28T13:18:46.588Z" }, + { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828, upload-time = "2023-09-28T13:18:47.875Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096, upload-time = "2023-09-28T13:18:49.678Z" }, + { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210, upload-time = "2023-09-28T13:18:51.039Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952, upload-time = "2023-09-28T13:18:52.871Z" }, + { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511, upload-time = "2023-09-28T13:18:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980, upload-time = "2023-09-28T13:18:56.058Z" }, + { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547, upload-time = "2023-09-28T13:18:57.396Z" }, + { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669, upload-time = "2023-09-28T13:18:58.957Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353, upload-time = "2023-09-28T13:19:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455, upload-time = "2023-09-28T13:19:03.201Z" }, + { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367, upload-time = "2023-09-28T13:19:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860, upload-time = "2023-09-28T13:19:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759, upload-time = "2023-09-28T13:19:08.148Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330, upload-time = "2023-09-28T13:19:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537, upload-time = "2023-09-28T13:19:10.898Z" }, + { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561, upload-time = "2023-09-28T13:19:12.779Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009, upload-time = "2023-09-28T13:19:14.373Z" }, + { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882, upload-time = "2023-09-28T13:19:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949, upload-time = "2023-09-28T13:19:18.114Z" }, + { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836, upload-time = "2023-09-28T13:19:19.729Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587, upload-time = "2023-09-28T13:19:21.666Z" }, + { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509, upload-time = "2023-09-28T13:19:23.161Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287, upload-time = "2023-09-28T13:19:25.097Z" }, ] [[package]] @@ -1379,83 +1421,83 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload-time = "2025-02-05T03:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, - { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, - { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, - { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, - { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, - { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, - { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, - { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, - { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, - { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, - { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, - { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, - { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, - { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, - { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, - { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, - { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, - { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, - { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, - { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, - { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, - { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, - { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, - { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, - { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, + { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433, upload-time = "2025-02-05T03:49:29.145Z" }, + { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472, upload-time = "2025-02-05T03:49:16.986Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424, upload-time = "2025-02-05T03:49:46.908Z" }, + { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450, upload-time = "2025-02-05T03:50:05.89Z" }, + { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765, upload-time = "2025-02-05T03:49:33.56Z" }, + { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701, upload-time = "2025-02-05T03:49:38.981Z" }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload-time = "2025-02-05T03:50:17.287Z" }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload-time = "2025-02-05T03:49:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload-time = "2025-02-05T03:50:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload-time = "2025-02-05T03:49:42.408Z" }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload-time = "2025-02-05T03:49:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload-time = "2025-02-05T03:49:54.581Z" }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload-time = "2025-02-05T03:50:28.25Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload-time = "2025-02-05T03:50:13.411Z" }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload-time = "2025-02-05T03:50:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload-time = "2025-02-05T03:48:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload-time = "2025-02-05T03:49:03.628Z" }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload-time = "2025-02-05T03:50:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592, upload-time = "2025-02-05T03:48:55.789Z" }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611, upload-time = "2025-02-05T03:48:44.581Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443, upload-time = "2025-02-05T03:49:25.514Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541, upload-time = "2025-02-05T03:49:57.623Z" }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348, upload-time = "2025-02-05T03:48:52.361Z" }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648, upload-time = "2025-02-05T03:49:11.395Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload-time = "2025-02-05T03:50:08.348Z" }, ] [[package]] name = "mypy-extensions" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload-time = "2023-02-04T12:11:27.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, ] [[package]] name = "networkx" version = "3.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload-time = "2023-10-28T08:41:39.364Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772 }, + { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload-time = "2023-10-28T08:41:36.945Z" }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, - { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, - { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, - { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, - { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, - { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, - { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, - { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, - { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, - { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, - { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, - { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, - { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, - { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, - { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, - { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, - { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" }, ] [[package]] @@ -1466,31 +1508,31 @@ dependencies = [ { name = "numpy" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017, upload-time = "2024-03-25T15:33:46.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483 }, - { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939 }, - { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856 }, - { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279 }, - { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703 }, - { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099 }, - { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376 }, - { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839 }, - { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944 }, - { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450 }, - { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808 }, - { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905 }, - { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304 }, - { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538 }, - { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415 }, - { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224 }, - { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234 }, - { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591 }, + { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483, upload-time = "2024-03-25T15:25:07.947Z" }, + { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939, upload-time = "2024-03-25T15:25:11.632Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856, upload-time = "2024-03-25T15:25:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279, upload-time = "2024-03-25T15:25:18.939Z" }, + { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703, upload-time = "2024-03-25T15:25:22.611Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099, upload-time = "2024-03-25T15:25:25.05Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376, upload-time = "2024-03-25T15:25:27.899Z" }, + { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839, upload-time = "2024-03-25T15:25:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944, upload-time = "2024-03-25T15:25:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450, upload-time = "2024-03-25T15:25:37.983Z" }, + { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808, upload-time = "2024-03-25T15:25:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905, upload-time = "2024-03-25T15:25:42.905Z" }, + { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304, upload-time = "2024-03-25T15:25:45.875Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538, upload-time = "2024-03-25T15:25:49.396Z" }, + { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415, upload-time = "2024-03-25T15:25:51.929Z" }, + { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224, upload-time = "2024-03-25T15:25:55.049Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234, upload-time = "2024-03-25T15:25:57.998Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591, upload-time = "2024-03-25T15:26:01.252Z" }, ] [[package]] name = "onnxruntime" -version = "1.20.1" +version = "1.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coloredlogs" }, @@ -1501,27 +1543,24 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/28/99f903b0eb1cd6f3faa0e343217d9fb9f47b84bca98bd9859884631336ee/onnxruntime-1.20.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:e50ba5ff7fed4f7d9253a6baf801ca2883cc08491f9d32d78a80da57256a5439", size = 30996314 }, - { url = "https://files.pythonhosted.org/packages/6d/c6/c4c0860bee2fde6037bdd9dcd12d323f6e38cf00fcc9a5065b394337fc55/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b2908b50101a19e99c4d4e97ebb9905561daf61829403061c1adc1b588bc0de", size = 11954010 }, - { url = "https://files.pythonhosted.org/packages/63/47/3dc0b075ab539f16b3d8b09df6b504f51836086ee709690a6278d791737d/onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d82daaec24045a2e87598b8ac2b417b1cce623244e80e663882e9fe1aae86410", size = 13330452 }, - { url = "https://files.pythonhosted.org/packages/27/ef/80fab86289ecc01a734b7ddf115dfb93d8b2e004bd1e1977e12881c72b12/onnxruntime-1.20.1-cp310-cp310-win32.whl", hash = "sha256:4c4b251a725a3b8cf2aab284f7d940c26094ecd9d442f07dd81ab5470e99b83f", size = 9813849 }, - { url = "https://files.pythonhosted.org/packages/a9/e6/33ab10066c9875a29d55e66ae97c3bf91b9b9b987179455d67c32261a49c/onnxruntime-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:d3b616bb53a77a9463707bb313637223380fc327f5064c9a782e8ec69c22e6a2", size = 11329702 }, - { url = "https://files.pythonhosted.org/packages/95/8d/2634e2959b34aa8a0037989f4229e9abcfa484e9c228f99633b3241768a6/onnxruntime-1.20.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:06bfbf02ca9ab5f28946e0f912a562a5f005301d0c419283dc57b3ed7969bb7b", size = 30998725 }, - { url = "https://files.pythonhosted.org/packages/a5/da/c44bf9bd66cd6d9018a921f053f28d819445c4d84b4dd4777271b0fe52a2/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6243e34d74423bdd1edf0ae9596dd61023b260f546ee17d701723915f06a9f7", size = 11955227 }, - { url = "https://files.pythonhosted.org/packages/11/ac/4120dfb74c8e45cce1c664fc7f7ce010edd587ba67ac41489f7432eb9381/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eec64c0269dcdb8d9a9a53dc4d64f87b9e0c19801d9321246a53b7eb5a7d1bc", size = 13331703 }, - { url = "https://files.pythonhosted.org/packages/12/f1/cefacac137f7bb7bfba57c50c478150fcd3c54aca72762ac2c05ce0532c1/onnxruntime-1.20.1-cp311-cp311-win32.whl", hash = "sha256:a19bc6e8c70e2485a1725b3d517a2319603acc14c1f1a017dda0afe6d4665b41", size = 9813977 }, - { url = "https://files.pythonhosted.org/packages/2c/2d/2d4d202c0bcfb3a4cc2b171abb9328672d7f91d7af9ea52572722c6d8d96/onnxruntime-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:8508887eb1c5f9537a4071768723ec7c30c28eb2518a00d0adcd32c89dea3221", size = 11329895 }, - { url = "https://files.pythonhosted.org/packages/e5/39/9335e0874f68f7d27103cbffc0e235e32e26759202df6085716375c078bb/onnxruntime-1.20.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:22b0655e2bf4f2161d52706e31f517a0e54939dc393e92577df51808a7edc8c9", size = 31007580 }, - { url = "https://files.pythonhosted.org/packages/c5/9d/a42a84e10f1744dd27c6f2f9280cc3fb98f869dd19b7cd042e391ee2ab61/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f56e898815963d6dc4ee1c35fc6c36506466eff6d16f3cb9848cea4e8c8172", size = 11952833 }, - { url = "https://files.pythonhosted.org/packages/47/42/2f71f5680834688a9c81becbe5c5bb996fd33eaed5c66ae0606c3b1d6a02/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb71a814f66517a65628c9e4a2bb530a6edd2cd5d87ffa0af0f6f773a027d99e", size = 13333903 }, - { url = "https://files.pythonhosted.org/packages/c8/f1/aabfdf91d013320aa2fc46cf43c88ca0182860ff15df872b4552254a9680/onnxruntime-1.20.1-cp312-cp312-win32.whl", hash = "sha256:bd386cc9ee5f686ee8a75ba74037750aca55183085bf1941da8efcfe12d5b120", size = 9814562 }, - { url = "https://files.pythonhosted.org/packages/dd/80/76979e0b744307d488c79e41051117634b956612cc731f1028eb17ee7294/onnxruntime-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:19c2d843eb074f385e8bbb753a40df780511061a63f9def1b216bf53860223fb", size = 11331482 }, - { url = "https://files.pythonhosted.org/packages/f7/71/c5d980ac4189589267a06f758bd6c5667d07e55656bed6c6c0580733ad07/onnxruntime-1.20.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:cc01437a32d0042b606f462245c8bbae269e5442797f6213e36ce61d5abdd8cc", size = 31007574 }, - { url = "https://files.pythonhosted.org/packages/81/0d/13bbd9489be2a6944f4a940084bfe388f1100472f38c07080a46fbd4ab96/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb44b08e017a648924dbe91b82d89b0c105b1adcfe31e90d1dc06b8677ad37be", size = 11951459 }, - { url = "https://files.pythonhosted.org/packages/c0/ea/4454ae122874fd52bbb8a961262de81c5f932edeb1b72217f594c700d6ef/onnxruntime-1.20.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda6aebdf7917c1d811f21d41633df00c58aff2bef2f598f69289c1f1dabc4b3", size = 13331620 }, - { url = "https://files.pythonhosted.org/packages/d8/e0/50db43188ca1c945decaa8fc2a024c33446d31afed40149897d4f9de505f/onnxruntime-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:d30367df7e70f1d9fc5a6a68106f5961686d39b54d3221f760085524e8d38e16", size = 11331758 }, - { url = "https://files.pythonhosted.org/packages/d8/55/3821c5fd60b52a6c82a00bba18531793c93c4addfe64fbf061e235c5617a/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9158465745423b2b5d97ed25aa7740c7d38d2993ee2e5c3bfacb0c4145c49d8", size = 11950342 }, - { url = "https://files.pythonhosted.org/packages/14/56/fd990ca222cef4f9f4a9400567b9a15b220dee2eafffb16b2adbc55c8281/onnxruntime-1.20.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0df6f2df83d61f46e842dbcde610ede27218947c33e994545a22333491e72a3b", size = 13337040 }, + { url = "https://files.pythonhosted.org/packages/67/3c/c99b21646a782b89c33cffd96fdee02a81bc43f0cb651de84d58ec11e30e/onnxruntime-1.22.0-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:85d8826cc8054e4d6bf07f779dc742a363c39094015bdad6a08b3c18cfe0ba8c", size = 34273493, upload-time = "2025-05-09T20:25:55.66Z" }, + { url = "https://files.pythonhosted.org/packages/54/ab/fd9a3b5285008c060618be92e475337fcfbf8689787953d37273f7b52ab0/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:468c9502a12f6f49ec335c2febd22fdceecc1e4cc96dfc27e419ba237dff5aff", size = 14445346, upload-time = "2025-05-09T20:25:41.322Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ca/a5625644bc079e04e3076a5ac1fb954d1e90309b8eb987a4f800732ffee6/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:681fe356d853630a898ee05f01ddb95728c9a168c9460e8361d0a240c9b7cb97", size = 16392959, upload-time = "2025-05-09T20:26:09.047Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6b/8267490476e8d4dd1883632c7e46a4634384c7ff1c35ae44edc8ab0bb7a9/onnxruntime-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:20bca6495d06925631e201f2b257cc37086752e8fe7b6c83a67c6509f4759bc9", size = 12689974, upload-time = "2025-05-12T21:26:09.704Z" }, + { url = "https://files.pythonhosted.org/packages/7a/08/c008711d1b92ff1272f4fea0fbee57723171f161d42e5c680625535280af/onnxruntime-1.22.0-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:8d6725c5b9a681d8fe72f2960c191a96c256367887d076b08466f52b4e0991df", size = 34282151, upload-time = "2025-05-09T20:25:59.246Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8b/22989f6b59bc4ad1324f07a945c80b9ab825f0a581ad7a6064b93716d9b7/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef17d665a917866d1f68f09edc98223b9a27e6cb167dec69da4c66484ad12fd", size = 14446302, upload-time = "2025-05-09T20:25:44.299Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d5/aa83d084d05bc8f6cf8b74b499c77431ffd6b7075c761ec48ec0c161a47f/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b978aa63a9a22095479c38371a9b359d4c15173cbb164eaad5f2cd27d666aa65", size = 16393496, upload-time = "2025-05-09T20:26:11.588Z" }, + { url = "https://files.pythonhosted.org/packages/89/a5/1c6c10322201566015183b52ef011dfa932f5dd1b278de8d75c3b948411d/onnxruntime-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:03d3ef7fb11adf154149d6e767e21057e0e577b947dd3f66190b212528e1db31", size = 12691517, upload-time = "2025-05-12T21:26:13.354Z" }, + { url = "https://files.pythonhosted.org/packages/4d/de/9162872c6e502e9ac8c99a98a8738b2fab408123d11de55022ac4f92562a/onnxruntime-1.22.0-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:f3c0380f53c1e72a41b3f4d6af2ccc01df2c17844072233442c3a7e74851ab97", size = 34298046, upload-time = "2025-05-09T20:26:02.399Z" }, + { url = "https://files.pythonhosted.org/packages/03/79/36f910cd9fc96b444b0e728bba14607016079786adf032dae61f7c63b4aa/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8601128eaef79b636152aea76ae6981b7c9fc81a618f584c15d78d42b310f1c", size = 14443220, upload-time = "2025-05-09T20:25:47.078Z" }, + { url = "https://files.pythonhosted.org/packages/8c/60/16d219b8868cc8e8e51a68519873bdb9f5f24af080b62e917a13fff9989b/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6964a975731afc19dc3418fad8d4e08c48920144ff590149429a5ebe0d15fb3c", size = 16406377, upload-time = "2025-05-09T20:26:14.478Z" }, + { url = "https://files.pythonhosted.org/packages/36/b4/3f1c71ce1d3d21078a6a74c5483bfa2b07e41a8d2b8fb1e9993e6a26d8d3/onnxruntime-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0d534a43d1264d1273c2d4f00a5a588fa98d21117a3345b7104fa0bbcaadb9a", size = 12692233, upload-time = "2025-05-12T21:26:16.963Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/5cb5018d5b0b7cba820d2c4a1d1b02d40df538d49138ba36a509457e4df6/onnxruntime-1.22.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:fe7c051236aae16d8e2e9ffbfc1e115a0cc2450e873a9c4cb75c0cc96c1dae07", size = 34298715, upload-time = "2025-05-09T20:26:05.634Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/1dfe1b368831d1256b90b95cb8d11da8ab769febd5c8833ec85ec1f79d21/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a6bbed10bc5e770c04d422893d3045b81acbbadc9fb759a2cd1ca00993da919", size = 14443266, upload-time = "2025-05-09T20:25:49.479Z" }, + { url = "https://files.pythonhosted.org/packages/1e/70/342514ade3a33ad9dd505dcee96ff1f0e7be6d0e6e9c911fe0f1505abf42/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe45ee3e756300fccfd8d61b91129a121d3d80e9d38e01f03ff1295badc32b8", size = 16406707, upload-time = "2025-05-09T20:26:17.454Z" }, + { url = "https://files.pythonhosted.org/packages/3e/89/2f64e250945fa87140fb917ba377d6d0e9122e029c8512f389a9b7f953f4/onnxruntime-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:5a31d84ef82b4b05d794a4ce8ba37b0d9deb768fd580e36e17b39e0b4840253b", size = 12691777, upload-time = "2025-05-12T21:26:20.19Z" }, + { url = "https://files.pythonhosted.org/packages/9f/48/d61d5f1ed098161edd88c56cbac49207d7b7b149e613d2cd7e33176c63b3/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2ac5bd9205d831541db4e508e586e764a74f14efdd3f89af7fd20e1bf4a1ed", size = 14454003, upload-time = "2025-05-09T20:25:52.287Z" }, + { url = "https://files.pythonhosted.org/packages/c3/16/873b955beda7bada5b0d798d3a601b2ff210e44ad5169f6d405b93892103/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64845709f9e8a2809e8e009bc4c8f73b788cee9c6619b7d9930344eae4c9cd36", size = 16427482, upload-time = "2025-05-09T20:26:20.376Z" }, ] [[package]] @@ -1558,10 +1597,10 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800 }, - { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263 }, - { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927 }, - { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464 }, + { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800, upload-time = "2024-06-25T06:30:37.042Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263, upload-time = "2024-06-24T13:38:15.906Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927, upload-time = "2024-06-25T06:30:43.765Z" }, + { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464, upload-time = "2024-06-24T13:38:18.437Z" }, ] [[package]] @@ -1571,171 +1610,175 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" }, ] [[package]] name = "orjson" -version = "3.10.16" +version = "3.10.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/c7/03913cc4332174071950acf5b0735463e3f63760c80585ef369270c2b372/orjson-3.10.16.tar.gz", hash = "sha256:d2aaa5c495e11d17b9b93205f5fa196737ee3202f000aaebf028dc9a73750f10", size = 5410415 } +sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/a6/22cb9b03baf167bc2d659c9e74d7580147f36e6a155e633801badfd5a74d/orjson-3.10.16-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4cb473b8e79154fa778fb56d2d73763d977be3dcc140587e07dbc545bbfc38f8", size = 249179 }, - { url = "https://files.pythonhosted.org/packages/d7/ce/3e68cc33020a6ebd8f359b8628b69d2132cd84fea68155c33057e502ee51/orjson-3.10.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:622a8e85eeec1948690409a19ca1c7d9fd8ff116f4861d261e6ae2094fe59a00", size = 138510 }, - { url = "https://files.pythonhosted.org/packages/dc/12/63bee7764ce12052f7c1a1393ce7f26dc392c93081eb8754dd3dce9b7c6b/orjson-3.10.16-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c682d852d0ce77613993dc967e90e151899fe2d8e71c20e9be164080f468e370", size = 132373 }, - { url = "https://files.pythonhosted.org/packages/b3/d5/2998c2f319adcd572f2b03ba2083e8176863d1055d8d713683ddcf927b71/orjson-3.10.16-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c520ae736acd2e32df193bcff73491e64c936f3e44a2916b548da048a48b46b", size = 136774 }, - { url = "https://files.pythonhosted.org/packages/00/03/88c236ae307bd0604623204d4a835e15fbf9c75b8535c8f13ef45abd413f/orjson-3.10.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:134f87c76bfae00f2094d85cfab261b289b76d78c6da8a7a3b3c09d362fd1e06", size = 138030 }, - { url = "https://files.pythonhosted.org/packages/66/ba/3e256ddfeb364f98fd6ac65774844090d356158b2d1de8998db2bf984503/orjson-3.10.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b59afde79563e2cf37cfe62ee3b71c063fd5546c8e662d7fcfc2a3d5031a5c4c", size = 142677 }, - { url = "https://files.pythonhosted.org/packages/2c/71/73a1214bd27baa2ea5184fff4aa6193a114dfb0aa5663dad48fe63e8cd29/orjson-3.10.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:113602f8241daaff05d6fad25bd481d54c42d8d72ef4c831bb3ab682a54d9e15", size = 132798 }, - { url = "https://files.pythonhosted.org/packages/53/ac/0b2f41c0a1e8c095439d0fab3b33103cf41a39be8e6aa2c56298a6034259/orjson-3.10.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4fc0077d101f8fab4031e6554fc17b4c2ad8fdbc56ee64a727f3c95b379e31da", size = 135450 }, - { url = "https://files.pythonhosted.org/packages/d9/ca/7524c7b0bc815d426ca134dab54cad519802287b808a3846b047a5b2b7a3/orjson-3.10.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9c6bf6ff180cd69e93f3f50380224218cfab79953a868ea3908430bcfaf9cb5e", size = 412356 }, - { url = "https://files.pythonhosted.org/packages/05/1d/3ae2367c255276bf16ff7e1b210dd0af18bc8da20c4e4295755fc7de1268/orjson-3.10.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5673eadfa952f95a7cd76418ff189df11b0a9c34b1995dff43a6fdbce5d63bf4", size = 152769 }, - { url = "https://files.pythonhosted.org/packages/d3/2d/8eb10b6b1d30bb69c35feb15e5ba5ac82466cf743d562e3e8047540efd2f/orjson-3.10.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5fe638a423d852b0ae1e1a79895851696cb0d9fa0946fdbfd5da5072d9bb9551", size = 137223 }, - { url = "https://files.pythonhosted.org/packages/47/42/f043717930cb2de5fbebe47f308f101bed9ec2b3580b1f99c8284b2f5fe8/orjson-3.10.16-cp310-cp310-win32.whl", hash = "sha256:33af58f479b3c6435ab8f8b57999874b4b40c804c7a36b5cc6b54d8f28e1d3dd", size = 141734 }, - { url = "https://files.pythonhosted.org/packages/67/99/795ad7282b425b9fddcfb8a31bded5dcf84dba78ecb1e7ae716e84e794da/orjson-3.10.16-cp310-cp310-win_amd64.whl", hash = "sha256:0338356b3f56d71293c583350af26f053017071836b07e064e92819ecf1aa055", size = 133779 }, - { url = "https://files.pythonhosted.org/packages/97/29/43f91a5512b5d2535594438eb41c5357865fd5e64dec745d90a588820c75/orjson-3.10.16-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44fcbe1a1884f8bc9e2e863168b0f84230c3d634afe41c678637d2728ea8e739", size = 249180 }, - { url = "https://files.pythonhosted.org/packages/0c/36/2a72d55e266473c19a86d97b7363bb8bf558ab450f75205689a287d5ce61/orjson-3.10.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78177bf0a9d0192e0b34c3d78bcff7fe21d1b5d84aeb5ebdfe0dbe637b885225", size = 138510 }, - { url = "https://files.pythonhosted.org/packages/bb/ad/f86d6f55c1a68b57ff6ea7966bce5f4e5163f2e526ddb7db9fc3c2c8d1c4/orjson-3.10.16-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12824073a010a754bb27330cad21d6e9b98374f497f391b8707752b96f72e741", size = 132373 }, - { url = "https://files.pythonhosted.org/packages/5e/8b/d18f2711493a809f3082a88fda89342bc8e16767743b909cd3c34989fba3/orjson-3.10.16-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddd41007e56284e9867864aa2f29f3136bb1dd19a49ca43c0b4eda22a579cf53", size = 136773 }, - { url = "https://files.pythonhosted.org/packages/a1/dc/ce025f002f8e0749e3f057c4d773a4d4de32b7b4c1fc5a50b429e7532586/orjson-3.10.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0877c4d35de639645de83666458ca1f12560d9fa7aa9b25d8bb8f52f61627d14", size = 138029 }, - { url = "https://files.pythonhosted.org/packages/0e/1b/cf9df85852b91160029d9f26014230366a2b4deb8cc51fabe68e250a8c1a/orjson-3.10.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a09a539e9cc3beead3e7107093b4ac176d015bec64f811afb5965fce077a03c", size = 142677 }, - { url = "https://files.pythonhosted.org/packages/92/18/5b1e1e995bffad49dc4311a0bdfd874bc6f135fd20f0e1f671adc2c9910e/orjson-3.10.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b98bc9b40610fec971d9a4d67bb2ed02eec0a8ae35f8ccd2086320c28526ca", size = 132800 }, - { url = "https://files.pythonhosted.org/packages/d6/eb/467f25b580e942fcca1344adef40633b7f05ac44a65a63fc913f9a805d58/orjson-3.10.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0ce243f5a8739f3a18830bc62dc2e05b69a7545bafd3e3249f86668b2bcd8e50", size = 135451 }, - { url = "https://files.pythonhosted.org/packages/8d/4b/9d10888038975cb375982e9339d9495bac382d5c976c500b8d6f2c8e2e4e/orjson-3.10.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64792c0025bae049b3074c6abe0cf06f23c8e9f5a445f4bab31dc5ca23dbf9e1", size = 412358 }, - { url = "https://files.pythonhosted.org/packages/3b/e2/cfbcfcc4fbe619e0ca9bdbbfccb2d62b540bbfe41e0ee77d44a628594f59/orjson-3.10.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea53f7e68eec718b8e17e942f7ca56c6bd43562eb19db3f22d90d75e13f0431d", size = 152772 }, - { url = "https://files.pythonhosted.org/packages/b9/d6/627a1b00569be46173007c11dde3da4618c9bfe18409325b0e3e2a82fe29/orjson-3.10.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a741ba1a9488c92227711bde8c8c2b63d7d3816883268c808fbeada00400c164", size = 137225 }, - { url = "https://files.pythonhosted.org/packages/0a/7b/a73c67b505021af845b9f05c7c848793258ea141fa2058b52dd9b067c2b4/orjson-3.10.16-cp311-cp311-win32.whl", hash = "sha256:c7ed2c61bb8226384c3fdf1fb01c51b47b03e3f4536c985078cccc2fd19f1619", size = 141733 }, - { url = "https://files.pythonhosted.org/packages/f4/22/5e8217c48d68c0adbfb181e749d6a733761074e598b083c69a1383d18147/orjson-3.10.16-cp311-cp311-win_amd64.whl", hash = "sha256:cd67d8b3e0e56222a2e7b7f7da9031e30ecd1fe251c023340b9f12caca85ab60", size = 133784 }, - { url = "https://files.pythonhosted.org/packages/5d/15/67ce9d4c959c83f112542222ea3b9209c1d424231d71d74c4890ea0acd2b/orjson-3.10.16-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6d3444abbfa71ba21bb042caa4b062535b122248259fdb9deea567969140abca", size = 249325 }, - { url = "https://files.pythonhosted.org/packages/da/2c/1426b06f30a1b9ada74b6f512c1ddf9d2760f53f61cdb59efeb9ad342133/orjson-3.10.16-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:30245c08d818fdcaa48b7d5b81499b8cae09acabb216fe61ca619876b128e184", size = 133621 }, - { url = "https://files.pythonhosted.org/packages/9e/88/18d26130954bc73bee3be10f95371ea1dfb8679e0e2c46b0f6d8c6289402/orjson-3.10.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0ba1d0baa71bf7579a4ccdcf503e6f3098ef9542106a0eca82395898c8a500a", size = 138270 }, - { url = "https://files.pythonhosted.org/packages/4f/f9/6d8b64fcd58fae072e80ee7981be8ba0d7c26ace954e5cd1d027fc80518f/orjson-3.10.16-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb0beefa5ef3af8845f3a69ff2a4aa62529b5acec1cfe5f8a6b4141033fd46ef", size = 132346 }, - { url = "https://files.pythonhosted.org/packages/16/3f/2513fd5bc786f40cd12af569c23cae6381aeddbefeed2a98f0a666eb5d0d/orjson-3.10.16-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6daa0e1c9bf2e030e93c98394de94506f2a4d12e1e9dadd7c53d5e44d0f9628e", size = 136845 }, - { url = "https://files.pythonhosted.org/packages/6d/42/b0e7b36720f5ab722b48e8ccf06514d4f769358dd73c51abd8728ef58d0b/orjson-3.10.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da9019afb21e02410ef600e56666652b73eb3e4d213a0ec919ff391a7dd52aa", size = 138078 }, - { url = "https://files.pythonhosted.org/packages/a3/a8/d220afb8a439604be74fc755dbc740bded5ed14745ca536b304ed32eb18a/orjson-3.10.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:daeb3a1ee17b69981d3aae30c3b4e786b0f8c9e6c71f2b48f1aef934f63f38f4", size = 142712 }, - { url = "https://files.pythonhosted.org/packages/8c/88/7e41e9883c00f84f92fe357a8371edae816d9d7ef39c67b5106960c20389/orjson-3.10.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fed80eaf0e20a31942ae5d0728849862446512769692474be5e6b73123a23b", size = 133136 }, - { url = "https://files.pythonhosted.org/packages/e9/ca/61116095307ad0be828ea26093febaf59e38596d84a9c8d765c3c5e4934f/orjson-3.10.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73390ed838f03764540a7bdc4071fe0123914c2cc02fb6abf35182d5fd1b7a42", size = 135258 }, - { url = "https://files.pythonhosted.org/packages/dc/1b/09493cf7d801505f094c9295f79c98c1e0af2ac01c7ed8d25b30fcb19ada/orjson-3.10.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a22bba012a0c94ec02a7768953020ab0d3e2b884760f859176343a36c01adf87", size = 412326 }, - { url = "https://files.pythonhosted.org/packages/ea/02/125d7bbd7f7a500190ddc8ae5d2d3c39d87ed3ed28f5b37cfe76962c678d/orjson-3.10.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5385bbfdbc90ff5b2635b7e6bebf259652db00a92b5e3c45b616df75b9058e88", size = 152800 }, - { url = "https://files.pythonhosted.org/packages/f9/09/7658a9e3e793d5b3b00598023e0fb6935d0e7bbb8ff72311c5415a8ce677/orjson-3.10.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02c6279016346e774dd92625d46c6c40db687b8a0d685aadb91e26e46cc33e1e", size = 137516 }, - { url = "https://files.pythonhosted.org/packages/29/87/32b7a4831e909d347278101a48d4cf9f3f25901b2295e7709df1651f65a1/orjson-3.10.16-cp312-cp312-win32.whl", hash = "sha256:7ca55097a11426db80f79378e873a8c51f4dde9ffc22de44850f9696b7eb0e8c", size = 141759 }, - { url = "https://files.pythonhosted.org/packages/35/ce/81a27e7b439b807bd393585271364cdddf50dc281fc57c4feef7ccb186a6/orjson-3.10.16-cp312-cp312-win_amd64.whl", hash = "sha256:86d127efdd3f9bf5f04809b70faca1e6836556ea3cc46e662b44dab3fe71f3d6", size = 133944 }, - { url = "https://files.pythonhosted.org/packages/87/b9/ff6aa28b8c86af9526160905593a2fe8d004ac7a5e592ee0b0ff71017511/orjson-3.10.16-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:148a97f7de811ba14bc6dbc4a433e0341ffd2cc285065199fb5f6a98013744bd", size = 249289 }, - { url = "https://files.pythonhosted.org/packages/6c/81/6d92a586149b52684ab8fd70f3623c91d0e6a692f30fd8c728916ab2263c/orjson-3.10.16-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1d960c1bf0e734ea36d0adc880076de3846aaec45ffad29b78c7f1b7962516b8", size = 133640 }, - { url = "https://files.pythonhosted.org/packages/c2/88/b72443f4793d2e16039ab85d0026677932b15ab968595fb7149750d74134/orjson-3.10.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a318cd184d1269f68634464b12871386808dc8b7c27de8565234d25975a7a137", size = 138286 }, - { url = "https://files.pythonhosted.org/packages/c3/3c/72a22d4b28c076c4016d5a52bd644a8e4d849d3bb0373d9e377f9e3b2250/orjson-3.10.16-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df23f8df3ef9223d1d6748bea63fca55aae7da30a875700809c500a05975522b", size = 132307 }, - { url = "https://files.pythonhosted.org/packages/8a/a2/f1259561bdb6ad7061ff1b95dab082fe32758c4bc143ba8d3d70831f0a06/orjson-3.10.16-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b94dda8dd6d1378f1037d7f3f6b21db769ef911c4567cbaa962bb6dc5021cf90", size = 136739 }, - { url = "https://files.pythonhosted.org/packages/3d/af/c7583c4b34f33d8b8b90cfaab010ff18dd64e7074cc1e117a5f1eff20dcf/orjson-3.10.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12970a26666a8775346003fd94347d03ccb98ab8aa063036818381acf5f523e", size = 138076 }, - { url = "https://files.pythonhosted.org/packages/d7/59/d7fc7fbdd3d4a64c2eae4fc7341a5aa39cf9549bd5e2d7f6d3c07f8b715b/orjson-3.10.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a1431a245d856bd56e4d29ea0023eb4d2c8f71efe914beb3dee8ab3f0cd7fb", size = 142643 }, - { url = "https://files.pythonhosted.org/packages/92/0e/3bd8f2197d27601f16b4464ae948826da2bcf128af31230a9dbbad7ceb57/orjson-3.10.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83655cfc247f399a222567d146524674a7b217af7ef8289c0ff53cfe8db09f0", size = 133168 }, - { url = "https://files.pythonhosted.org/packages/af/a8/351fd87b664b02f899f9144d2c3dc848b33ac04a5df05234cbfb9e2a7540/orjson-3.10.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa59ae64cb6ddde8f09bdbf7baf933c4cd05734ad84dcf4e43b887eb24e37652", size = 135271 }, - { url = "https://files.pythonhosted.org/packages/ba/b0/a6d42a7d412d867c60c0337d95123517dd5a9370deea705ea1be0f89389e/orjson-3.10.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ca5426e5aacc2e9507d341bc169d8af9c3cbe88f4cd4c1cf2f87e8564730eb56", size = 412444 }, - { url = "https://files.pythonhosted.org/packages/79/ec/7572cd4e20863f60996f3f10bc0a6da64a6fd9c35954189a914cec0b7377/orjson-3.10.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6fd5da4edf98a400946cd3a195680de56f1e7575109b9acb9493331047157430", size = 152737 }, - { url = "https://files.pythonhosted.org/packages/a9/19/ceb9e8fed5403b2e76a8ac15f581b9d25780a3be3c9b3aa54b7777a210d5/orjson-3.10.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:980ecc7a53e567169282a5e0ff078393bac78320d44238da4e246d71a4e0e8f5", size = 137482 }, - { url = "https://files.pythonhosted.org/packages/1b/78/a78bb810f3786579dbbbd94768284cbe8f2fd65167cd7020260679665c17/orjson-3.10.16-cp313-cp313-win32.whl", hash = "sha256:28f79944dd006ac540a6465ebd5f8f45dfdf0948ff998eac7a908275b4c1add6", size = 141714 }, - { url = "https://files.pythonhosted.org/packages/81/9c/b66ce9245ff319df2c3278acd351a3f6145ef34b4a2d7f4b0f739368370f/orjson-3.10.16-cp313-cp313-win_amd64.whl", hash = "sha256:fe0a145e96d51971407cb8ba947e63ead2aa915db59d6631a355f5f2150b56b7", size = 133954 }, + { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, + { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, + { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, + { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, + { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, + { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, + { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, + { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, + { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, + { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, + { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, + { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, + { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, + { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, + { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, + { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, + { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, + { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, + { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, + { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, + { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, + { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, + { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, + { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, + { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, + { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, + { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, + { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, + { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, + { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, + { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, ] [[package]] name = "packaging" version = "23.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714, upload-time = "2023-10-01T13:50:05.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 }, + { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011, upload-time = "2023-10-01T13:50:03.745Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, - { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, - { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, - { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, - { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, - { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, - { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, - { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, - { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, - { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, - { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, - { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, - { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, - { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, - { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 }, - { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 }, - { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 }, - { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 }, - { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 }, - { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 }, - { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 }, - { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 }, - { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 }, - { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 }, - { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 }, - { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 }, - { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 }, - { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 }, - { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 }, - { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 }, - { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, - { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, - { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265, upload-time = "2024-07-01T09:45:49.812Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655, upload-time = "2024-07-01T09:45:52.462Z" }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304, upload-time = "2024-07-01T09:45:55.006Z" }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804, upload-time = "2024-07-01T09:45:58.437Z" }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126, upload-time = "2024-07-01T09:46:00.713Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541, upload-time = "2024-07-01T09:46:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616, upload-time = "2024-07-01T09:46:05.356Z" }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802, upload-time = "2024-07-01T09:46:08.145Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213, upload-time = "2024-07-01T09:46:10.211Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498, upload-time = "2024-07-01T09:46:12.685Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219, upload-time = "2024-07-01T09:46:14.83Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload-time = "2024-07-01T09:46:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload-time = "2024-07-01T09:46:19.169Z" }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload-time = "2024-07-01T09:46:21.883Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload-time = "2024-07-01T09:46:24.321Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload-time = "2024-07-01T09:46:26.825Z" }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload-time = "2024-07-01T09:46:29.355Z" }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload-time = "2024-07-01T09:46:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload-time = "2024-07-01T09:46:33.73Z" }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload-time = "2024-07-01T09:46:36.587Z" }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload-time = "2024-07-01T09:46:38.777Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload-time = "2024-07-01T09:46:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685, upload-time = "2024-07-01T09:46:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883, upload-time = "2024-07-01T09:46:47.331Z" }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837, upload-time = "2024-07-01T09:46:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562, upload-time = "2024-07-01T09:46:51.811Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761, upload-time = "2024-07-01T09:46:53.961Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767, upload-time = "2024-07-01T09:46:56.664Z" }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989, upload-time = "2024-07-01T09:46:58.977Z" }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255, upload-time = "2024-07-01T09:47:01.189Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603, upload-time = "2024-07-01T09:47:03.918Z" }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972, upload-time = "2024-07-01T09:47:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375, upload-time = "2024-07-01T09:47:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, ] [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/d1/7feaaacb1a3faeba96c06e6c5091f90695cc0f94b7e8e1a3a3fe2b33ff9a/platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420", size = 19760 } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload-time = "2025-03-19T20:36:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/53/42fe5eab4a09d251a76d0043e018172db324a23fcdac70f77a551c11f618/platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", size = 17420 }, + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -1745,51 +1788,51 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874, upload-time = "2023-09-11T14:04:14.548Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772 }, + { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772, upload-time = "2023-09-11T14:03:45.582Z" }, ] [[package]] name = "protobuf" version = "4.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282 } +sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282, upload-time = "2024-01-10T19:37:42.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408 }, - { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397 }, - { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159 }, - { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698 }, - { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609 }, - { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463 }, + { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408, upload-time = "2024-01-10T19:37:23.466Z" }, + { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397, upload-time = "2024-01-10T19:37:26.321Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159, upload-time = "2024-01-10T19:37:28.932Z" }, + { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698, upload-time = "2024-01-10T19:37:30.666Z" }, + { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609, upload-time = "2024-01-10T19:37:32.777Z" }, + { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463, upload-time = "2024-01-10T19:37:41.24Z" }, ] [[package]] name = "psutil" version = "5.9.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429 } +sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429, upload-time = "2023-12-17T11:25:21.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972 }, - { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514 }, - { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469 }, - { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406 }, - { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245 }, - { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739 }, + { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972, upload-time = "2023-12-17T11:25:48.202Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514, upload-time = "2023-12-17T11:25:51.371Z" }, + { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469, upload-time = "2023-12-17T11:25:54.25Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406, upload-time = "2023-12-17T12:38:50.326Z" }, + { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245, upload-time = "2023-12-17T12:39:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739, upload-time = "2023-12-17T11:25:57.305Z" }, ] [[package]] name = "pycparser" version = "2.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877, upload-time = "2021-11-06T12:48:46.095Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697 }, + { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697, upload-time = "2021-11-06T12:50:13.61Z" }, ] [[package]] name = "pydantic" -version = "2.11.3" +version = "2.11.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1797,136 +1840,137 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, + { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, ] [[package]] name = "pydantic-core" -version = "2.33.1" +version = "2.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/ea/5f572806ab4d4223d11551af814d243b0e3e02cc6913def4d1fe4a5ca41c/pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", size = 2044021 }, - { url = "https://files.pythonhosted.org/packages/8c/d1/f86cc96d2aa80e3881140d16d12ef2b491223f90b28b9a911346c04ac359/pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", size = 1861742 }, - { url = "https://files.pythonhosted.org/packages/37/08/fbd2cd1e9fc735a0df0142fac41c114ad9602d1c004aea340169ae90973b/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", size = 1910414 }, - { url = "https://files.pythonhosted.org/packages/7f/73/3ac217751decbf8d6cb9443cec9b9eb0130eeada6ae56403e11b486e277e/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", size = 1996848 }, - { url = "https://files.pythonhosted.org/packages/9a/f5/5c26b265cdcff2661e2520d2d1e9db72d117ea00eb41e00a76efe68cb009/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", size = 2141055 }, - { url = "https://files.pythonhosted.org/packages/5d/14/a9c3cee817ef2f8347c5ce0713e91867a0dceceefcb2973942855c917379/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", size = 2753806 }, - { url = "https://files.pythonhosted.org/packages/f2/68/866ce83a51dd37e7c604ce0050ff6ad26de65a7799df89f4db87dd93d1d6/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", size = 2007777 }, - { url = "https://files.pythonhosted.org/packages/b6/a8/36771f4404bb3e49bd6d4344da4dede0bf89cc1e01f3b723c47248a3761c/pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", size = 2122803 }, - { url = "https://files.pythonhosted.org/packages/18/9c/730a09b2694aa89360d20756369822d98dc2f31b717c21df33b64ffd1f50/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", size = 2086755 }, - { url = "https://files.pythonhosted.org/packages/54/8e/2dccd89602b5ec31d1c58138d02340ecb2ebb8c2cac3cc66b65ce3edb6ce/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", size = 2257358 }, - { url = "https://files.pythonhosted.org/packages/d1/9c/126e4ac1bfad8a95a9837acdd0963695d69264179ba4ede8b8c40d741702/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091", size = 2257916 }, - { url = "https://files.pythonhosted.org/packages/7d/ba/91eea2047e681a6853c81c20aeca9dcdaa5402ccb7404a2097c2adf9d038/pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", size = 1923823 }, - { url = "https://files.pythonhosted.org/packages/94/c0/fcdf739bf60d836a38811476f6ecd50374880b01e3014318b6e809ddfd52/pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", size = 1952494 }, - { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224 }, - { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845 }, - { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029 }, - { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784 }, - { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075 }, - { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849 }, - { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794 }, - { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237 }, - { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351 }, - { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914 }, - { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385 }, - { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, - { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, - { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, - { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640 }, - { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649 }, - { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472 }, - { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509 }, - { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702 }, - { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428 }, - { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753 }, - { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849 }, - { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541 }, - { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225 }, - { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373 }, - { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, - { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, - { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, - { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, - { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, - { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, - { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, - { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, - { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, - { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, - { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, - { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, - { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, - { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, - { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, - { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, - { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, - { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, - { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, - { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, - { url = "https://files.pythonhosted.org/packages/9c/c7/8b311d5adb0fe00a93ee9b4e92a02b0ec08510e9838885ef781ccbb20604/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", size = 2041659 }, - { url = "https://files.pythonhosted.org/packages/8a/d6/4f58d32066a9e26530daaf9adc6664b01875ae0691570094968aaa7b8fcc/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", size = 1873294 }, - { url = "https://files.pythonhosted.org/packages/f7/3f/53cc9c45d9229da427909c751f8ed2bf422414f7664ea4dde2d004f596ba/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", size = 1903771 }, - { url = "https://files.pythonhosted.org/packages/f0/49/bf0783279ce674eb9903fb9ae43f6c614cb2f1c4951370258823f795368b/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", size = 2083558 }, - { url = "https://files.pythonhosted.org/packages/9c/5b/0d998367687f986c7d8484a2c476d30f07bf5b8b1477649a6092bd4c540e/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", size = 2118038 }, - { url = "https://files.pythonhosted.org/packages/b3/33/039287d410230ee125daee57373ac01940d3030d18dba1c29cd3089dc3ca/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", size = 2079315 }, - { url = "https://files.pythonhosted.org/packages/1f/85/6d8b2646d99c062d7da2d0ab2faeb0d6ca9cca4c02da6076376042a20da3/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", size = 2249063 }, - { url = "https://files.pythonhosted.org/packages/17/d7/c37d208d5738f7b9ad8f22ae8a727d88ebf9c16c04ed2475122cc3f7224a/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", size = 2254631 }, - { url = "https://files.pythonhosted.org/packages/13/e0/bafa46476d328e4553b85ab9b2f7409e7aaef0ce4c937c894821c542d347/pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", size = 2080877 }, - { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858 }, - { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745 }, - { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188 }, - { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479 }, - { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415 }, - { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623 }, - { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175 }, - { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674 }, - { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951 }, + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, ] [[package]] name = "pydantic-settings" -version = "2.8.1" +version = "2.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } +sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, + { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" }, ] [[package]] name = "pygments" version = "2.17.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 } +sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772, upload-time = "2023-11-21T20:43:53.875Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 }, + { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756, upload-time = "2023-11-21T20:43:49.423Z" }, ] [[package]] name = "pyparsing" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814 } +sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814, upload-time = "2023-07-30T15:07:02.617Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139 }, + { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139, upload-time = "2023-07-30T15:06:59.829Z" }, ] [[package]] name = "pyreadline3" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465, upload-time = "2022-01-24T20:05:11.66Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203 }, + { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203, upload-time = "2022-01-24T20:05:10.442Z" }, ] [[package]] @@ -1941,9 +1985,9 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] [[package]] @@ -1953,9 +1997,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 }, + { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" }, ] [[package]] @@ -1966,9 +2010,9 @@ dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 } +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 }, + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, ] [[package]] @@ -1978,9 +2022,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload-time = "2024-03-21T22:14:04.964Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload-time = "2024-03-21T22:14:02.694Z" }, ] [[package]] @@ -1990,27 +2034,58 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324, upload-time = "2021-07-14T08:19:19.783Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702 }, + { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702, upload-time = "2021-07-14T08:19:18.161Z" }, ] [[package]] name = "python-dotenv" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399 } +sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399, upload-time = "2023-02-24T06:46:37.282Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482 }, + { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482, upload-time = "2023-02-24T06:46:36.009Z" }, +] + +[[package]] +name = "python-engineio" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "simple-websocket" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/e1/eee1129544b7f78fa2afa9fa0fce153cdcb21015b9b331d1b8adf90f45cb/python_engineio-4.12.0.tar.gz", hash = "sha256:f42a36a868d7063aa10ddccf6bd6117a169b6bd00d7ca53999772093b62014f9", size = 91503, upload-time = "2025-04-12T15:30:23.905Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/f7/0aeea75424c47633c1d98557a2323be23bed31fa950f00161b34a5150d06/python_engineio-4.12.0-py3-none-any.whl", hash = "sha256:a0c47c129c39777e8ebc6d18011efd50db2144e4e8f08983acae8a3614626535", size = 59319, upload-time = "2025-04-12T15:30:22.325Z" }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, +] + +[[package]] +name = "python-socketio" +version = "5.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bidict" }, + { name = "python-engineio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/1a/396d50ccf06ee539fa758ce5623b59a9cb27637fc4b2dc07ed08bf495e77/python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029", size = 121125, upload-time = "2025-04-12T15:46:59.933Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/32/b4fb8585d1be0f68bde7e110dffbcf354915f77ad8c778563f0ad9655c02/python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf", size = 77800, upload-time = "2025-04-12T15:46:58.412Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "requests" }, + { name = "websocket-client" }, ] [[package]] @@ -2018,45 +2093,45 @@ name = "pywin32" version = "306" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422 }, - { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392 }, - { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689 }, - { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547 }, - { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324 }, - { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705 }, - { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429 }, - { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145 }, + { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422, upload-time = "2023-03-26T03:27:46.303Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392, upload-time = "2023-03-26T03:27:53.591Z" }, + { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689, upload-time = "2023-03-25T23:50:08.499Z" }, + { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547, upload-time = "2023-03-25T23:50:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324, upload-time = "2023-03-25T23:50:30.904Z" }, + { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705, upload-time = "2023-03-25T23:50:40.279Z" }, + { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429, upload-time = "2023-03-25T23:50:50.222Z" }, + { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145, upload-time = "2023-03-25T23:51:01.401Z" }, ] [[package]] name = "pyyaml" version = "6.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201, upload-time = "2023-07-18T00:00:23.308Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447 }, - { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264 }, - { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003 }, - { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070 }, - { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525 }, - { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514 }, - { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488 }, - { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338 }, - { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867 }, - { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530 }, - { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244 }, - { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871 }, - { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729 }, - { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528 }, - { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286 }, - { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699 }, - { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692 }, - { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622 }, - { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937 }, - { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969 }, - { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604 }, - { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098 }, - { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675 }, + { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447, upload-time = "2023-07-17T23:57:04.325Z" }, + { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264, upload-time = "2023-07-17T23:57:07.787Z" }, + { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003, upload-time = "2023-07-17T23:57:13.144Z" }, + { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070, upload-time = "2023-07-17T23:57:19.402Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525, upload-time = "2023-07-17T23:57:25.272Z" }, + { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514, upload-time = "2023-08-28T18:43:20.945Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488, upload-time = "2023-07-17T23:57:28.144Z" }, + { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338, upload-time = "2023-07-17T23:57:31.118Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867, upload-time = "2023-07-17T23:57:34.35Z" }, + { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530, upload-time = "2023-07-17T23:57:36.975Z" }, + { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244, upload-time = "2023-07-17T23:57:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871, upload-time = "2023-07-17T23:57:51.921Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729, upload-time = "2023-07-17T23:57:59.865Z" }, + { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528, upload-time = "2023-08-28T18:43:23.207Z" }, + { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286, upload-time = "2023-07-17T23:58:02.964Z" }, + { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699, upload-time = "2023-07-17T23:58:05.586Z" }, + { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692, upload-time = "2023-08-28T18:43:24.924Z" }, + { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622, upload-time = "2023-08-28T18:43:26.54Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937, upload-time = "2024-01-18T20:40:22.92Z" }, + { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969, upload-time = "2023-08-28T18:43:28.56Z" }, + { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604, upload-time = "2023-08-28T18:43:30.206Z" }, + { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098, upload-time = "2023-08-28T18:43:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675, upload-time = "2023-08-28T18:43:33.613Z" }, ] [[package]] @@ -2066,46 +2141,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339, upload-time = "2023-12-05T07:34:47.976Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310 }, - { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619 }, - { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360 }, - { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959 }, - { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585 }, - { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267 }, - { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853 }, - { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212 }, - { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737 }, - { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288 }, - { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923 }, - { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360 }, - { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008 }, - { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394 }, - { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453 }, - { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501 }, - { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513 }, - { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541 }, - { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133 }, - { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636 }, - { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865 }, - { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332 }, - { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111 }, - { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844 }, - { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373 }, - { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901 }, - { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147 }, - { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315 }, - { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747 }, - { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221 }, - { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505 }, - { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891 }, - { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773 }, - { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654 }, - { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436 }, - { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396 }, - { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856 }, - { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836 }, + { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310, upload-time = "2023-12-05T07:48:13.713Z" }, + { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619, upload-time = "2023-12-05T07:50:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360, upload-time = "2023-12-05T07:42:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959, upload-time = "2023-12-05T07:44:29.904Z" }, + { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585, upload-time = "2023-12-05T07:45:05.518Z" }, + { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267, upload-time = "2023-12-05T07:39:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853, upload-time = "2023-12-05T07:41:09.261Z" }, + { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212, upload-time = "2023-12-05T07:49:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737, upload-time = "2023-12-05T07:49:09.096Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288, upload-time = "2023-12-05T07:42:05.509Z" }, + { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923, upload-time = "2023-12-05T07:44:54.296Z" }, + { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360, upload-time = "2023-12-05T07:48:16.153Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008, upload-time = "2023-12-05T07:50:40.442Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394, upload-time = "2023-12-05T07:42:27.815Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453, upload-time = "2023-12-05T07:44:32.003Z" }, + { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501, upload-time = "2023-12-05T07:45:07.18Z" }, + { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513, upload-time = "2023-12-05T07:39:53.338Z" }, + { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541, upload-time = "2023-12-05T07:41:10.786Z" }, + { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133, upload-time = "2023-12-05T07:49:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636, upload-time = "2023-12-05T07:49:13.787Z" }, + { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865, upload-time = "2023-12-05T07:42:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332, upload-time = "2023-12-05T07:44:56.111Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111, upload-time = "2023-12-05T07:48:17.868Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844, upload-time = "2023-12-05T07:50:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373, upload-time = "2023-12-05T07:42:29.595Z" }, + { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901, upload-time = "2023-12-05T07:44:33.819Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147, upload-time = "2023-12-05T07:45:10.058Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315, upload-time = "2023-12-05T07:39:55.851Z" }, + { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747, upload-time = "2023-12-05T07:41:13.219Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221, upload-time = "2023-12-05T07:49:16.352Z" }, + { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505, upload-time = "2023-12-05T07:49:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891, upload-time = "2023-12-05T07:42:09.208Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773, upload-time = "2023-12-05T07:44:58.16Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654, upload-time = "2023-12-05T07:47:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436, upload-time = "2023-12-05T07:42:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396, upload-time = "2023-12-05T07:44:43.727Z" }, + { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856, upload-time = "2023-12-05T07:45:21.117Z" }, + { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836, upload-time = "2023-12-05T07:53:22.583Z" }, ] [[package]] @@ -2118,9 +2193,9 @@ dependencies = [ { name = "scikit-learn" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100, upload-time = "2021-08-09T16:47:55.807Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478 }, + { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478, upload-time = "2021-08-09T16:47:54.637Z" }, ] [[package]] @@ -2133,9 +2208,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, ] [[package]] @@ -2147,14 +2222,14 @@ dependencies = [ { name = "pygments" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, ] [[package]] name = "rknn-toolkit-lite2" -version = "2.3.0" +version = "2.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -2162,9 +2237,9 @@ dependencies = [ { name = "ruamel-yaml" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/77/6af374a4a8cd2aee762a1fb8a3050dcf3f129134bbdc4bb6bed755c4325b/rknn_toolkit_lite2-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b6733689bd09a262bcb6ba4744e690dd4b37ebeac4ed427cf45242c4b4ce9a4", size = 559372 }, - { url = "https://files.pythonhosted.org/packages/9b/0c/76ff1eb09d09ce4394a6959d2343a321d28dd9e604348ffdafceafdc344c/rknn_toolkit_lite2-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e4fefe355dc34a155680e4bcb9e4abb37ebc271f045ec9e0a4a3a018bc5beb", size = 569149 }, - { url = "https://files.pythonhosted.org/packages/0d/6e/8679562028051b02312212defc6e8c07248953f10dd7ad506e941b575bf3/rknn_toolkit_lite2-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37394371d1561f470c553f39869d7c35ff93405dffe3d0d72babf297a2b0aee9", size = 527457 }, + { url = "https://files.pythonhosted.org/packages/ab/db/76b40afe343f8a8c5222300da425e0dace30ce639a94776468b1d157311b/rknn_toolkit_lite2-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:821e80c95e6838308c133915660b1a6ae78bb8d079b2cbbd46a02dae61192d33", size = 559386, upload-time = "2025-04-09T09:39:54.414Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3d/e80e1742420f62cb628d40a8bf547d6f7c9dbe4e13dcb7b7e7c0b5620e74/rknn_toolkit_lite2-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda74f1179e15fccb8726054a24898982522784b65bb340b20146955d254e800", size = 569160, upload-time = "2025-04-09T09:39:56.149Z" }, + { url = "https://files.pythonhosted.org/packages/ff/db/64c756f3f06b219e92ff4f0fd4e000870ee49f214d505ff01c8b0275e26d/rknn_toolkit_lite2-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1e4ec691fed900c0e6fde5e7d8eeba17f806aa45092b63b361ee775e2c1b50e", size = 527458, upload-time = "2025-04-09T09:39:58.881Z" }, ] [[package]] @@ -2174,78 +2249,78 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload-time = "2025-01-06T14:08:51.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729 }, + { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload-time = "2025-01-06T14:08:47.471Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315 } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301 }, - { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728 }, - { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230 }, - { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712 }, - { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936 }, - { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580 }, - { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393 }, - { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326 }, - { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079 }, - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224 }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480 }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068 }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362 }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118 }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 }, - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011 }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488 }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066 }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785 }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017 }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270 }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059 }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583 }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190 }, + { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301, upload-time = "2024-10-20T10:12:35.876Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728, upload-time = "2024-10-20T10:12:37.858Z" }, + { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230, upload-time = "2024-10-20T10:12:39.457Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712, upload-time = "2024-10-20T10:12:41.119Z" }, + { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936, upload-time = "2024-10-21T11:26:37.419Z" }, + { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580, upload-time = "2024-10-21T11:26:39.503Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393, upload-time = "2024-12-11T19:58:13.873Z" }, + { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326, upload-time = "2024-10-20T10:12:42.967Z" }, + { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079, upload-time = "2024-10-20T10:12:44.117Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" }, + { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, + { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, + { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, + { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, ] [[package]] name = "ruff" -version = "0.11.5" +version = "0.11.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/71/5759b2a6b2279bb77fe15b1435b89473631c2cd6374d45ccdb6b785810be/ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef", size = 3976488 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/53/ae4857030d59286924a8bdb30d213d6ff22d8f0957e738d0289990091dd8/ruff-0.11.11.tar.gz", hash = "sha256:7774173cc7c1980e6bf67569ebb7085989a78a103922fb83ef3dfe230cd0687d", size = 4186707, upload-time = "2025-05-22T19:19:34.363Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/db/6efda6381778eec7f35875b5cbefd194904832a1153d68d36d6b269d81a8/ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b", size = 10103150 }, - { url = "https://files.pythonhosted.org/packages/44/f2/06cd9006077a8db61956768bc200a8e52515bf33a8f9b671ee527bb10d77/ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077", size = 10898637 }, - { url = "https://files.pythonhosted.org/packages/18/f5/af390a013c56022fe6f72b95c86eb7b2585c89cc25d63882d3bfe411ecf1/ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779", size = 10236012 }, - { url = "https://files.pythonhosted.org/packages/b8/ca/b9bf954cfed165e1a0c24b86305d5c8ea75def256707f2448439ac5e0d8b/ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794", size = 10415338 }, - { url = "https://files.pythonhosted.org/packages/d9/4d/2522dde4e790f1b59885283f8786ab0046958dfd39959c81acc75d347467/ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038", size = 9965277 }, - { url = "https://files.pythonhosted.org/packages/e5/7a/749f56f150eef71ce2f626a2f6988446c620af2f9ba2a7804295ca450397/ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f", size = 11541614 }, - { url = "https://files.pythonhosted.org/packages/89/b2/7d9b8435222485b6aac627d9c29793ba89be40b5de11584ca604b829e960/ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82", size = 12198873 }, - { url = "https://files.pythonhosted.org/packages/00/e0/a1a69ef5ffb5c5f9c31554b27e030a9c468fc6f57055886d27d316dfbabd/ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304", size = 11670190 }, - { url = "https://files.pythonhosted.org/packages/05/61/c1c16df6e92975072c07f8b20dad35cd858e8462b8865bc856fe5d6ccb63/ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470", size = 13902301 }, - { url = "https://files.pythonhosted.org/packages/79/89/0af10c8af4363304fd8cb833bd407a2850c760b71edf742c18d5a87bb3ad/ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a", size = 11350132 }, - { url = "https://files.pythonhosted.org/packages/b9/e1/ecb4c687cbf15164dd00e38cf62cbab238cad05dd8b6b0fc68b0c2785e15/ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b", size = 10312937 }, - { url = "https://files.pythonhosted.org/packages/cf/4f/0e53fe5e500b65934500949361e3cd290c5ba60f0324ed59d15f46479c06/ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a", size = 9936683 }, - { url = "https://files.pythonhosted.org/packages/04/a8/8183c4da6d35794ae7f76f96261ef5960853cd3f899c2671961f97a27d8e/ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159", size = 10950217 }, - { url = "https://files.pythonhosted.org/packages/26/88/9b85a5a8af21e46a0639b107fcf9bfc31da4f1d263f2fc7fbe7199b47f0a/ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783", size = 11404521 }, - { url = "https://files.pythonhosted.org/packages/fc/52/047f35d3b20fd1ae9ccfe28791ef0f3ca0ef0b3e6c1a58badd97d450131b/ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe", size = 10320697 }, - { url = "https://files.pythonhosted.org/packages/b9/fe/00c78010e3332a6e92762424cf4c1919065707e962232797d0b57fd8267e/ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800", size = 11378665 }, - { url = "https://files.pythonhosted.org/packages/43/7c/c83fe5cbb70ff017612ff36654edfebec4b1ef79b558b8e5fd933bab836b/ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e", size = 10460287 }, + { url = "https://files.pythonhosted.org/packages/b1/14/f2326676197bab099e2a24473158c21656fbf6a207c65f596ae15acb32b9/ruff-0.11.11-py3-none-linux_armv6l.whl", hash = "sha256:9924e5ae54125ed8958a4f7de320dab7380f6e9fa3195e3dc3b137c6842a0092", size = 10229049, upload-time = "2025-05-22T19:18:45.516Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f3/bff7c92dd66c959e711688b2e0768e486bbca46b2f35ac319bb6cce04447/ruff-0.11.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8a93276393d91e952f790148eb226658dd275cddfde96c6ca304873f11d2ae4", size = 11053601, upload-time = "2025-05-22T19:18:49.269Z" }, + { url = "https://files.pythonhosted.org/packages/e2/38/8e1a3efd0ef9d8259346f986b77de0f62c7a5ff4a76563b6b39b68f793b9/ruff-0.11.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6e333dbe2e6ae84cdedefa943dfd6434753ad321764fd937eef9d6b62022bcd", size = 10367421, upload-time = "2025-05-22T19:18:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/b4/50/557ad9dd4fb9d0bf524ec83a090a3932d284d1a8b48b5906b13b72800e5f/ruff-0.11.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7885d9a5e4c77b24e8c88aba8c80be9255fa22ab326019dac2356cff42089fc6", size = 10581980, upload-time = "2025-05-22T19:18:54.011Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b2/e2ed82d6e2739ece94f1bdbbd1d81b712d3cdaf69f0a1d1f1a116b33f9ad/ruff-0.11.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b5ab797fcc09121ed82e9b12b6f27e34859e4227080a42d090881be888755d4", size = 10089241, upload-time = "2025-05-22T19:18:56.041Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9f/b4539f037a5302c450d7c695c82f80e98e48d0d667ecc250e6bdeb49b5c3/ruff-0.11.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e231ff3132c1119ece836487a02785f099a43992b95c2f62847d29bace3c75ac", size = 11699398, upload-time = "2025-05-22T19:18:58.248Z" }, + { url = "https://files.pythonhosted.org/packages/61/fb/32e029d2c0b17df65e6eaa5ce7aea5fbeaed22dddd9fcfbbf5fe37c6e44e/ruff-0.11.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a97c9babe1d4081037a90289986925726b802d180cca784ac8da2bbbc335f709", size = 12427955, upload-time = "2025-05-22T19:19:00.981Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e3/160488dbb11f18c8121cfd588e38095ba779ae208292765972f7732bfd95/ruff-0.11.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8c4ddcbe8a19f59f57fd814b8b117d4fcea9bee7c0492e6cf5fdc22cfa563c8", size = 12069803, upload-time = "2025-05-22T19:19:03.258Z" }, + { url = "https://files.pythonhosted.org/packages/ff/16/3b006a875f84b3d0bff24bef26b8b3591454903f6f754b3f0a318589dcc3/ruff-0.11.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6224076c344a7694c6fbbb70d4f2a7b730f6d47d2a9dc1e7f9d9bb583faf390b", size = 11242630, upload-time = "2025-05-22T19:19:05.871Z" }, + { url = "https://files.pythonhosted.org/packages/65/0d/0338bb8ac0b97175c2d533e9c8cdc127166de7eb16d028a43c5ab9e75abd/ruff-0.11.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:882821fcdf7ae8db7a951df1903d9cb032bbe838852e5fc3c2b6c3ab54e39875", size = 11507310, upload-time = "2025-05-22T19:19:08.584Z" }, + { url = "https://files.pythonhosted.org/packages/6f/bf/d7130eb26174ce9b02348b9f86d5874eafbf9f68e5152e15e8e0a392e4a3/ruff-0.11.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:dcec2d50756463d9df075a26a85a6affbc1b0148873da3997286caf1ce03cae1", size = 10441144, upload-time = "2025-05-22T19:19:13.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f3/4be2453b258c092ff7b1761987cf0749e70ca1340cd1bfb4def08a70e8d8/ruff-0.11.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99c28505ecbaeb6594701a74e395b187ee083ee26478c1a795d35084d53ebd81", size = 10081987, upload-time = "2025-05-22T19:19:15.821Z" }, + { url = "https://files.pythonhosted.org/packages/6c/6e/dfa4d2030c5b5c13db158219f2ec67bf333e8a7748dccf34cfa2a6ab9ebc/ruff-0.11.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9263f9e5aa4ff1dec765e99810f1cc53f0c868c5329b69f13845f699fe74f639", size = 11073922, upload-time = "2025-05-22T19:19:18.104Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f4/f7b0b0c3d32b593a20ed8010fa2c1a01f2ce91e79dda6119fcc51d26c67b/ruff-0.11.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:64ac6f885e3ecb2fdbb71de2701d4e34526651f1e8503af8fb30d4915a3fe345", size = 11568537, upload-time = "2025-05-22T19:19:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d2/46/0e892064d0adc18bcc81deed9aaa9942a27fd2cd9b1b7791111ce468c25f/ruff-0.11.11-py3-none-win32.whl", hash = "sha256:1adcb9a18802268aaa891ffb67b1c94cd70578f126637118e8099b8e4adcf112", size = 10536492, upload-time = "2025-05-22T19:19:23.642Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d9/232e79459850b9f327e9f1dc9c047a2a38a6f9689e1ec30024841fc4416c/ruff-0.11.11-py3-none-win_amd64.whl", hash = "sha256:748b4bb245f11e91a04a4ff0f96e386711df0a30412b9fe0c74d5bdc0e4a531f", size = 11612562, upload-time = "2025-05-22T19:19:27.013Z" }, + { url = "https://files.pythonhosted.org/packages/ce/eb/09c132cff3cc30b2e7244191dcce69437352d6d6709c0adf374f3e6f476e/ruff-0.11.11-py3-none-win_arm64.whl", hash = "sha256:6c51f136c0364ab1b774767aa8b86331bd8e9d414e2d107db7a2189f35ea1f7b", size = 10735951, upload-time = "2025-05-22T19:19:30.043Z" }, ] [[package]] @@ -2262,23 +2337,23 @@ dependencies = [ { name = "scipy" }, { name = "tifffile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018 } +sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018, upload-time = "2023-10-03T21:36:34.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039 }, - { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212 }, - { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779 }, - { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042 }, - { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345 }, - { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619 }, - { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761 }, - { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710 }, - { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172 }, - { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321 }, - { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808 }, - { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763 }, - { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233 }, - { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814 }, - { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039, upload-time = "2023-10-03T21:35:27.279Z" }, + { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212, upload-time = "2023-10-03T21:35:30.864Z" }, + { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779, upload-time = "2023-10-03T21:35:34.273Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042, upload-time = "2023-10-03T21:35:37.787Z" }, + { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345, upload-time = "2023-10-03T21:35:41.122Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619, upload-time = "2023-10-03T21:35:44.88Z" }, + { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761, upload-time = "2023-10-03T21:35:48.865Z" }, + { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710, upload-time = "2023-10-03T21:35:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172, upload-time = "2023-10-03T21:35:55.752Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321, upload-time = "2023-10-03T21:35:58.847Z" }, + { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808, upload-time = "2023-10-03T21:36:02.526Z" }, + { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763, upload-time = "2023-10-03T21:36:05.504Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233, upload-time = "2023-10-03T21:36:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814, upload-time = "2023-10-03T21:36:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857, upload-time = "2023-10-03T21:36:15.457Z" }, ] [[package]] @@ -2291,23 +2366,23 @@ dependencies = [ { name = "scipy" }, { name = "threadpoolctl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251 } +sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251, upload-time = "2023-10-23T13:47:55.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999 }, - { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353 }, - { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705 }, - { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807 }, - { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427 }, - { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376 }, - { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415 }, - { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163 }, - { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422 }, - { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060 }, - { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322 }, - { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688 }, - { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398 }, - { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478 }, - { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979 }, + { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999, upload-time = "2023-10-23T13:46:30.373Z" }, + { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353, upload-time = "2023-10-23T13:46:34.368Z" }, + { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705, upload-time = "2023-10-23T13:46:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807, upload-time = "2023-10-23T13:46:41.59Z" }, + { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427, upload-time = "2023-10-23T13:46:44.826Z" }, + { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376, upload-time = "2023-10-23T13:46:48.147Z" }, + { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415, upload-time = "2023-10-23T13:46:51.324Z" }, + { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163, upload-time = "2023-10-23T13:46:54.642Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422, upload-time = "2023-10-23T13:46:58.087Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060, upload-time = "2023-10-23T13:47:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322, upload-time = "2023-10-23T13:47:03.977Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688, upload-time = "2023-10-23T13:47:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398, upload-time = "2023-10-23T13:47:10.796Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478, upload-time = "2023-10-23T13:47:14.077Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979, upload-time = "2023-10-23T13:47:17.389Z" }, ] [[package]] @@ -2317,53 +2392,65 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202 } +sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202, upload-time = "2023-11-18T21:06:08.277Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259 }, - { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656 }, - { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489 }, - { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040 }, - { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257 }, - { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285 }, - { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197 }, - { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057 }, - { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747 }, - { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732 }, - { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138 }, - { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631 }, - { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653 }, - { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601 }, - { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137 }, - { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534 }, - { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721 }, - { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653 }, + { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259, upload-time = "2023-11-18T21:01:18.805Z" }, + { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656, upload-time = "2023-11-18T21:01:41.815Z" }, + { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489, upload-time = "2023-11-18T21:01:50.637Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040, upload-time = "2023-11-18T21:02:00.119Z" }, + { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257, upload-time = "2023-11-18T21:02:06.798Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285, upload-time = "2023-11-18T21:02:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197, upload-time = "2023-11-18T21:02:21.959Z" }, + { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057, upload-time = "2023-11-18T21:02:28.169Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747, upload-time = "2023-11-18T21:02:33.683Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732, upload-time = "2023-11-18T21:02:39.762Z" }, + { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138, upload-time = "2023-11-18T21:02:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631, upload-time = "2023-11-18T21:02:52.859Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653, upload-time = "2023-11-18T21:03:00.107Z" }, + { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601, upload-time = "2023-11-18T21:03:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137, upload-time = "2023-11-18T21:03:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534, upload-time = "2023-11-18T21:03:21.451Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721, upload-time = "2023-11-18T21:03:27.85Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653, upload-time = "2023-11-18T21:03:34.758Z" }, ] [[package]] name = "setuptools" version = "70.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112 } +sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112, upload-time = "2024-07-09T16:08:06.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070 }, + { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070, upload-time = "2024-07-09T16:07:58.829Z" }, +] + +[[package]] +name = "simple-websocket" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/d4/bfa032f961103eba93de583b161f0e6a5b63cebb8f2c7d0c6e6efe1e3d2e/simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4", size = 17300, upload-time = "2024-10-10T22:39:31.412Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c", size = 13842, upload-time = "2024-10-10T22:39:29.645Z" }, ] [[package]] name = "six" version = "1.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" }, ] [[package]] name = "sniffio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103, upload-time = "2022-09-01T12:31:36.968Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165 }, + { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165, upload-time = "2022-09-01T12:31:34.186Z" }, ] [[package]] @@ -2373,9 +2460,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867, upload-time = "2024-10-27T08:20:02.818Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259 }, + { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259, upload-time = "2024-10-27T08:20:00.052Z" }, ] [[package]] @@ -2385,18 +2472,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203 } +sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203, upload-time = "2023-05-10T18:23:00.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435 }, + { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435, upload-time = "2023-05-10T18:22:14.76Z" }, ] [[package]] name = "threadpoolctl" version = "3.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266 } +sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266, upload-time = "2023-07-13T14:53:53.299Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539 }, + { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539, upload-time = "2023-07-13T14:53:39.336Z" }, ] [[package]] @@ -2406,9 +2493,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467, upload-time = "2023-12-09T20:46:29.203Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627 }, + { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627, upload-time = "2023-12-09T20:46:26.569Z" }, ] [[package]] @@ -2418,31 +2505,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256, upload-time = "2025-03-13T10:51:18.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, - { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, - { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, - { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, - { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, - { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, - { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, - { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, - { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, - { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, - { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767, upload-time = "2025-03-13T10:51:09.459Z" }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555, upload-time = "2025-03-13T10:51:07.692Z" }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541, upload-time = "2025-03-13T10:50:56.679Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058, upload-time = "2025-03-13T10:50:59.525Z" }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278, upload-time = "2025-03-13T10:51:04.678Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253, upload-time = "2025-03-13T10:51:01.261Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225, upload-time = "2025-03-13T10:51:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874, upload-time = "2025-03-13T10:51:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448, upload-time = "2025-03-13T10:51:10.927Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877, upload-time = "2025-03-13T10:51:12.688Z" }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645, upload-time = "2025-03-13T10:51:14.723Z" }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380, upload-time = "2025-03-13T10:51:16.526Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506, upload-time = "2025-03-13T10:51:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481, upload-time = "2025-03-13T10:51:19.243Z" }, ] [[package]] name = "tomli" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload-time = "2022-02-08T10:54:04.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload-time = "2022-02-08T10:54:02.017Z" }, ] [[package]] @@ -2452,30 +2539,30 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551 } +sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551, upload-time = "2024-05-02T21:44:05.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374 }, + { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374, upload-time = "2024-05-02T21:44:01.541Z" }, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20250402" +version = "6.0.12.20250516" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/68/609eed7402f87c9874af39d35942744e39646d1ea9011765ec87b01b2a3c/types_pyyaml-6.0.12.20250402.tar.gz", hash = "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075", size = 17282 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/56/1fe61db05685fbb512c07ea9323f06ea727125951f1eb4dff110b3311da3/types_pyyaml-6.0.12.20250402-py3-none-any.whl", hash = "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681", size = 20329 }, + { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, ] [[package]] name = "types-requests" -version = "2.32.0.20250328" +version = "2.32.0.20250515" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995 } +sdist = { url = "https://files.pythonhosted.org/packages/06/c1/cdc4f9b8cfd9130fbe6276db574f114541f4231fcc6fb29648289e6e3390/types_requests-2.32.0.20250515.tar.gz", hash = "sha256:09c8b63c11318cb2460813871aaa48b671002e59fda67ca909e9883777787581", size = 23012, upload-time = "2025-05-15T03:04:31.817Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/68a997c73a129287785f418c1ebb6004f81e46b53b3caba88c0e03fcd04a/types_requests-2.32.0.20250515-py3-none-any.whl", hash = "sha256:f8eba93b3a892beee32643ff836993f15a785816acca21ea0ffa006f05ef0fb2", size = 20635, upload-time = "2025-05-15T03:04:30.5Z" }, ] [[package]] @@ -2485,36 +2572,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627, upload-time = "2025-03-13T02:51:28.3Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845 }, + { url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845, upload-time = "2025-03-13T02:51:27.055Z" }, ] [[package]] name = "types-simplejson" version = "3.20.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489 } +sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489, upload-time = "2025-03-26T02:53:35.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462 }, + { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462, upload-time = "2025-03-26T02:53:35.036Z" }, ] [[package]] name = "types-ujson" version = "5.10.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340, upload-time = "2025-03-26T02:53:39.197Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644 }, + { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644, upload-time = "2025-03-26T02:53:38.2Z" }, ] [[package]] name = "typing-extensions" version = "4.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, ] [[package]] @@ -2524,32 +2611,32 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, ] [[package]] name = "urllib3" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900 } +sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900, upload-time = "2023-11-13T12:29:45.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579 }, + { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579, upload-time = "2023-11-13T12:29:42.719Z" }, ] [[package]] name = "uvicorn" -version = "0.34.0" +version = "0.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, + { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, ] [package.optional-dependencies] @@ -2567,26 +2654,26 @@ standard = [ name = "uvloop" version = "0.19.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492 } +sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492, upload-time = "2023-10-22T22:03:57.665Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484 }, - { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850 }, - { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601 }, - { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731 }, - { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572 }, - { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235 }, - { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681 }, - { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421 }, - { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000 }, - { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361 }, - { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571 }, - { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459 }, - { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376 }, - { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031 }, - { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630 }, - { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957 }, - { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951 }, - { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911 }, + { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484, upload-time = "2023-10-22T22:02:54.169Z" }, + { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850, upload-time = "2023-10-22T22:02:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601, upload-time = "2023-10-22T22:02:58.717Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731, upload-time = "2023-10-22T22:03:01.043Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572, upload-time = "2023-10-22T22:03:02.874Z" }, + { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235, upload-time = "2023-10-22T22:03:05.361Z" }, + { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681, upload-time = "2023-10-22T22:03:07.158Z" }, + { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421, upload-time = "2023-10-22T22:03:09.4Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000, upload-time = "2023-10-22T22:03:11.755Z" }, + { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361, upload-time = "2023-10-22T22:03:13.841Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571, upload-time = "2023-10-22T22:03:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459, upload-time = "2023-10-22T22:03:17.988Z" }, + { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376, upload-time = "2023-10-22T22:03:20.075Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031, upload-time = "2023-10-22T22:03:21.404Z" }, + { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630, upload-time = "2023-10-22T22:03:23.568Z" }, + { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957, upload-time = "2023-10-22T22:03:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951, upload-time = "2023-10-22T22:03:27.055Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911, upload-time = "2023-10-22T22:03:29.39Z" }, ] [[package]] @@ -2596,106 +2683,115 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098 } +sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098, upload-time = "2023-10-13T13:06:39.809Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182 }, - { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275 }, - { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785 }, - { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374 }, - { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033 }, - { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393 }, - { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953 }, - { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961 }, - { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393 }, - { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409 }, - { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101 }, - { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675 }, - { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210 }, - { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196 }, - { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287 }, - { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653 }, - { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844 }, - { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343 }, - { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858 }, - { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464 }, - { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343 }, - { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338 }, - { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658 }, - { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113 }, - { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688 }, - { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902 }, - { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300 }, - { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126 }, - { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275 }, - { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255 }, - { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845 }, - { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957 }, - { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542 }, - { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238 }, - { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406 }, - { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789 }, - { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292 }, - { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026 }, - { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092 }, - { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188 }, - { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366 }, - { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270 }, + { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182, upload-time = "2023-10-13T13:04:34.803Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275, upload-time = "2023-10-13T13:04:36.632Z" }, + { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785, upload-time = "2023-10-13T13:04:38.641Z" }, + { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374, upload-time = "2023-10-13T13:04:41.711Z" }, + { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033, upload-time = "2023-10-13T13:04:43.324Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393, upload-time = "2023-10-13T13:04:44.818Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953, upload-time = "2023-10-13T13:04:46.714Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961, upload-time = "2023-10-13T13:04:48.072Z" }, + { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393, upload-time = "2023-10-13T13:04:49.638Z" }, + { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409, upload-time = "2023-10-13T13:04:51.762Z" }, + { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101, upload-time = "2023-10-13T13:04:53.78Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675, upload-time = "2023-10-13T13:04:55.113Z" }, + { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210, upload-time = "2023-10-13T13:04:56.894Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196, upload-time = "2023-10-13T13:04:58.19Z" }, + { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287, upload-time = "2023-10-13T13:04:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653, upload-time = "2023-10-13T13:05:01.622Z" }, + { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844, upload-time = "2023-10-13T13:05:03.805Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343, upload-time = "2023-10-13T13:05:05.248Z" }, + { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858, upload-time = "2023-10-13T13:05:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464, upload-time = "2023-10-13T13:05:08.622Z" }, + { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343, upload-time = "2023-10-13T13:05:10.584Z" }, + { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338, upload-time = "2023-10-13T13:05:12.671Z" }, + { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658, upload-time = "2023-10-13T13:05:13.972Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113, upload-time = "2023-10-13T13:05:15.289Z" }, + { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688, upload-time = "2023-10-13T13:05:17.144Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902, upload-time = "2023-10-13T13:05:18.828Z" }, + { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300, upload-time = "2023-10-13T13:05:20.116Z" }, + { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126, upload-time = "2023-10-13T13:05:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275, upload-time = "2023-10-13T13:05:22.995Z" }, + { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255, upload-time = "2023-10-13T13:05:24.618Z" }, + { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845, upload-time = "2023-10-13T13:05:26.531Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957, upload-time = "2023-10-13T13:05:28.365Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542, upload-time = "2023-10-13T13:05:29.862Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238, upload-time = "2023-10-13T13:05:32.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406, upload-time = "2023-10-13T13:05:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789, upload-time = "2023-10-13T13:05:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292, upload-time = "2023-10-13T13:05:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026, upload-time = "2023-10-13T13:05:38.591Z" }, + { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092, upload-time = "2023-10-13T13:06:21.419Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188, upload-time = "2023-10-13T13:06:22.934Z" }, + { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366, upload-time = "2023-10-13T13:06:24.254Z" }, + { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270, upload-time = "2023-10-13T13:06:25.742Z" }, ] [[package]] name = "wcwidth" version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, ] [[package]] name = "websockets" version = "12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994, upload-time = "2023-10-21T14:21:11.88Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025 }, - { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261 }, - { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328 }, - { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925 }, - { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930 }, - { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245 }, - { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966 }, - { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196 }, - { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822 }, - { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454 }, - { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974 }, - { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047 }, - { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282 }, - { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325 }, - { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502 }, - { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491 }, - { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872 }, - { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318 }, - { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594 }, - { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191 }, - { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453 }, - { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971 }, - { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061 }, - { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296 }, - { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326 }, - { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807 }, - { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751 }, - { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176 }, - { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246 }, - { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466 }, - { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083 }, - { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460 }, - { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985 }, - { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110 }, - { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216 }, - { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821 }, - { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768 }, - { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009 }, - { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370 }, + { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025, upload-time = "2023-10-21T14:19:28.387Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261, upload-time = "2023-10-21T14:19:30.203Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328, upload-time = "2023-10-21T14:19:31.765Z" }, + { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925, upload-time = "2023-10-21T14:19:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930, upload-time = "2023-10-21T14:19:35.109Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245, upload-time = "2023-10-21T14:19:36.761Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966, upload-time = "2023-10-21T14:19:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196, upload-time = "2023-10-21T14:19:40.264Z" }, + { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822, upload-time = "2023-10-21T14:19:41.836Z" }, + { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454, upload-time = "2023-10-21T14:19:43.639Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974, upload-time = "2023-10-21T14:19:44.934Z" }, + { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047, upload-time = "2023-10-21T14:19:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282, upload-time = "2023-10-21T14:19:47.739Z" }, + { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325, upload-time = "2023-10-21T14:19:49.4Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502, upload-time = "2023-10-21T14:19:50.683Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491, upload-time = "2023-10-21T14:19:51.835Z" }, + { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872, upload-time = "2023-10-21T14:19:53.071Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318, upload-time = "2023-10-21T14:19:54.41Z" }, + { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594, upload-time = "2023-10-21T14:19:55.982Z" }, + { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191, upload-time = "2023-10-21T14:19:57.349Z" }, + { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453, upload-time = "2023-10-21T14:19:59.11Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971, upload-time = "2023-10-21T14:20:00.243Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061, upload-time = "2023-10-21T14:20:02.221Z" }, + { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296, upload-time = "2023-10-21T14:20:03.591Z" }, + { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326, upload-time = "2023-10-21T14:20:04.956Z" }, + { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807, upload-time = "2023-10-21T14:20:06.153Z" }, + { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751, upload-time = "2023-10-21T14:20:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176, upload-time = "2023-10-21T14:20:09.212Z" }, + { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246, upload-time = "2023-10-21T14:20:10.423Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466, upload-time = "2023-10-21T14:20:11.826Z" }, + { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083, upload-time = "2023-10-21T14:20:13.451Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460, upload-time = "2023-10-21T14:20:14.719Z" }, + { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985, upload-time = "2023-10-21T14:20:15.817Z" }, + { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110, upload-time = "2023-10-21T14:20:48.335Z" }, + { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216, upload-time = "2023-10-21T14:20:50.083Z" }, + { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821, upload-time = "2023-10-21T14:20:51.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768, upload-time = "2023-10-21T14:20:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009, upload-time = "2023-10-21T14:20:54.419Z" }, + { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370, upload-time = "2023-10-21T14:21:10.075Z" }, ] [[package]] @@ -2705,9 +2801,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342 } +sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342, upload-time = "2024-05-05T23:10:31.999Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274 }, + { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274, upload-time = "2024-05-05T23:10:29.567Z" }, +] + +[[package]] +name = "wsproto" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload-time = "2022-08-23T19:58:21.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, ] [[package]] @@ -2717,9 +2825,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350 } +sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350, upload-time = "2023-06-23T06:28:35.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824 }, + { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824, upload-time = "2023-06-23T06:28:32.652Z" }, ] [[package]] @@ -2729,24 +2837,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914 } +sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914, upload-time = "2023-10-05T11:24:38.943Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417 }, - { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528 }, - { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532 }, - { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703 }, - { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078 }, - { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155 }, - { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441 }, - { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530 }, - { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584 }, - { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737 }, - { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104 }, - { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155 }, - { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757 }, - { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654 }, - { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400 }, - { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853 }, - { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127 }, - { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268 }, + { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417, upload-time = "2023-10-05T11:24:25.141Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528, upload-time = "2023-10-05T11:24:27.336Z" }, + { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532, upload-time = "2023-10-05T11:49:20.587Z" }, + { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703, upload-time = "2023-10-05T11:25:33.542Z" }, + { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078, upload-time = "2023-10-05T11:25:48.235Z" }, + { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155, upload-time = "2023-10-05T11:37:56.715Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441, upload-time = "2023-10-05T11:24:20.414Z" }, + { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530, upload-time = "2023-10-05T11:24:22.975Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584, upload-time = "2023-10-05T11:49:22.978Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737, upload-time = "2023-10-05T11:25:35.439Z" }, + { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104, upload-time = "2023-10-05T11:25:51.355Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155, upload-time = "2023-10-05T11:39:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757, upload-time = "2023-10-05T11:25:05.865Z" }, + { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654, upload-time = "2023-10-05T11:25:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400, upload-time = "2023-10-05T11:49:25.326Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853, upload-time = "2023-10-05T11:25:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127, upload-time = "2023-10-05T11:25:53.819Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268, upload-time = "2023-10-05T11:41:22.778Z" }, ] diff --git a/mobile/.fvmrc b/mobile/.fvmrc index 07470f9cab..b987073ac6 100644 --- a/mobile/.fvmrc +++ b/mobile/.fvmrc @@ -1,3 +1,3 @@ { "flutter": "3.29.3" -} +} \ No newline at end of file diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json index ceaf9a6ab8..9c5244f098 100644 --- a/mobile/.vscode/settings.json +++ b/mobile/.vscode/settings.json @@ -1,5 +1,5 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.24.3", + "dart.flutterSdkPath": ".fvm/versions/3.29.3", "search.exclude": { "**/.fvm": true }, diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 04f3145908..854f852e3c 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -35,6 +35,7 @@ linter: analyzer: exclude: - openapi/** + - build/** - lib/generated_plugin_registrant.dart - lib/**/*.g.dart - lib/**/*.drift.dart @@ -92,6 +93,9 @@ custom_lint: allowed: # required / wanted - lib/repositories/*_api.repository.dart + - lib/domain/models/sync_event.model.dart + - lib/{domain,infrastructure}/**/sync_stream.* + - lib/{domain,infrastructure}/**/sync_api.* - lib/infrastructure/repositories/*_api.repository.dart - lib/infrastructure/utils/*.converter.dart # acceptable exceptions for the time being @@ -144,7 +148,9 @@ dart_code_metrics: - avoid-global-state - avoid-inverted-boolean-checks - avoid-late-final-reassignment - - avoid-local-functions + - avoid-local-functions: + exclude: + - test/**.dart - avoid-negated-conditions - avoid-nested-streams-and-futures - avoid-referencing-subclasses diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 58d7f0655a..2179c9eb3c 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ android:maxSdkVersion="32" /> - @@ -19,6 +18,7 @@ + @@ -125,4 +125,4 @@ - + \ No newline at end of file diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt index e7f787e8d8..ae2ec22a71 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackgroundServicePlugin.kt @@ -1,17 +1,17 @@ package app.alextran.immich +import android.app.Activity import android.content.ContentResolver import android.content.ContentUris -import android.content.ContentValues import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.Environment import android.provider.MediaStore import android.provider.Settings import android.util.Log +import androidx.annotation.RequiresApi import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -23,6 +23,7 @@ import io.flutter.plugin.common.PluginRegistry import java.security.MessageDigest import java.io.FileInputStream import kotlinx.coroutines.* +import androidx.core.net.toUri /** * Android plugin for Dart `BackgroundService` and file trash operations @@ -33,7 +34,8 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, private var fileTrashChannel: MethodChannel? = null private var context: Context? = null private var pendingResult: Result? = null - private val PERMISSION_REQUEST_CODE = 1001 + private val permissionRequestCode = 1001 + private val trashRequestCode = 1002 private var activityBinding: ActivityPluginBinding? = null override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -138,36 +140,35 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // File Trash methods moved from MainActivity "moveToTrash" -> { - val fileName = call.argument("fileName") - if (fileName != null) { - if (hasManageStoragePermission()) { - val success = moveToTrash(fileName) - result.success(success) + val mediaUrls = call.argument>("mediaUrls") + if (mediaUrls != null) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) && hasManageMediaPermission()) { + moveToTrash(mediaUrls, result) } else { - result.error("PERMISSION_DENIED", "Storage permission required", null) + result.error("PERMISSION_DENIED", "Media permission required", null) } } else { - result.error("INVALID_NAME", "The file name is not specified.", null) + result.error("INVALID_NAME", "The mediaUrls is not specified.", null) } } "restoreFromTrash" -> { val fileName = call.argument("fileName") - if (fileName != null) { - if (hasManageStoragePermission()) { - val success = untrashImage(fileName) - result.success(success) + val type = call.argument("type") + if (fileName != null && type != null) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) && hasManageMediaPermission()) { + restoreFromTrash(fileName, type, result) } else { - result.error("PERMISSION_DENIED", "Storage permission required", null) + result.error("PERMISSION_DENIED", "Media permission required", null) } } else { result.error("INVALID_NAME", "The file name is not specified.", null) } } - "requestManageStoragePermission" -> { - if (!hasManageStoragePermission()) { - requestManageStoragePermission(result) + "requestManageMediaPermission" -> { + if (!hasManageMediaPermission()) { + requestManageMediaPermission(result) } else { Log.e("Manage storage permission", "Permission already granted") result.success(true) @@ -178,100 +179,98 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, } } - // File Trash methods moved from MainActivity - private fun hasManageStoragePermission(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - Environment.isExternalStorageManager() - } else { - true + private fun hasManageMediaPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + MediaStore.canManageMedia(context!!); + } else { + false } } - private fun requestManageStoragePermission(result: Result) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + private fun requestManageMediaPermission(result: Result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingResult = result // Store the result callback val activity = activityBinding?.activity ?: return - val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) - intent.data = Uri.parse("package:${activity.packageName}") - activity.startActivityForResult(intent, PERMISSION_REQUEST_CODE) + val intent = Intent(Settings.ACTION_REQUEST_MANAGE_MEDIA) + intent.data = "package:${activity.packageName}".toUri() + activity.startActivityForResult(intent, permissionRequestCode) } else { - result.success(true) + result.success(false) } } - private fun moveToTrash(fileName: String): Boolean { - val contentResolver = context?.contentResolver ?: return false - val uri = getFileUri(fileName) + @RequiresApi(Build.VERSION_CODES.R) + private fun moveToTrash(mediaUrls: List, result: Result) { + val urisToTrash = mediaUrls.map { it.toUri() } + if (urisToTrash.isEmpty()) { + result.error("INVALID_ARGS", "No valid URIs provided", null) + return + } + + toggleTrash(urisToTrash, true, result); + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun restoreFromTrash(name: String, type: Int, result: Result) { + val uri = getTrashedFileUri(name, type) + if (uri == null) { + Log.e("TrashError", "Asset Uri cannot be found obtained") + result.error("TrashError", "Asset Uri cannot be found obtained", null) + return + } Log.e("FILE_URI", uri.toString()) - return uri?.let { moveToTrash(it) } ?: false + uri.let { toggleTrash(listOf(it), false, result) } } - private fun moveToTrash(contentUri: Uri): Boolean { - val contentResolver = context?.contentResolver ?: return false - return try { - val values = ContentValues().apply { - put(MediaStore.MediaColumns.IS_TRASHED, 1) // Move to trash + @RequiresApi(Build.VERSION_CODES.R) + private fun toggleTrash(contentUris: List, isTrashed: Boolean, result: Result) { + val activity = activityBinding?.activity + val contentResolver = context?.contentResolver + if (activity == null || contentResolver == null) { + result.error("TrashError", "Activity or ContentResolver not available", null) + return } - val updated = contentResolver.update(contentUri, values, null, null) - updated > 0 - } catch (e: Exception) { - Log.e("TrashError", "Error moving to trash", e) - false + + try { + val pendingIntent = MediaStore.createTrashRequest(contentResolver, contentUris, isTrashed) + pendingResult = result // Store for onActivityResult + activity.startIntentSenderForResult( + pendingIntent.intentSender, + trashRequestCode, + null, 0, 0, 0 + ) + } catch (e: Exception) { + Log.e("TrashError", "Error creating or starting trash request", e) + result.error("TrashError", "Error creating or starting trash request", null) } } - private fun getFileUri(fileName: String): Uri? { + @RequiresApi(Build.VERSION_CODES.R) + private fun getTrashedFileUri(fileName: String, type: Int): Uri? { val contentResolver = context?.contentResolver ?: return null - val contentUri = MediaStore.Files.getContentUri("external") - val projection = arrayOf(MediaStore.Images.Media._ID) - val selection = "${MediaStore.Images.Media.DISPLAY_NAME} = ?" - val selectionArgs = arrayOf(fileName) - var fileUri: Uri? = null - - contentResolver.query(contentUri, projection, selection, selectionArgs, null)?.use { cursor -> - if (cursor.moveToFirst()) { - val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)) - fileUri = ContentUris.withAppendedId(contentUri, id) - } - } - return fileUri - } - - private fun untrashImage(name: String): Boolean { - val contentResolver = context?.contentResolver ?: return false - val uri = getTrashedFileUri(contentResolver, name) - Log.e("FILE_URI", uri.toString()) - return uri?.let { untrashImage(it) } ?: false - } - - private fun untrashImage(contentUri: Uri): Boolean { - val contentResolver = context?.contentResolver ?: return false - return try { - val values = ContentValues().apply { - put(MediaStore.MediaColumns.IS_TRASHED, 0) // Restore file - } - val updated = contentResolver.update(contentUri, values, null, null) - updated > 0 - } catch (e: Exception) { - Log.e("TrashError", "Error restoring file", e) - false - } - } - - private fun getTrashedFileUri(contentResolver: ContentResolver, fileName: String): Uri? { - val contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) + val queryUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) val projection = arrayOf(MediaStore.Files.FileColumns._ID) val queryArgs = Bundle().apply { - putString(ContentResolver.QUERY_ARG_SQL_SELECTION, "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ?") + putString( + ContentResolver.QUERY_ARG_SQL_SELECTION, + "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ?" + ) putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, arrayOf(fileName)) putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_ONLY) } - contentResolver.query(contentUri, projection, queryArgs, null)?.use { cursor -> + contentResolver.query(queryUri, projection, queryArgs, null)?.use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)) + // same order as AssetType from dart + val contentUri = when (type) { + 1 -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI + 2 -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + 3 -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + else -> queryUri + } return ContentUris.withAppendedId(contentUri, id) } } @@ -301,12 +300,19 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler, // ActivityResultListener implementation override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (requestCode == PERMISSION_REQUEST_CODE) { - val granted = hasManageStoragePermission() + if (requestCode == permissionRequestCode) { + val granted = hasManageMediaPermission() pendingResult?.success(granted) pendingResult = null return true } + + if (requestCode == trashRequestCode) { + val approved = resultCode == Activity.RESULT_OK + pendingResult?.success(approved) + pendingResult = null + return true + } return false } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/HttpSSLOptionsPlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/HttpSSLOptionsPlugin.kt new file mode 100644 index 0000000000..44d2aee2ce --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/HttpSSLOptionsPlugin.kt @@ -0,0 +1,146 @@ +package app.alextran.immich + +import android.annotation.SuppressLint +import android.content.Context +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import java.io.ByteArrayInputStream +import java.net.InetSocketAddress +import java.net.Socket +import java.security.KeyStore +import java.security.cert.X509Certificate +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.KeyManager +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLEngine +import javax.net.ssl.SSLSession +import javax.net.ssl.TrustManager +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509ExtendedTrustManager + +/** + * Android plugin for Dart `HttpSSLOptions` + */ +class HttpSSLOptionsPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { + private var methodChannel: MethodChannel? = null + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + onAttachedToEngine(binding.applicationContext, binding.binaryMessenger) + } + + private fun onAttachedToEngine(ctx: Context, messenger: BinaryMessenger) { + methodChannel = MethodChannel(messenger, "immich/httpSSLOptions") + methodChannel?.setMethodCallHandler(this) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + onDetachedFromEngine() + } + + private fun onDetachedFromEngine() { + methodChannel?.setMethodCallHandler(null) + methodChannel = null + } + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + try { + when (call.method) { + "apply" -> { + val args = call.arguments>()!! + + var tm: Array? = null + if (args[0] as Boolean) { + tm = arrayOf(AllowSelfSignedTrustManager(args[1] as? String)) + } + + var km: Array? = null + if (args[2] != null) { + val cert = ByteArrayInputStream(args[2] as ByteArray) + val password = (args[3] as String).toCharArray() + val keyStore = KeyStore.getInstance("PKCS12") + keyStore.load(cert, password) + val keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + keyManagerFactory.init(keyStore, null) + km = keyManagerFactory.keyManagers + } + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(km, tm, null) + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) + + HttpsURLConnection.setDefaultHostnameVerifier(AllowSelfSignedHostnameVerifier(args[1] as? String)) + + result.success(true) + } + + else -> result.notImplemented() + } + } catch (e: Throwable) { + result.error("error", e.message, null) + } + } + + @SuppressLint("CustomX509TrustManager") + class AllowSelfSignedTrustManager(private val serverHost: String?) : X509ExtendedTrustManager() { + private val defaultTrustManager: X509ExtendedTrustManager = getDefaultTrustManager() + + override fun checkClientTrusted(chain: Array?, authType: String?) = + defaultTrustManager.checkClientTrusted(chain, authType) + + override fun checkClientTrusted( + chain: Array?, authType: String?, socket: Socket? + ) = defaultTrustManager.checkClientTrusted(chain, authType, socket) + + override fun checkClientTrusted( + chain: Array?, authType: String?, engine: SSLEngine? + ) = defaultTrustManager.checkClientTrusted(chain, authType, engine) + + override fun checkServerTrusted(chain: Array?, authType: String?) { + if (serverHost == null) return + defaultTrustManager.checkServerTrusted(chain, authType) + } + + override fun checkServerTrusted( + chain: Array?, authType: String?, socket: Socket? + ) { + if (serverHost == null) return + val socketAddress = socket?.remoteSocketAddress + if (socketAddress is InetSocketAddress && socketAddress.hostName == serverHost) return + defaultTrustManager.checkServerTrusted(chain, authType, socket) + } + + override fun checkServerTrusted( + chain: Array?, authType: String?, engine: SSLEngine? + ) { + if (serverHost == null || engine?.peerHost == serverHost) return + defaultTrustManager.checkServerTrusted(chain, authType, engine) + } + + override fun getAcceptedIssuers(): Array = defaultTrustManager.acceptedIssuers + + private fun getDefaultTrustManager(): X509ExtendedTrustManager { + val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + factory.init(null as KeyStore?) + return factory.trustManagers.filterIsInstance().first() + } + } + + class AllowSelfSignedHostnameVerifier(private val serverHost: String?) : HostnameVerifier { + companion object { + private val _defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier() + } + + override fun verify(hostname: String?, session: SSLSession?): Boolean { + if (serverHost == null || hostname == serverHost) { + return true + } else { + return _defaultHostnameVerifier.verify(hostname, session) + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt index 2b6bf81148..c1e5152d28 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt @@ -1,13 +1,14 @@ package app.alextran.immich -import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine import androidx.annotation.NonNull +import io.flutter.embedding.android.FlutterFragmentActivity +import io.flutter.embedding.engine.FlutterEngine -class MainActivity : FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - super.configureFlutterEngine(flutterEngine) - flutterEngine.plugins.add(BackgroundServicePlugin()) - // No need to set up method channel here as it's now handled in the plugin - } +class MainActivity : FlutterFragmentActivity() { + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + flutterEngine.plugins.add(BackgroundServicePlugin()) + flutterEngine.plugins.add(HttpSSLOptionsPlugin()) + // No need to set up method channel here as it's now handled in the plugin + } } diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml index 0fdc703671..0a4dd28549 100644 --- a/mobile/android/app/src/main/res/values/styles.xml +++ b/mobile/android/app/src/main/res/values/styles.xml @@ -1,22 +1,23 @@ - - - - - + + \ No newline at end of file diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 612e5084d2..20dfaaffad 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 193, - "android.injected.version.name" => "1.131.3", + "android.injected.version.code" => 200, + "android.injected.version.name" => "1.134.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/devtools_options.yaml b/mobile/devtools_options.yaml index fa0b357c4f..f592d85a9b 100644 --- a/mobile/devtools_options.yaml +++ b/mobile/devtools_options.yaml @@ -1,3 +1,4 @@ description: This file stores settings for Dart & Flutter DevTools. documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states extensions: + - drift: true \ No newline at end of file diff --git a/mobile/ios/Podfile b/mobile/ios/Podfile index 009dc5e8d4..ca0166a382 100644 --- a/mobile/ios/Podfile +++ b/mobile/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '14.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -45,7 +45,7 @@ post_install do |installer| installer.generated_projects.each do |project| project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' end end end diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 17b606aef2..45c84ce3e6 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -44,6 +44,8 @@ PODS: - Flutter - flutter_native_splash (2.4.3): - Flutter + - flutter_secure_storage (6.0.0): + - Flutter - flutter_udid (0.0.1): - Flutter - SAMKeychain @@ -59,6 +61,9 @@ PODS: - Flutter - isar_flutter_libs (1.0.0): - Flutter + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS - MapLibre (6.5.0) - maplibre_gl (0.0.1): - Flutter @@ -130,6 +135,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_udid (from `.symlinks/plugins/flutter_udid/ios`) - flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) @@ -137,6 +143,7 @@ DEPENDENCIES: - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) + - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - native_video_player (from `.symlinks/plugins/native_video_player/ios`) - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) @@ -178,6 +185,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_local_notifications/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" flutter_udid: :path: ".symlinks/plugins/flutter_udid/ios" flutter_web_auth_2: @@ -192,6 +201,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/integration_test/ios" isar_flutter_libs: :path: ".symlinks/plugins/isar_flutter_libs/ios" + local_auth_darwin: + :path: ".symlinks/plugins/local_auth_darwin/darwin" maplibre_gl: :path: ".symlinks/plugins/maplibre_gl/ios" native_video_player: @@ -224,6 +235,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: + background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe @@ -233,6 +245,7 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf + flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9 flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 @@ -240,6 +253,7 @@ SPEC CHECKSUMS: image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26 + local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 MapLibre: 0ebfa9329d313cec8bf0a5ba5a336a1dc903785e maplibre_gl: eab61cca6e1cfa9187249bacd3f08b51e8cd8ae9 native_video_player: b65c58951ede2f93d103a25366bdebca95081265 diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index f1b241ab8c..311f19857b 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -261,9 +261,11 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; }; FAC6F88F2D287C890078CB2F = { CreatedOnToolsVersion = 16.0; + ProvisioningStyle = Automatic; }; }; }; @@ -541,12 +543,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -685,12 +687,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -715,12 +717,12 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -748,7 +750,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -769,6 +771,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -791,7 +794,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -811,6 +814,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -831,7 +835,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 201; + CURRENT_PROJECT_VERSION = 208; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -851,6 +855,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index bad1ea42f2..38a1573dbd 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -1,165 +1,167 @@ - - AppGroupId - $(CUSTOM_GROUP_ID) - BGTaskSchedulerPermittedIdentifiers - - app.alextran.immich.backgroundFetch - app.alextran.immich.backgroundProcessing - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleDocumentTypes - - - CFBundleTypeName - ShareHandler - LSHandlerRank - Alternate - LSItemContentTypes - - public.file-url - public.image - public.text - public.movie - public.url - public.data - - - - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - ar - ca - cs - da - de - es - fi - fr - he - hi - hu - it - ja - ko - lv - mn - nb - nl - pl - pt - ro - ru - sk - sl - sr - sv - th - uk - vi - zh - - CFBundleName - immich_mobile - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.131.3 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) - - - - CFBundleVersion - 201 - FLTEnableImpeller - - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - https - - LSRequiresIPhoneOS - - LSSupportsOpeningDocumentsInPlace - No - MGLMapboxMetricsEnabledSettingShownInApp - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSCameraUsageDescription - We need to access the camera to let you take beautiful video using this app - NSLocationAlwaysAndWhenInUseUsageDescription - We require this permission to access the local WiFi name for background upload mechanism - NSLocationUsageDescription - We require this permission to access the local WiFi name - NSLocationWhenInUseUsageDescription - We require this permission to access the local WiFi name - NSMicrophoneUsageDescription - We need to access the microphone to let you take beautiful video using this app - NSPhotoLibraryAddUsageDescription - We need to manage backup your photos album - NSPhotoLibraryUsageDescription - We need to manage backup your photos album - NSUserActivityTypes - - INSendMessageIntent - - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - fetch - processing - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - io.flutter.embedded_views_preview - - - + + AppGroupId + $(CUSTOM_GROUP_ID) + BGTaskSchedulerPermittedIdentifiers + + app.alextran.immich.backgroundFetch + app.alextran.immich.backgroundProcessing + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleDocumentTypes + + + CFBundleTypeName + ShareHandler + LSHandlerRank + Alternate + LSItemContentTypes + + public.file-url + public.image + public.text + public.movie + public.url + public.data + + + + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + ar + ca + cs + da + de + es + fi + fr + he + hi + hu + it + ja + ko + lv + mn + nb + nl + pl + pt + ro + ru + sk + sl + sr + sv + th + uk + vi + zh + + CFBundleName + immich_mobile + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.134.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) + + + + CFBundleVersion + 208 + FLTEnableImpeller + + ITSAppUsesNonExemptEncryption + + LSApplicationQueriesSchemes + + https + + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + No + MGLMapboxMetricsEnabledSettingShownInApp + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSCameraUsageDescription + We need to access the camera to let you take beautiful video using this app + NSLocationAlwaysAndWhenInUseUsageDescription + We require this permission to access the local WiFi name for background upload mechanism + NSLocationUsageDescription + We require this permission to access the local WiFi name + NSLocationWhenInUseUsageDescription + We require this permission to access the local WiFi name + NSMicrophoneUsageDescription + We need to access the microphone to let you take beautiful video using this app + NSPhotoLibraryAddUsageDescription + We need to manage backup your photos album + NSPhotoLibraryUsageDescription + We need to manage backup your photos album + NSUserActivityTypes + + INSendMessageIntent + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + fetch + processing + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + io.flutter.embedded_views_preview + + NSFaceIDUsageDescription + We need to use FaceID to allow access to your locked folder + + \ No newline at end of file diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 4853e9be43..1d93a13568 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -18,8 +18,11 @@ default_platform(:ios) platform :ios do desc "iOS Release" lane :release do + enable_automatic_code_signing( + path: "./Runner.xcodeproj", + ) increment_version_number( - version_number: "1.131.3" + version_number: "1.134.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 83d540d54c..33683afd92 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -5,5 +5,12 @@ const double downloadFailed = -2; // Number of log entries to retain on app start const int kLogTruncateLimit = 250; +// Sync +const int kSyncEventBatchSize = 5000; + +// Hash batch limits const int kBatchHashFileLimit = 128; const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB + +// Secure storage keys +const String kSecuredPinCode = "secured_pin_code"; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index 3a3bf9959a..a691263a1e 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -8,3 +8,5 @@ enum TextSearchType { filename, description, } + +enum AssetVisibilityEnum { timeline, hidden, archive, locked } diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index 836d3cf06f..b4d4b63660 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -6,11 +6,14 @@ const Map locales = { // Additional locales 'Arabic (ar)': Locale('ar'), 'Catalan (ca)': Locale('ca'), - 'Chinese Simplified (zh_CN)': Locale('zh', 'SIMPLIFIED'), - 'Chinese Traditional (zh_TW)': Locale('zh', 'Hant'), + 'Chinese Simplified (zh_CN)': + Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), + 'Chinese Traditional (zh_TW)': + Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), 'Czech (cs)': Locale('cs'), 'Danish (da)': Locale('da'), 'Dutch (nl)': Locale('nl'), + 'Estonian (et)': Locale('et'), 'Finnish (fi)': Locale('fi'), 'French (fr)': Locale('fr'), 'Galician (gl)': Locale('gl'), @@ -31,11 +34,14 @@ const Map locales = { 'Portuguese (pt)': Locale('pt'), 'Romanian (ro)': Locale('ro'), 'Russian (ru)': Locale('ru'), - 'Serbian Cyrillic (sr_Cyrl)': Locale('sr', 'Cyrl'), - 'Serbian Latin (sr_Latn)': Locale('sr', 'Latn'), + 'Serbian Cyrillic (sr_Cyrl)': + Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl'), + 'Serbian Latin (sr_Latn)': + Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn'), 'Slovak (sk)': Locale('sk'), 'Slovenian (sl)': Locale('sl'), 'Spanish (es)': Locale('es'), + 'Swedish (sv)': Locale('sv'), 'Thai (th)': Locale('th'), 'Turkish (tr)': Locale('tr'), 'Ukrainian (uk)': Locale('uk'), diff --git a/mobile/lib/domain/interfaces/sync_api.interface.dart b/mobile/lib/domain/interfaces/sync_api.interface.dart index fb8f1aa46e..57abed2e7f 100644 --- a/mobile/lib/domain/interfaces/sync_api.interface.dart +++ b/mobile/lib/domain/interfaces/sync_api.interface.dart @@ -1,7 +1,12 @@ -import 'package:immich_mobile/domain/models/sync/sync_event.model.dart'; +import 'package:http/http.dart' as http; +import 'package:immich_mobile/domain/models/sync_event.model.dart'; abstract interface class ISyncApiRepository { - Future ack(String data); + Future ack(List data); - Stream> watchUserSyncEvent(); + Future streamChanges( + Function(List, Function() abort) onData, { + int batchSize, + http.Client? httpClient, + }); } diff --git a/mobile/lib/domain/interfaces/sync_stream.interface.dart b/mobile/lib/domain/interfaces/sync_stream.interface.dart new file mode 100644 index 0000000000..5f61d6b52f --- /dev/null +++ b/mobile/lib/domain/interfaces/sync_stream.interface.dart @@ -0,0 +1,18 @@ +import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:openapi/api.dart'; + +abstract interface class ISyncStreamRepository implements IDatabaseRepository { + Future updateUsersV1(Iterable data); + Future deleteUsersV1(Iterable data); + + Future updatePartnerV1(Iterable data); + Future deletePartnerV1(Iterable data); + + Future updateAssetsV1(Iterable data); + Future deleteAssetsV1(Iterable data); + Future updateAssetsExifV1(Iterable data); + + Future updatePartnerAssetsV1(Iterable data); + Future deletePartnerAssetsV1(Iterable data); + Future updatePartnerAssetsExifV1(Iterable data); +} diff --git a/mobile/lib/domain/models/sync/sync_event.model.dart b/mobile/lib/domain/models/sync/sync_event.model.dart deleted file mode 100644 index f4642d59cf..0000000000 --- a/mobile/lib/domain/models/sync/sync_event.model.dart +++ /dev/null @@ -1,14 +0,0 @@ -class SyncEvent { - // dynamic - final dynamic data; - - final String ack; - - SyncEvent({ - required this.data, - required this.ack, - }); - - @override - String toString() => 'SyncEvent(data: $data, ack: $ack)'; -} diff --git a/mobile/lib/domain/models/sync_event.model.dart b/mobile/lib/domain/models/sync_event.model.dart new file mode 100644 index 0000000000..2ad8a75fe1 --- /dev/null +++ b/mobile/lib/domain/models/sync_event.model.dart @@ -0,0 +1,13 @@ +import 'package:openapi/api.dart'; + +class SyncEvent { + final SyncEntityType type; + // ignore: avoid-dynamic + final dynamic data; + final String ack; + + const SyncEvent({required this.type, required this.data, required this.ack}); + + @override + String toString() => 'SyncEvent(type: $type, data: $data, ack: $ack)'; +} diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 72e29b3677..ac63734b07 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -1,49 +1,92 @@ +// ignore_for_file: avoid-passing-async-when-sync-expected + import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; +import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; +import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; class SyncStreamService { + final Logger _logger = Logger('SyncStreamService'); + final ISyncApiRepository _syncApiRepository; + final ISyncStreamRepository _syncStreamRepository; + final bool Function()? _cancelChecker; - SyncStreamService(this._syncApiRepository); + SyncStreamService({ + required ISyncApiRepository syncApiRepository, + required ISyncStreamRepository syncStreamRepository, + bool Function()? cancelChecker, + }) : _syncApiRepository = syncApiRepository, + _syncStreamRepository = syncStreamRepository, + _cancelChecker = cancelChecker; - StreamSubscription? _userSyncSubscription; + bool get isCancelled => _cancelChecker?.call() ?? false; - void syncUsers() { - _userSyncSubscription = - _syncApiRepository.watchUserSyncEvent().listen((events) async { - for (final event in events) { - if (event.data is SyncUserV1) { - final data = event.data as SyncUserV1; - debugPrint("User Update: $data"); + Future sync() => _syncApiRepository.streamChanges(_handleEvents); - // final user = await _userRepository.get(data.id); - - // if (user == null) { - // continue; - // } - - // user.name = data.name; - // user.email = data.email; - // user.updatedAt = DateTime.now(); - - // await _userRepository.update(user); - // await _syncApiRepository.ack(event.ack); - } - - if (event.data is SyncUserDeleteV1) { - final data = event.data as SyncUserDeleteV1; - - debugPrint("User delete: $data"); - // await _syncApiRepository.ack(event.ack); - } + Future _handleEvents(List events, Function() abort) async { + List items = []; + for (final event in events) { + if (isCancelled) { + _logger.warning("Sync stream cancelled"); + abort(); + return; } - }); + + if (event.type != items.firstOrNull?.type) { + await _processBatch(items); + } + + items.add(event); + } + + await _processBatch(items); } - Future dispose() async { - await _userSyncSubscription?.cancel(); + Future _processBatch(List batch) async { + if (batch.isEmpty) { + return; + } + + final type = batch.first.type; + await _handleSyncData(type, batch.map((e) => e.data)); + await _syncApiRepository.ack([batch.last.ack]); + batch.clear(); + } + + Future _handleSyncData( + SyncEntityType type, + // ignore: avoid-dynamic + Iterable data, + ) async { + _logger.fine("Processing sync data for $type of length ${data.length}"); + // ignore: prefer-switch-expression + switch (type) { + case SyncEntityType.userV1: + return _syncStreamRepository.updateUsersV1(data.cast()); + case SyncEntityType.userDeleteV1: + return _syncStreamRepository.deleteUsersV1(data.cast()); + case SyncEntityType.partnerV1: + return _syncStreamRepository.updatePartnerV1(data.cast()); + case SyncEntityType.partnerDeleteV1: + return _syncStreamRepository.deletePartnerV1(data.cast()); + case SyncEntityType.assetV1: + return _syncStreamRepository.updateAssetsV1(data.cast()); + case SyncEntityType.assetDeleteV1: + return _syncStreamRepository.deleteAssetsV1(data.cast()); + case SyncEntityType.assetExifV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast()); + case SyncEntityType.partnerAssetV1: + return _syncStreamRepository.updatePartnerAssetsV1(data.cast()); + case SyncEntityType.partnerAssetDeleteV1: + return _syncStreamRepository.deletePartnerAssetsV1(data.cast()); + case SyncEntityType.partnerAssetExifV1: + return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast()); + default: + _logger.warning("Unknown sync data type: $type"); + } } } diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart new file mode 100644 index 0000000000..f63dc81ba9 --- /dev/null +++ b/mobile/lib/domain/utils/background_sync.dart @@ -0,0 +1,39 @@ +// ignore_for_file: avoid-passing-async-when-sync-expected + +import 'dart:async'; + +import 'package:immich_mobile/providers/infrastructure/sync_stream.provider.dart'; +import 'package:immich_mobile/utils/isolate.dart'; +import 'package:worker_manager/worker_manager.dart'; + +class BackgroundSyncManager { + Cancelable? _syncTask; + + BackgroundSyncManager(); + + Future cancel() { + final futures = []; + + if (_syncTask != null) { + futures.add(_syncTask!.future); + } + _syncTask?.cancel(); + _syncTask = null; + + return Future.wait(futures); + } + + Future sync() { + if (_syncTask != null) { + return _syncTask!.future; + } + + _syncTask = runInIsolateGentle( + computation: (ref) => ref.read(syncStreamServiceProvider).sync(), + ); + _syncTask!.whenComplete(() { + _syncTask = null; + }); + return _syncTask!.future; + } +} diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index 084cd1ee5d..d8d2bd23c3 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' @@ -45,7 +46,8 @@ class Asset { : remote.stack?.primaryAssetId, stackCount = remote.stack?.assetCount ?? 0, stackId = remote.stack?.id, - thumbhash = remote.thumbhash; + thumbhash = remote.thumbhash, + visibility = getVisibility(remote.visibility); Asset({ this.id = Isar.autoIncrement, @@ -71,6 +73,7 @@ class Asset { this.stackCount = 0, this.isOffline = false, this.thumbhash, + this.visibility = AssetVisibilityEnum.timeline, }); @ignore @@ -173,6 +176,9 @@ class Asset { int stackCount; + @Enumerated(EnumType.ordinal) + AssetVisibilityEnum visibility; + /// Returns null if the asset has no sync access to the exif info @ignore double? get aspectRatio { @@ -349,7 +355,8 @@ class Asset { a.thumbhash != thumbhash || stackId != a.stackId || stackCount != a.stackCount || - stackPrimaryAssetId == null && a.stackPrimaryAssetId != null; + stackPrimaryAssetId == null && a.stackPrimaryAssetId != null || + visibility != a.visibility; } /// Returns a new [Asset] with values from this and merged & updated with [a] @@ -452,6 +459,7 @@ class Asset { String? stackPrimaryAssetId, int? stackCount, String? thumbhash, + AssetVisibilityEnum? visibility, }) => Asset( id: id ?? this.id, @@ -477,6 +485,7 @@ class Asset { stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, stackCount: stackCount ?? this.stackCount, thumbhash: thumbhash ?? this.thumbhash, + visibility: visibility ?? this.visibility, ); Future put(Isar db) async { @@ -541,8 +550,22 @@ class Asset { "isArchived": $isArchived, "isTrashed": $isTrashed, "isOffline": $isOffline, + "visibility": "$visibility", }"""; } + + static getVisibility(AssetVisibility visibility) { + switch (visibility) { + case AssetVisibility.timeline: + return AssetVisibilityEnum.timeline; + case AssetVisibility.archive: + return AssetVisibilityEnum.archive; + case AssetVisibility.hidden: + return AssetVisibilityEnum.hidden; + case AssetVisibility.locked: + return AssetVisibilityEnum.locked; + } + } } enum AssetType { diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index 07eee4825e..b558690813 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -118,8 +118,14 @@ const AssetSchema = CollectionSchema( name: r'updatedAt', type: IsarType.dateTime, ), - r'width': PropertySchema( + r'visibility': PropertySchema( id: 20, + name: r'visibility', + type: IsarType.byte, + enumMap: _AssetvisibilityEnumValueMap, + ), + r'width': PropertySchema( + id: 21, name: r'width', type: IsarType.int, ) @@ -256,7 +262,8 @@ void _assetSerialize( writer.writeString(offsets[17], object.thumbhash); writer.writeByte(offsets[18], object.type.index); writer.writeDateTime(offsets[19], object.updatedAt); - writer.writeInt(offsets[20], object.width); + writer.writeByte(offsets[20], object.visibility.index); + writer.writeInt(offsets[21], object.width); } Asset _assetDeserialize( @@ -288,7 +295,10 @@ Asset _assetDeserialize( type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, updatedAt: reader.readDateTime(offsets[19]), - width: reader.readIntOrNull(offsets[20]), + visibility: + _AssetvisibilityValueEnumMap[reader.readByteOrNull(offsets[20])] ?? + AssetVisibilityEnum.timeline, + width: reader.readIntOrNull(offsets[21]), ); return object; } @@ -342,6 +352,9 @@ P _assetDeserializeProp

( case 19: return (reader.readDateTime(offset)) as P; case 20: + return (_AssetvisibilityValueEnumMap[reader.readByteOrNull(offset)] ?? + AssetVisibilityEnum.timeline) as P; + case 21: return (reader.readIntOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -360,6 +373,18 @@ const _AssettypeValueEnumMap = { 2: AssetType.video, 3: AssetType.audio, }; +const _AssetvisibilityEnumValueMap = { + 'timeline': 0, + 'hidden': 1, + 'archive': 2, + 'locked': 3, +}; +const _AssetvisibilityValueEnumMap = { + 0: AssetVisibilityEnum.timeline, + 1: AssetVisibilityEnum.hidden, + 2: AssetVisibilityEnum.archive, + 3: AssetVisibilityEnum.locked, +}; Id _assetGetId(Asset object) { return object.id; @@ -2477,6 +2502,59 @@ extension AssetQueryFilter on QueryBuilder { }); } + QueryBuilder visibilityEqualTo( + AssetVisibilityEnum value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'visibility', + value: value, + )); + }); + } + + QueryBuilder visibilityGreaterThan( + AssetVisibilityEnum value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'visibility', + value: value, + )); + }); + } + + QueryBuilder visibilityLessThan( + AssetVisibilityEnum value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'visibility', + value: value, + )); + }); + } + + QueryBuilder visibilityBetween( + AssetVisibilityEnum lower, + AssetVisibilityEnum upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'visibility', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + QueryBuilder widthIsNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNull( @@ -2791,6 +2869,18 @@ extension AssetQuerySortBy on QueryBuilder { }); } + QueryBuilder sortByVisibility() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'visibility', Sort.asc); + }); + } + + QueryBuilder sortByVisibilityDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'visibility', Sort.desc); + }); + } + QueryBuilder sortByWidth() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'width', Sort.asc); @@ -3057,6 +3147,18 @@ extension AssetQuerySortThenBy on QueryBuilder { }); } + QueryBuilder thenByVisibility() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'visibility', Sort.asc); + }); + } + + QueryBuilder thenByVisibilityDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'visibility', Sort.desc); + }); + } + QueryBuilder thenByWidth() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'width', Sort.asc); @@ -3201,6 +3303,12 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } + QueryBuilder distinctByVisibility() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'visibility'); + }); + } + QueryBuilder distinctByWidth() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'width'); @@ -3335,6 +3443,13 @@ extension AssetQueryProperty on QueryBuilder { }); } + QueryBuilder + visibilityProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'visibility'); + }); + } + QueryBuilder widthProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'width'); diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 67411013ee..73c8c2d34c 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,3 +1,7 @@ +import 'dart:typed_data'; + +import 'package:uuid/parsing.dart'; + extension StringExtension on String { String capitalize() { return split(" ") @@ -29,3 +33,8 @@ extension DurationExtension on String { return int.parse(this); } } + +extension UUIDExtension on String { + Uint8List toUuidByte({bool shouldValidate = false}) => + UuidParsing.parseAsByteList(this, validate: shouldValidate); +} diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 88a6838c44..c69122335e 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -1,55 +1,71 @@ import 'dart:async'; import 'dart:convert'; -import 'package:flutter/foundation.dart'; -import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; -import 'package:immich_mobile/domain/models/sync/sync_event.model.dart'; -import 'package:immich_mobile/services/api.service.dart'; -import 'package:openapi/api.dart'; import 'package:http/http.dart' as http; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; +import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:logging/logging.dart'; +import 'package:openapi/api.dart'; class SyncApiRepository implements ISyncApiRepository { + final Logger _logger = Logger('SyncApiRepository'); final ApiService _api; - const SyncApiRepository(this._api); + SyncApiRepository(this._api); @override - Stream> watchUserSyncEvent() { - return _getSyncStream( - SyncStreamDto(types: [SyncRequestType.usersV1]), - ); + Future ack(List data) { + return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data)); } @override - Future ack(String data) { - return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: [data])); - } - - Stream> _getSyncStream( - SyncStreamDto dto, { - int batchSize = 5000, - }) async* { - final client = http.Client(); + Future streamChanges( + Function(List, Function() abort) onData, { + int batchSize = kSyncEventBatchSize, + http.Client? httpClient, + }) async { + final stopwatch = Stopwatch()..start(); + final client = httpClient ?? http.Client(); final endpoint = "${_api.apiClient.basePath}/sync/stream"; - final headers = { + final headers = { 'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json', }; - final queryParams = []; final headerParams = {}; - await _api.applyToParams(queryParams, headerParams); + await _api.applyToParams([], headerParams); headers.addAll(headerParams); final request = http.Request('POST', Uri.parse(endpoint)); request.headers.addAll(headers); - request.body = jsonEncode(dto.toJson()); + request.body = jsonEncode( + SyncStreamDto( + types: [ + SyncRequestType.usersV1, + SyncRequestType.partnersV1, + SyncRequestType.assetsV1, + SyncRequestType.partnerAssetsV1, + SyncRequestType.assetExifsV1, + SyncRequestType.partnerAssetExifsV1, + ], + ).toJson(), + ); String previousChunk = ''; List lines = []; + bool shouldAbort = false; + + void abort() { + _logger.warning("Abort requested, stopping sync stream"); + shouldAbort = true; + } + try { - final response = await client.send(request); + final response = + await client.send(request).timeout(const Duration(seconds: 20)); if (response.statusCode != 200) { final errorBody = await response.stream.bytesToString(); @@ -60,8 +76,12 @@ class SyncApiRepository implements ISyncApiRepository { } await for (final chunk in response.stream.transform(utf8.decoder)) { + if (shouldAbort) { + break; + } + previousChunk += chunk; - final parts = previousChunk.split('\n'); + final parts = previousChunk.toString().split('\n'); previousChunk = parts.removeLast(); lines.addAll(parts); @@ -69,44 +89,55 @@ class SyncApiRepository implements ISyncApiRepository { continue; } - yield await compute(_parseSyncResponse, lines); + await onData(_parseLines(lines), abort); lines.clear(); } - } finally { - if (lines.isNotEmpty) { - yield await compute(_parseSyncResponse, lines); + + if (lines.isNotEmpty && !shouldAbort) { + await onData(_parseLines(lines), abort); } + } catch (error, stack) { + _logger.severe("Error processing stream", error, stack); + return Future.error(error, stack); + } finally { client.close(); } + stopwatch.stop(); + _logger + .info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); } -} -const _kResponseMap = { - SyncEntityType.userV1: SyncUserV1.fromJson, - SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson, -}; + List _parseLines(List lines) { + final List data = []; -// Need to be outside of the class to be able to use compute -List _parseSyncResponse(List lines) { - final List data = []; - - for (var line in lines) { - try { + for (final line in lines) { final jsonData = jsonDecode(line); final type = SyncEntityType.fromJson(jsonData['type'])!; final dataJson = jsonData['data']; final ack = jsonData['ack']; final converter = _kResponseMap[type]; if (converter == null) { - debugPrint("[_parseSyncReponse] Unknown type $type"); + _logger.warning("Unknown type $type"); continue; } - data.add(SyncEvent(data: converter(dataJson), ack: ack)); - } catch (error, stack) { - debugPrint("[_parseSyncReponse] Error parsing json $error $stack"); + data.add(SyncEvent(type: type, data: converter(dataJson), ack: ack)); } - } - return data; + return data; + } } + +// ignore: avoid-dynamic +const _kResponseMap = { + SyncEntityType.userV1: SyncUserV1.fromJson, + SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson, + SyncEntityType.partnerV1: SyncPartnerV1.fromJson, + SyncEntityType.partnerDeleteV1: SyncPartnerDeleteV1.fromJson, + SyncEntityType.assetV1: SyncAssetV1.fromJson, + SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson, + SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson, + SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson, + SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson, + SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson, +}; diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart new file mode 100644 index 0000000000..5ad9a369df --- /dev/null +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -0,0 +1,134 @@ +import 'package:drift/drift.dart'; +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; +import 'package:immich_mobile/extensions/string_extensions.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:logging/logging.dart'; +import 'package:openapi/api.dart'; + +class DriftSyncStreamRepository extends DriftDatabaseRepository + implements ISyncStreamRepository { + final Logger _logger = Logger('DriftSyncStreamRepository'); + final Drift _db; + + DriftSyncStreamRepository(super.db) : _db = db; + + @override + Future deleteUsersV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final user in data) { + batch.delete( + _db.userEntity, + UserEntityCompanion(id: Value(user.userId.toUuidByte())), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error while processing SyncUserDeleteV1', error, stack); + rethrow; + } + } + + @override + Future updateUsersV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final user in data) { + final companion = UserEntityCompanion( + name: Value(user.name), + email: Value(user.email), + ); + + batch.insert( + _db.userEntity, + companion.copyWith(id: Value(user.id.toUuidByte())), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error while processing SyncUserV1', error, stack); + rethrow; + } + } + + @override + Future deletePartnerV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final partner in data) { + batch.delete( + _db.partnerEntity, + PartnerEntityCompanion( + sharedById: Value(partner.sharedById.toUuidByte()), + sharedWithId: Value(partner.sharedWithId.toUuidByte()), + ), + ); + } + }); + } catch (e, s) { + _logger.severe('Error while processing SyncPartnerDeleteV1', e, s); + rethrow; + } + } + + @override + Future updatePartnerV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final partner in data) { + final companion = + PartnerEntityCompanion(inTimeline: Value(partner.inTimeline)); + + batch.insert( + _db.partnerEntity, + companion.copyWith( + sharedById: Value(partner.sharedById.toUuidByte()), + sharedWithId: Value(partner.sharedWithId.toUuidByte()), + ), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (e, s) { + _logger.severe('Error while processing SyncPartnerV1', e, s); + rethrow; + } + } + + // Assets + @override + Future updateAssetsV1(Iterable data) async { + debugPrint("updateAssetsV1 - ${data.length}"); + } + + @override + Future deleteAssetsV1(Iterable data) async { + debugPrint("deleteAssetsV1 - ${data.length}"); + } + + // Partner Assets + @override + Future updatePartnerAssetsV1(Iterable data) async { + debugPrint("updatePartnerAssetsV1 - ${data.length}"); + } + + @override + Future deletePartnerAssetsV1(Iterable data) async { + debugPrint("deletePartnerAssetsV1 - ${data.length}"); + } + + // EXIF + @override + Future updateAssetsExifV1(Iterable data) async { + debugPrint("updateAssetsExifV1 - ${data.length}"); + } + + @override + Future updatePartnerAssetsExifV1(Iterable data) async { + debugPrint("updatePartnerAssetsExifV1 - ${data.length}"); + } +} diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart index 76744c9172..ca9e9d64fb 100644 --- a/mobile/lib/interfaces/asset.interface.dart +++ b/mobile/lib/interfaces/asset.interface.dart @@ -61,7 +61,7 @@ abstract interface class IAssetRepository implements IDatabaseRepository { Future> getTrashAssets(String userId); - Future> getRecentlyAddedAssets(String userId); + Future> getRecentlyTakenAssets(String userId); Future> getMotionAssets(String userId); } diff --git a/mobile/lib/interfaces/asset_api.interface.dart b/mobile/lib/interfaces/asset_api.interface.dart index fe3320c9bb..a17e607d83 100644 --- a/mobile/lib/interfaces/asset_api.interface.dart +++ b/mobile/lib/interfaces/asset_api.interface.dart @@ -1,3 +1,4 @@ +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; abstract interface class IAssetApiRepository { @@ -15,4 +16,9 @@ abstract interface class IAssetApiRepository { // Future delete(String id); Future> search({List personIds = const []}); + + Future updateVisibility( + List list, + AssetVisibilityEnum visibility, + ); } diff --git a/mobile/lib/interfaces/auth_api.interface.dart b/mobile/lib/interfaces/auth_api.interface.dart index 0a4b235ff3..bb9a8b5a2c 100644 --- a/mobile/lib/interfaces/auth_api.interface.dart +++ b/mobile/lib/interfaces/auth_api.interface.dart @@ -6,4 +6,9 @@ abstract interface class IAuthApiRepository { Future logout(); Future changePassword(String newPassword); + + Future unlockPinCode(String pinCode); + Future lockPinCode(); + + Future setupPinCode(String pinCode); } diff --git a/mobile/lib/interfaces/biometric.interface.dart b/mobile/lib/interfaces/biometric.interface.dart new file mode 100644 index 0000000000..e410c8e26e --- /dev/null +++ b/mobile/lib/interfaces/biometric.interface.dart @@ -0,0 +1,6 @@ +import 'package:immich_mobile/models/auth/biometric_status.model.dart'; + +abstract interface class IBiometricRepository { + Future getStatus(); + Future authenticate(String? message); +} diff --git a/mobile/lib/interfaces/local_files_manager.interface.dart b/mobile/lib/interfaces/local_files_manager.interface.dart index c8b83a7c93..07274b7e29 100644 --- a/mobile/lib/interfaces/local_files_manager.interface.dart +++ b/mobile/lib/interfaces/local_files_manager.interface.dart @@ -1,5 +1,5 @@ abstract interface class ILocalFilesManager { - Future moveToTrash(String fileName); - Future restoreFromTrash(String fileName); - Future requestManageStoragePermission(); + Future moveToTrash(List mediaUrls); + Future restoreFromTrash(String fileName, int type); + Future requestManageMediaPermission(); } diff --git a/mobile/lib/interfaces/secure_storage.interface.dart b/mobile/lib/interfaces/secure_storage.interface.dart new file mode 100644 index 0000000000..81230e0abd --- /dev/null +++ b/mobile/lib/interfaces/secure_storage.interface.dart @@ -0,0 +1,5 @@ +abstract interface class ISecureStorageRepository { + Future read(String key); + Future write(String key, String value); + Future delete(String key); +} diff --git a/mobile/lib/interfaces/timeline.interface.dart b/mobile/lib/interfaces/timeline.interface.dart index bc486a785f..3a4cce3cb6 100644 --- a/mobile/lib/interfaces/timeline.interface.dart +++ b/mobile/lib/interfaces/timeline.interface.dart @@ -31,4 +31,9 @@ abstract class ITimelineRepository { ); Stream watchAssetSelectionTimeline(String userId); + + Stream watchLockedTimeline( + String userId, + GroupAssetsBy groupAssetsBy, + ); } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 1a434aa359..3c7c1fbe4d 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -11,6 +11,7 @@ import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/generated/codegen_loader.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; @@ -18,7 +19,7 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/routing/tab_navigation_observer.dart'; +import 'package:immich_mobile/routing/app_navigation_observer.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; @@ -26,20 +27,22 @@ import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; import 'package:immich_mobile/utils/download.dart'; -import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:logging/logging.dart'; import 'package:timezone/data/latest.dart'; -import 'package:immich_mobile/generated/codegen_loader.g.dart'; +import 'package:worker_manager/worker_manager.dart'; void main() async { ImmichWidgetsBinding(); final db = await Bootstrap.initIsar(); await Bootstrap.initDomain(db); await initApp(); + // Warm-up isolate pool for worker manager + await workerManager.init(dynamicSpawning: true); await migrateDatabaseIfNeeded(db); - HttpOverrides.global = HttpSSLCertOverride(); + HttpSSLOptions.apply(); runApp( ProviderScope( @@ -216,7 +219,7 @@ class ImmichAppState extends ConsumerState ), routeInformationParser: router.defaultRouteParser(), routerDelegate: router.delegate( - navigatorObservers: () => [TabNavigationObserver(ref: ref)], + navigatorObservers: () => [AppNavigationObserver(ref: ref)], ), ), ), diff --git a/mobile/lib/models/auth/biometric_status.model.dart b/mobile/lib/models/auth/biometric_status.model.dart new file mode 100644 index 0000000000..3057f06e9c --- /dev/null +++ b/mobile/lib/models/auth/biometric_status.model.dart @@ -0,0 +1,38 @@ +import 'package:collection/collection.dart'; +import 'package:local_auth/local_auth.dart'; + +class BiometricStatus { + final List availableBiometrics; + final bool canAuthenticate; + + const BiometricStatus({ + required this.availableBiometrics, + required this.canAuthenticate, + }); + + @override + String toString() => + 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; + + BiometricStatus copyWith({ + List? availableBiometrics, + bool? canAuthenticate, + }) { + return BiometricStatus( + availableBiometrics: availableBiometrics ?? this.availableBiometrics, + canAuthenticate: canAuthenticate ?? this.canAuthenticate, + ); + } + + @override + bool operator ==(covariant BiometricStatus other) { + if (identical(this, other)) return true; + final listEquals = const DeepCollectionEquality().equals; + + return listEquals(other.availableBiometrics, availableBiometrics) && + other.canAuthenticate == canAuthenticate; + } + + @override + int get hashCode => availableBiometrics.hashCode ^ canAuthenticate.hashCode; +} diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index 617fbfee11..7b0ce8cdc4 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -3,13 +3,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart'; import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { @@ -59,7 +59,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { : const Text( 'share_assets_selected', style: TextStyle(fontSize: 18), - ).tr(args: [selected.value.length.toString()]), + ).tr(namedArgs: {'count': selected.value.length.toString()}), centerTitle: false, actions: [ if (selected.value.isNotEmpty || canDeselect) diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index 1269fe0a8e..8cc09a1ded 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -229,11 +229,13 @@ class AlbumsPage extends HookConsumerWidget { ), subtitle: sorted[index].ownerId != null ? Text( - '${(sorted[index].assetCount == 1 ? 'album_thumbnail_card_item'.tr( - args: ['${sorted[index].assetCount}'], - ) : 'album_thumbnail_card_items'.tr( - args: ['${sorted[index].assetCount}'], - ))} â€ĸ ${sorted[index].ownerId != userId ? 'album_thumbnail_shared_by'.tr(args: [sorted[index].ownerName!]) : 'owned'.tr()}', + '${(sorted[index].assetCount == 1 ? 'album_thumbnail_card_item'.tr() : 'album_thumbnail_card_items'.tr( + namedArgs: { + 'count': sorted[index] + .assetCount + .toString(), + }, + ))} â€ĸ ${sorted[index].ownerId != userId ? 'album_thumbnail_shared_by'.tr(namedArgs: {'user': sorted[index].ownerName!}) : 'owned'.tr()}', overflow: TextOverflow.ellipsis, style: context.textTheme.bodyMedium?.copyWith( @@ -267,6 +269,7 @@ class AlbumsPage extends HookConsumerWidget { ], ), ), + resizeToAvoidBottomInset: false, ); } } diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index 671a9bfe16..c4124efb52 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -214,13 +214,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ListTile( title: Text( "backup_album_selection_page_albums_device".tr( - args: [ - ref + namedArgs: { + 'count': ref .watch(backupProvider) .availableAlbums .length .toString(), - ], + }, ), style: context.textTheme.titleSmall, ), diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 7392a4d340..420b699730 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -63,9 +63,12 @@ class GalleryViewerPage extends HookConsumerWidget { final loadAsset = renderList.loadAsset; final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider); - // This key is to prevent the video player from being re-initialized during - // hero animation or device rotation. - final videoPlayerKey = useMemoized(() => GlobalKey()); + final videoPlayerKeys = useRef>({}); + + GlobalKey getVideoPlayerKey(int id) { + videoPlayerKeys.value.putIfAbsent(id, () => GlobalKey()); + return videoPlayerKeys.value[id]!; + } Future precacheNextImage(int index) async { if (!context.mounted) { @@ -243,7 +246,7 @@ class GalleryViewerPage extends HookConsumerWidget { width: context.width, height: context.height, child: NativeVideoViewerPage( - key: videoPlayerKey, + key: getVideoPlayerKey(asset.id), asset: asset, image: Image( key: ValueKey(asset), diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index 805f15a845..0188d953dc 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -167,6 +167,7 @@ class TabControllerPage extends HookConsumerWidget { onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, child: Scaffold( + resizeToAvoidBottomInset: false, body: isScreenLandscape ? Row( children: [ diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index bfa60eae00..39524df024 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -1,18 +1,18 @@ -import 'dart:typed_data'; import 'dart:async'; +import 'dart:typed_data'; import 'dart:ui'; +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/repositories/file_media.repository.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; -import 'package:auto_route/auto_route.dart'; -import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:easy_localization/easy_localization.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:path/path.dart' as p; /// A stateless widget that provides functionality for editing an image. @@ -81,7 +81,7 @@ class EditImagePage extends ConsumerWidget { ImmichToast.show( durationInSecond: 6, context: context, - msg: "error_saving_image".tr(args: [e.toString()]), + msg: "error_saving_image".tr(namedArgs: {'error': e.toString()}), gravity: ToastGravity.CENTER, ); } diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index a13adc21f2..2b4aa64f3b 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -24,7 +24,7 @@ class ArchivePage extends HookConsumerWidget { automaticallyImplyLeading: false, title: const Text( 'archive_page_title', - ).tr(args: [count]), + ).tr(namedArgs: {'count': count}), ); } diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index 1852fb7877..50126ed1a8 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -140,6 +140,19 @@ class QuickAccessButtons extends ConsumerWidget { ), onTap: () => context.pushRoute(FolderRoute()), ), + ListTile( + leading: const Icon( + Icons.lock_outline_rounded, + size: 26, + ), + title: Text( + 'locked_folder'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + onTap: () => context.pushRoute(const LockedRoute()), + ), ListTile( leading: const Icon( Icons.group_outlined, @@ -297,32 +310,34 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + SizedBox( height: size, width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withAlpha(30), + context.colorScheme.primary.withAlpha(25), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: GridView.count( + crossAxisCount: 2, + padding: const EdgeInsets.all(12), + crossAxisSpacing: 8, + mainAxisSpacing: 8, + physics: const NeverScrollableScrollPhysics(), + children: albums.take(4).map((album) { + return AlbumThumbnailCard( + album: album, + showTitle: false, + ); + }).toList(), ), - ), - child: GridView.count( - crossAxisCount: 2, - padding: const EdgeInsets.all(12), - crossAxisSpacing: 8, - mainAxisSpacing: 8, - physics: const NeverScrollableScrollPhysics(), - children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); - }).toList(), ), ), Padding( @@ -354,27 +369,35 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PlacesCollectionRoute()), + onTap: () => context.pushRoute( + PlacesCollectionRoute( + currentLocation: null, + ), + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + SizedBox( height: size, width: size, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: context.colorScheme.secondaryContainer.withAlpha(100), - ), - child: IgnorePointer( - child: MapThumbnail( - zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + color: + context.colorScheme.secondaryContainer.withAlpha(100), + ), + child: IgnorePointer( + child: MapThumbnail( + zoom: 8, + centre: const LatLng( + 21.44950, + -157.91959, + ), + showAttribution: false, + themeMode: context.isDarkTheme + ? ThemeMode.dark + : ThemeMode.light, ), - showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 164ea3bad8..5ce6d453ae 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -2,7 +2,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/utils/translation.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -43,7 +45,12 @@ class LocalAlbumsPage extends HookConsumerWidget { fontWeight: FontWeight.w600, ), ), - subtitle: Text('${albums[index].assetCount} items'), + subtitle: Text( + t('items_count', {'count': albums[index].assetCount}), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), onTap: () => context .pushRoute(AlbumViewerRoute(albumId: albums[index].id)), ), diff --git a/mobile/lib/pages/library/locked/locked.page.dart b/mobile/lib/pages/library/locked/locked.page.dart new file mode 100644 index 0000000000..eef12a7107 --- /dev/null +++ b/mobile/lib/pages/library/locked/locked.page.dart @@ -0,0 +1,95 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/multiselect.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; +import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart'; + +@RoutePage() +class LockedPage extends HookConsumerWidget { + const LockedPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final appLifeCycle = useAppLifecycleState(); + final showOverlay = useState(false); + final authProviderNotifier = ref.read(authProvider.notifier); + // lock the page when it is destroyed + useEffect( + () { + return () { + authProviderNotifier.lockPinCode(); + }; + }, + [], + ); + + useEffect( + () { + if (context.mounted) { + if (appLifeCycle == AppLifecycleState.resumed) { + showOverlay.value = false; + } else { + showOverlay.value = true; + } + } + + return null; + }, + [appLifeCycle], + ); + + return Scaffold( + appBar: ref.watch(multiselectProvider) ? null : const LockPageAppBar(), + body: showOverlay.value + ? const SizedBox() + : MultiselectGrid( + renderListProvider: lockedTimelineProvider, + topWidget: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + 'no_locked_photos_message'.tr(), + style: context.textTheme.labelLarge, + ), + ), + ), + editEnabled: false, + favoriteEnabled: false, + unfavorite: false, + archiveEnabled: false, + stackEnabled: false, + unarchive: false, + ), + ); + } +} + +class LockPageAppBar extends ConsumerWidget implements PreferredSizeWidget { + const LockPageAppBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AppBar( + leading: IconButton( + onPressed: () { + ref.read(authProvider.notifier).lockPinCode(); + context.maybePop(); + }, + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + centerTitle: true, + automaticallyImplyLeading: false, + title: const Text( + 'locked_folder', + ).tr(), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart new file mode 100644 index 0000000000..cca0e3b7ac --- /dev/null +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -0,0 +1,127 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/local_auth.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/forms/pin_registration_form.dart'; +import 'package:immich_mobile/widgets/forms/pin_verification_form.dart'; + +@RoutePage() +class PinAuthPage extends HookConsumerWidget { + final bool createPinCode; + + const PinAuthPage({super.key, this.createPinCode = false}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final localAuthState = ref.watch(localAuthProvider); + final showPinRegistrationForm = useState(createPinCode); + + Future registerBiometric(String pinCode) async { + final isRegistered = + await ref.read(localAuthProvider.notifier).registerBiometric( + context, + pinCode, + ); + + if (isRegistered) { + context.showSnackBar( + SnackBar( + content: Text( + 'biometric_auth_enabled'.tr(), + style: context.textTheme.labelLarge, + ), + duration: const Duration(seconds: 3), + backgroundColor: context.colorScheme.primaryContainer, + ), + ); + + context.replaceRoute(const LockedRoute()); + } + } + + enableBiometricAuth() { + showDialog( + context: context, + builder: (buildContext) { + return SimpleDialog( + children: [ + Container( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + PinVerificationForm( + description: 'enable_biometric_auth_description'.tr(), + onSuccess: (pinCode) { + Navigator.pop(buildContext); + registerBiometric(pinCode); + }, + autoFocus: true, + icon: Icons.fingerprint_rounded, + successIcon: Icons.fingerprint_rounded, + ), + ], + ), + ), + ], + ); + }, + ); + } + + return Scaffold( + appBar: AppBar( + title: Text('locked_folder'.tr()), + ), + body: ListView( + shrinkWrap: true, + children: [ + Padding( + padding: const EdgeInsets.only(top: 36.0), + child: showPinRegistrationForm.value + ? Center( + child: PinRegistrationForm( + onDone: () => showPinRegistrationForm.value = false, + ), + ) + : Column( + children: [ + Center( + child: PinVerificationForm( + autoFocus: true, + onSuccess: (_) => + context.replaceRoute(const LockedRoute()), + ), + ), + const SizedBox(height: 24), + if (localAuthState.canAuthenticate) ...[ + Padding( + padding: const EdgeInsets.only(right: 16.0), + child: TextButton.icon( + icon: const Icon( + Icons.fingerprint, + size: 28, + ), + onPressed: enableBiometricAuth, + label: Text( + 'use_biometric'.tr(), + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + fontSize: 18, + ), + ), + ), + ), + ], + ], + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index 90a6a7f04e..91b661e7ce 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -73,7 +73,8 @@ class PartnerPage extends HookConsumerWidget { builder: (BuildContext context) { return ConfirmDialog( title: "stop_photo_sharing", - content: "partner_page_stop_sharing_content".tr(args: [u.name]), + content: "partner_page_stop_sharing_content" + .tr(namedArgs: {'partner': u.name}), onOk: () => ref.read(partnerServiceProvider).removePartner(u), ); }, diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index 5f587c0c76..27daf0a887 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/search/people.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; import 'package:immich_mobile/widgets/search/person_name_edit_form.dart'; @RoutePage() @@ -42,47 +42,12 @@ class PeopleCollectionPage extends HookConsumerWidget { appBar: AppBar( automaticallyImplyLeading: search.value == null, title: search.value != null - ? TextField( + ? SearchField( focusNode: formFocus, onTapOutside: (_) => formFocus.unfocus(), onChanged: (value) => search.value = value, - decoration: InputDecoration( - contentPadding: const EdgeInsets.only(left: 24), - filled: true, - fillColor: context.primaryColor.withValues(alpha: 0.1), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurfaceSecondary, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), - borderSide: BorderSide( - color: context.colorScheme.primary.withAlpha(150), - ), - ), - prefixIcon: Icon( - Icons.search_rounded, - color: context.colorScheme.primary, - ), - hintText: 'filter_people'.tr(), - ), + filled: true, + hintText: 'filter_people'.tr(), autofocus: true, ) : Text('people'.tr()), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index d4da3ff37e..5f2dea0dec 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -12,43 +13,75 @@ import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/providers/search/search_page_state.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; @RoutePage() class PlacesCollectionPage extends HookConsumerWidget { - const PlacesCollectionPage({super.key}); + const PlacesCollectionPage({super.key, this.currentLocation}); + final LatLng? currentLocation; @override Widget build(BuildContext context, WidgetRef ref) { final places = ref.watch(getAllPlacesProvider); + final formFocus = useFocusNode(); + final ValueNotifier search = useState(null); return Scaffold( appBar: AppBar( - title: Text('places'.tr()), + automaticallyImplyLeading: search.value == null, + title: search.value != null + ? SearchField( + autofocus: true, + filled: true, + focusNode: formFocus, + onChanged: (value) => search.value = value, + onTapOutside: (_) => formFocus.unfocus(), + hintText: 'filter_places'.tr(), + ) + : Text('places'.tr()), + actions: [ + IconButton( + icon: Icon(search.value != null ? Icons.close : Icons.search), + onPressed: () { + search.value = search.value == null ? '' : null; + }, + ), + ], ), body: ListView( shrinkWrap: true, children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - height: 200, - width: context.width, - child: MapThumbnail( - onTap: (_, __) => context.pushRoute(const MapRoute()), - zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, + if (search.value == null) + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + height: 200, + width: context.width, + child: MapThumbnail( + onTap: (_, __) => context + .pushRoute(MapRoute(initialLocation: currentLocation)), + zoom: 8, + centre: currentLocation ?? + const LatLng( + 21.44950, + -157.91959, + ), + showAttribution: false, + themeMode: + context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), - showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), - ), places.when( data: (places) { + if (search.value != null) { + places = places.where((place) { + return place.label + .toLowerCase() + .contains(search.value!.toLowerCase()); + }).toList(); + } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index e4bf1ebe9b..6c18841089 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -7,11 +7,11 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/shared_link.provider.dart'; import 'package:immich_mobile/services/shared_link.service.dart'; -import 'package:immich_mobile/providers/server_info.provider.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/utils/url_helper.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; @RoutePage() class SharedLinkEditPage extends HookConsumerWidget { @@ -241,8 +241,8 @@ class SharedLinkEditPage extends HookConsumerWidget { ), DropdownMenuEntry( value: 30, - label: - "shared_link_edit_expire_after_option_minutes".tr(args: ["30"]), + label: "shared_link_edit_expire_after_option_minutes" + .tr(namedArgs: {'count': "30"}), ), DropdownMenuEntry( value: 60, @@ -250,7 +250,8 @@ class SharedLinkEditPage extends HookConsumerWidget { ), DropdownMenuEntry( value: 60 * 6, - label: "shared_link_edit_expire_after_option_hours".tr(args: ["6"]), + label: "shared_link_edit_expire_after_option_hours" + .tr(namedArgs: {'count': "6"}), ), DropdownMenuEntry( value: 60 * 24, @@ -258,20 +259,23 @@ class SharedLinkEditPage extends HookConsumerWidget { ), DropdownMenuEntry( value: 60 * 24 * 7, - label: "shared_link_edit_expire_after_option_days".tr(args: ["7"]), + label: "shared_link_edit_expire_after_option_days" + .tr(namedArgs: {'count': "7"}), ), DropdownMenuEntry( value: 60 * 24 * 30, - label: "shared_link_edit_expire_after_option_days".tr(args: ["30"]), + label: "shared_link_edit_expire_after_option_days" + .tr(namedArgs: {'count': "30"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 3, - label: - "shared_link_edit_expire_after_option_months".tr(args: ["3"]), + label: "shared_link_edit_expire_after_option_months" + .tr(namedArgs: {'count': "3"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 12, - label: "shared_link_edit_expire_after_option_year".tr(args: ["1"]), + label: "shared_link_edit_expire_after_option_year" + .tr(namedArgs: {'count': "1"}), ), ], ); diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index f8a1d7605d..c645719974 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -4,18 +4,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; -import 'package:immich_mobile/providers/timeline.provider.dart'; -import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; -import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; -import 'package:immich_mobile/providers/trash.provider.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; +import 'package:immich_mobile/providers/trash.provider.dart'; +import 'package:immich_mobile/utils/immich_loading_overlay.dart'; +import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; +import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -import 'package:immich_mobile/utils/immich_loading_overlay.dart'; @RoutePage() class TrashPage extends HookConsumerWidget { @@ -77,7 +77,7 @@ class TrashPage extends HookConsumerWidget { ImmichToast.show( context: context, msg: 'assets_deleted_permanently' - .tr(args: ["${selection.value.length}"]), + .tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -118,7 +118,7 @@ class TrashPage extends HookConsumerWidget { ImmichToast.show( context: context, msg: 'assets_restored_successfully' - .tr(args: ["${selection.value.length}"]), + .tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -135,7 +135,7 @@ class TrashPage extends HookConsumerWidget { ? "${selection.value.length}" : "trash_page_select_assets_btn".tr(); } - return 'trash_page_title'.tr(args: [count]); + return 'trash_page_title'.tr(namedArgs: {'count': count}); } AppBar buildAppBar(String count) { @@ -260,7 +260,7 @@ class TrashPage extends HookConsumerWidget { ), child: const Text( "trash_page_info", - ).tr(args: ["$trashDays"]), + ).tr(namedArgs: {"days": "$trashDays"}), ), ), ), diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index a6768cc207..b0a1b34b06 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -44,7 +44,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { } }), child: const Text( - 'grant_permission', + 'continue', ).tr(), ), ], diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 0e64759241..b80b96f94f 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -34,7 +34,8 @@ import 'package:maplibre_gl/maplibre_gl.dart'; @RoutePage() class MapPage extends HookConsumerWidget { - const MapPage({super.key}); + const MapPage({super.key, this.initialLocation}); + final LatLng? initialLocation; @override Widget build(BuildContext context, WidgetRef ref) { @@ -235,7 +236,8 @@ class MapPage extends HookConsumerWidget { } void onZoomToLocation() async { - final (location, error) = await MapUtils.checkPermAndGetLocation(context); + final (location, error) = + await MapUtils.checkPermAndGetLocation(context: context); if (error != null) { if (error == LocationPermission.unableToDetermine && context.mounted) { ImmichToast.show( @@ -272,6 +274,7 @@ class MapPage extends HookConsumerWidget { body: Stack( children: [ _MapWithMarker( + initialLocation: initialLocation, style: style, selectedMarker: selectedMarker, onMapCreated: onMapCreated, @@ -303,6 +306,7 @@ class MapPage extends HookConsumerWidget { body: Stack( children: [ _MapWithMarker( + initialLocation: initialLocation, style: style, selectedMarker: selectedMarker, onMapCreated: onMapCreated, @@ -368,6 +372,7 @@ class _MapWithMarker extends StatelessWidget { final OnStyleLoadedCallback onStyleLoaded; final Function()? onMarkerTapped; final ValueNotifier<_AssetMarkerMeta?> selectedMarker; + final LatLng? initialLocation; const _MapWithMarker({ required this.style, @@ -377,6 +382,7 @@ class _MapWithMarker extends StatelessWidget { required this.onStyleLoaded, required this.selectedMarker, this.onMarkerTapped, + this.initialLocation, }); @override @@ -389,8 +395,10 @@ class _MapWithMarker extends StatelessWidget { children: [ style.widgetWhen( onData: (style) => MapLibreMap( - initialCameraPosition: - const CameraPosition(target: LatLng(0, 0)), + initialCameraPosition: CameraPosition( + target: initialLocation ?? const LatLng(0, 0), + zoom: initialLocation != null ? 12 : 0, + ), styleString: style, // This is needed to update the selectedMarker's position on map camera updates // The changes are notified through the mapController ValueListener which is added in [onMapCreated] diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index 9d526d8080..f27deae052 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -46,7 +46,7 @@ class MapLocationPickerPage extends HookConsumerWidget { Future getCurrentLocation() async { var (currentLocation, _) = - await MapUtils.checkPermAndGetLocation(context); + await MapUtils.checkPermAndGetLocation(context: context); if (currentLocation == null) { return; diff --git a/mobile/lib/pages/search/recently_added.page.dart b/mobile/lib/pages/search/recently_taken.page.dart similarity index 74% rename from mobile/lib/pages/search/recently_added.page.dart rename to mobile/lib/pages/search/recently_taken.page.dart index b79527e222..cc1eb7086e 100644 --- a/mobile/lib/pages/search/recently_added.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -4,19 +4,19 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; -import 'package:immich_mobile/providers/search/recently_added_asset.provider.dart'; +import 'package:immich_mobile/providers/search/recently_taken_asset.provider.dart'; @RoutePage() -class RecentlyAddedPage extends HookConsumerWidget { - const RecentlyAddedPage({super.key}); +class RecentlyTakenPage extends HookConsumerWidget { + const RecentlyTakenPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final recents = ref.watch(recentlyAddedAssetProvider); + final recents = ref.watch(recentlyTakenAssetProvider); return Scaffold( appBar: AppBar( - title: const Text('recently_added_page_title').tr(), + title: const Text('recently_taken_page_title').tr(), leading: IconButton( onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded), diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 62a62d6c98..017ced9a19 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -523,7 +523,7 @@ class SearchPage extends HookConsumerWidget { } return Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: false, appBar: AppBar( automaticallyImplyLeading: true, actions: [ @@ -843,10 +843,10 @@ class QuickLinkList extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), children: [ QuickLink( - title: 'recently_added'.tr(), + title: 'recently_taken'.tr(), icon: Icons.schedule_outlined, isTop: true, - onTap: () => context.pushRoute(const RecentlyAddedRoute()), + onTap: () => context.pushRoute(const RecentlyTakenRoute()), ), QuickLink( title: 'videos'.tr(), diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index d18fd15b6d..ff137ce0aa 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -3,12 +3,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; +import 'package:immich_mobile/utils/url_helper.dart'; @RoutePage() class ShareIntentPage extends HookConsumerWidget { @@ -18,7 +17,7 @@ class ShareIntentPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final currentEndpoint = Store.get(StoreKey.serverEndpoint); + final currentEndpoint = getServerUrl() ?? '--'; final candidates = ref.watch(shareIntentUploadProvider); final isUploaded = useState(false); @@ -57,9 +56,7 @@ class ShareIntentPage extends HookConsumerWidget { title: Column( children: [ const Text('upload_to_immich').tr( - args: [ - candidates.length.toString(), - ], + namedArgs: {'count': candidates.length.toString()}, ), Text( currentEndpoint, @@ -177,8 +174,12 @@ class UploadingText extends StatelessWidget { return element.status == UploadStatus.complete; }).length; - return const Text("shared_intent_upload_button_progress_text") - .tr(args: [uploadedCount.toString(), candidates.length.toString()]); + return const Text("shared_intent_upload_button_progress_text").tr( + namedArgs: { + 'current': uploadedCount.toString(), + 'total': candidates.length.toString(), + }, + ); } } diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index a35ab10bf3..5b77da90f3 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -170,6 +171,13 @@ class AssetNotifier extends StateNotifier { status ??= !assets.every((a) => a.isArchived); return _assetService.changeArchiveStatus(assets, status); } + + Future setLockedView( + List selection, + AssetVisibilityEnum visibility, + ) { + return _assetService.setVisibility(selection, visibility); + } } final assetDetailProvider = diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 3221b80526..5207858f99 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_udid/flutter_udid.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; @@ -11,6 +12,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; +import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -20,6 +22,7 @@ final authProvider = StateNotifierProvider((ref) { ref.watch(authServiceProvider), ref.watch(apiServiceProvider), ref.watch(userServiceProvider), + ref.watch(secureStorageServiceProvider), ); }); @@ -27,12 +30,17 @@ class AuthNotifier extends StateNotifier { final AuthService _authService; final ApiService _apiService; final UserService _userService; + final SecureStorageService _secureStorageService; final _log = Logger("AuthenticationNotifier"); static const Duration _timeoutDuration = Duration(seconds: 7); - AuthNotifier(this._authService, this._apiService, this._userService) - : super( + AuthNotifier( + this._authService, + this._apiService, + this._userService, + this._secureStorageService, + ) : super( AuthState( deviceId: "", userId: "", @@ -67,6 +75,7 @@ class AuthNotifier extends StateNotifier { Future logout() async { try { + await _secureStorageService.delete(kSecuredPinCode); await _authService.logout(); } finally { await _cleanUp(); @@ -185,12 +194,19 @@ class AuthNotifier extends StateNotifier { return Store.tryGet(StoreKey.serverEndpoint); } - /// Returns the current server URL (input by the user) from the store - String? getServerUrl() { - return Store.tryGet(StoreKey.serverUrl); - } - Future setOpenApiServiceEndpoint() { return _authService.setOpenApiServiceEndpoint(); } + + Future unlockPinCode(String pinCode) { + return _authService.unlockPinCode(pinCode); + } + + Future lockPinCode() { + return _authService.lockPinCode(); + } + + Future setupPinCode(String pinCode) { + return _authService.setupPinCode(pinCode); + } } diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart new file mode 100644 index 0000000000..83d103bb3b --- /dev/null +++ b/mobile/lib/providers/background_sync.provider.dart @@ -0,0 +1,8 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/utils/background_sync.dart'; + +final backgroundSyncProvider = Provider((ref) { + final manager = BackgroundSyncManager(); + ref.onDispose(manager.cancel); + return manager; +}); diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index fc7e4d866c..646a03cebc 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -6,27 +6,27 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; -import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; -import 'package:immich_mobile/repositories/file_media.repository.dart'; -import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/manual_upload_state.model.dart'; +import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; +import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; -import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; +import 'package:immich_mobile/services/background.service.dart'; +import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/backup_album.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; @@ -170,7 +170,7 @@ class ManualUploadNotifier extends StateNotifier { if (state.showDetailedNotification) { final title = "backup_background_service_current_upload_notification" - .tr(args: [state.currentUploadAsset.fileName]); + .tr(namedArgs: {'filename': state.currentUploadAsset.fileName}); _throttledDetailNotify(title: title, progress: sent, total: total); } } @@ -186,7 +186,7 @@ class ManualUploadNotifier extends StateNotifier { if (state.showDetailedNotification) { _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(args: [currentUploadAsset.fileName]); + .tr(namedArgs: {'filename': currentUploadAsset.fileName}); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 36fd3334b9..4c77ee4b56 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -53,50 +53,35 @@ class ImmichLocalImageProvider extends ImageProvider { ImageDecoderCallback decode, StreamController chunkEvents, ) async* { - ui.ImmutableBuffer? buffer; try { final local = asset.local; if (local == null) { throw StateError('Asset ${asset.fileName} has no local data'); } - var thumbBytes = await local - .thumbnailDataWithSize(const ThumbnailSize.square(256), quality: 80); - if (thumbBytes == null) { - throw StateError("Loading thumbnail for ${asset.fileName} failed"); - } - buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - thumbBytes = null; - yield await decode(buffer); - buffer = null; - switch (asset.type) { case AssetType.image: final File? file = await local.originFile; if (file == null) { throw StateError("Opening file for asset ${asset.fileName} failed"); } - buffer = await ui.ImmutableBuffer.fromFilePath(file.path); + final buffer = await ui.ImmutableBuffer.fromFilePath(file.path); yield await decode(buffer); - buffer = null; break; case AssetType.video: final size = ThumbnailSize(width.ceil(), height.ceil()); - thumbBytes = await local.thumbnailDataWithSize(size); + final thumbBytes = await local.thumbnailDataWithSize(size); if (thumbBytes == null) { throw StateError("Failed to load preview for ${asset.fileName}"); } - buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - thumbBytes = null; + final buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); yield await decode(buffer); - buffer = null; break; default: throw StateError('Unsupported asset type ${asset.type}'); } } catch (error, stack) { log.severe('Error loading local image ${asset.fileName}', error, stack); - buffer?.dispose(); } finally { chunkEvents.close(); } @@ -106,12 +91,11 @@ class ImmichLocalImageProvider extends ImageProvider { bool operator ==(Object other) { if (identical(this, other)) return true; if (other is ImmichLocalImageProvider) { - return asset == other.asset; + return asset.id == other.asset.id && asset.localId == other.asset.localId; } - return false; } @override - int get hashCode => asset.hashCode; + int get hashCode => Object.hash(asset.id, asset.localId); } diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index 69cdb105c0..edcf8a9458 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -2,11 +2,14 @@ import 'dart:async'; import 'dart:ui' as ui; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:photo_manager/photo_manager.dart' show ThumbnailSize; +import 'package:logging/logging.dart'; /// The local image provider for an asset /// Only viable @@ -15,11 +18,16 @@ class ImmichLocalThumbnailProvider final Asset asset; final int height; final int width; + final CacheManager? cacheManager; + final Logger log = Logger("ImmichLocalThumbnailProvider"); + final String? userId; ImmichLocalThumbnailProvider({ required this.asset, this.height = 256, this.width = 256, + this.cacheManager, + this.userId, }) : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key @@ -36,57 +44,62 @@ class ImmichLocalThumbnailProvider ImmichLocalThumbnailProvider key, ImageDecoderCallback decode, ) { - final chunkEvents = StreamController(); + final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( - codec: _codec(key.asset, decode, chunkEvents), + codec: _codec(key.asset, cache, decode), scale: 1.0, - chunkEvents: chunkEvents.stream, informationCollector: () sync* { - yield ErrorDescription(asset.fileName); + yield ErrorDescription(key.asset.fileName); }, ); } // Streams in each stage of the image as we ask for it Stream _codec( - Asset key, + Asset assetData, + CacheManager cache, ImageDecoderCallback decode, - StreamController chunkEvents, ) async* { - // Load a small thumbnail - final thumbBytes = await asset.local?.thumbnailDataWithSize( - const ThumbnailSize.square(32), - quality: 75, - ); - if (thumbBytes != null) { - final buffer = await ui.ImmutableBuffer.fromUint8List(thumbBytes); - final codec = await decode(buffer); - yield codec; - } else { - debugPrint("Loading thumb for ${asset.fileName} failed"); + final cacheKey = + '$userId${assetData.localId}${assetData.checksum}$width$height'; + final fileFromCache = await cache.getFileFromCache(cacheKey); + if (fileFromCache != null) { + try { + final buffer = + await ui.ImmutableBuffer.fromFilePath(fileFromCache.file.path); + final codec = await decode(buffer); + yield codec; + return; + } catch (error) { + log.severe('Found thumbnail in cache, but loading it failed', error); + } } - final normalThumbBytes = - await asset.local?.thumbnailDataWithSize(ThumbnailSize(width, height)); - if (normalThumbBytes == null) { + final thumbnailBytes = await assetData.local?.thumbnailDataWithSize( + ThumbnailSize(width, height), + quality: 80, + ); + if (thumbnailBytes == null) { throw StateError( - "Loading thumb for local photo ${asset.fileName} failed", + "Loading thumb for local photo ${assetData.fileName} failed", ); } - final buffer = await ui.ImmutableBuffer.fromUint8List(normalThumbBytes); + + final buffer = await ui.ImmutableBuffer.fromUint8List(thumbnailBytes); final codec = await decode(buffer); yield codec; - - chunkEvents.close(); + await cache.putFile(cacheKey, thumbnailBytes); } @override bool operator ==(Object other) { - if (other is! ImmichLocalThumbnailProvider) return false; if (identical(this, other)) return true; - return asset == other.asset; + if (other is ImmichLocalThumbnailProvider) { + return asset.id == other.asset.id && asset.localId == other.asset.localId; + } + return false; } @override - int get hashCode => asset.hashCode; + int get hashCode => Object.hash(asset.id, asset.localId); } diff --git a/mobile/lib/providers/image/immich_remote_image_provider.dart b/mobile/lib/providers/image/immich_remote_image_provider.dart index 9e1d8aa120..d5189fa4fc 100644 --- a/mobile/lib/providers/image/immich_remote_image_provider.dart +++ b/mobile/lib/providers/image/immich_remote_image_provider.dart @@ -57,12 +57,6 @@ class ImmichRemoteImageProvider AppSettingsEnum.loadOriginal.defaultValue, ); - /// Whether to load the preview thumbnail first or not - bool get _loadPreview => Store.get( - AppSettingsEnum.loadPreview.storeKey, - AppSettingsEnum.loadPreview.defaultValue, - ); - // Streams in each stage of the image as we ask for it Stream _codec( ImmichRemoteImageProvider key, @@ -70,21 +64,6 @@ class ImmichRemoteImageProvider ImageDecoderCallback decode, StreamController chunkEvents, ) async* { - // Load a preview to the chunk events - if (_loadPreview) { - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); - - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); - } - // Load the higher resolution version of the image final url = getThumbnailUrlForRemoteId( key.assetId, diff --git a/mobile/lib/providers/infrastructure/cancel.provider.dart b/mobile/lib/providers/infrastructure/cancel.provider.dart new file mode 100644 index 0000000000..6851861e1a --- /dev/null +++ b/mobile/lib/providers/infrastructure/cancel.provider.dart @@ -0,0 +1,12 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +/// Provider holding a boolean function that returns true when cancellation is requested. +/// A computation running in the isolate uses the function to implement cooperative cancellation. +final cancellationProvider = Provider( + // This will be overridden in the isolate's container. + // Throwing ensures it's not used without an override. + (ref) => throw UnimplementedError( + "cancellationProvider must be overridden in the isolate's ProviderContainer and not to be used in the root isolate", + ), + name: 'cancellationProvider', +); diff --git a/mobile/lib/providers/infrastructure/db.provider.dart b/mobile/lib/providers/infrastructure/db.provider.dart index 84010b3b96..4eefbc556c 100644 --- a/mobile/lib/providers/infrastructure/db.provider.dart +++ b/mobile/lib/providers/infrastructure/db.provider.dart @@ -1,4 +1,7 @@ +import 'dart:async'; + import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -6,3 +9,9 @@ part 'db.provider.g.dart'; @Riverpod(keepAlive: true) Isar isar(Ref ref) => throw UnimplementedError('isar'); + +final driftProvider = Provider((ref) { + final drift = Drift(); + ref.onDispose(() => unawaited(drift.close())); + return drift; +}); diff --git a/mobile/lib/providers/infrastructure/sync_stream.provider.dart b/mobile/lib/providers/infrastructure/sync_stream.provider.dart index 64f1a6cb05..e313982a30 100644 --- a/mobile/lib/providers/infrastructure/sync_stream.provider.dart +++ b/mobile/lib/providers/infrastructure/sync_stream.provider.dart @@ -1,24 +1,23 @@ -import 'dart:async'; - import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/sync_stream.service.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; final syncStreamServiceProvider = Provider( - (ref) { - final instance = SyncStreamService( - ref.watch(syncApiRepositoryProvider), - ); - - ref.onDispose(() => unawaited(instance.dispose())); - - return instance; - }, + (ref) => SyncStreamService( + syncApiRepository: ref.watch(syncApiRepositoryProvider), + syncStreamRepository: ref.watch(syncStreamRepositoryProvider), + cancelChecker: ref.watch(cancellationProvider), + ), ); final syncApiRepositoryProvider = Provider( - (ref) => SyncApiRepository( - ref.watch(apiServiceProvider), - ), + (ref) => SyncApiRepository(ref.watch(apiServiceProvider)), +); + +final syncStreamRepositoryProvider = Provider( + (ref) => DriftSyncStreamRepository(ref.watch(driftProvider)), ); diff --git a/mobile/lib/providers/local_auth.provider.dart b/mobile/lib/providers/local_auth.provider.dart new file mode 100644 index 0000000000..6f7ca5eb71 --- /dev/null +++ b/mobile/lib/providers/local_auth.provider.dart @@ -0,0 +1,97 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/models/auth/biometric_status.model.dart'; +import 'package:immich_mobile/services/local_auth.service.dart'; +import 'package:immich_mobile/services/secure_storage.service.dart'; +import 'package:logging/logging.dart'; + +final localAuthProvider = + StateNotifierProvider((ref) { + return LocalAuthNotifier( + ref.watch(localAuthServiceProvider), + ref.watch(secureStorageServiceProvider), + ); +}); + +class LocalAuthNotifier extends StateNotifier { + final LocalAuthService _localAuthService; + final SecureStorageService _secureStorageService; + + final _log = Logger("LocalAuthNotifier"); + + LocalAuthNotifier(this._localAuthService, this._secureStorageService) + : super( + const BiometricStatus( + availableBiometrics: [], + canAuthenticate: false, + ), + ) { + _localAuthService.getStatus().then((value) { + state = state.copyWith( + canAuthenticate: value.canAuthenticate, + availableBiometrics: value.availableBiometrics, + ); + }); + } + + Future registerBiometric(BuildContext context, String pinCode) async { + final isAuthenticated = + await authenticate(context, 'Authenticate to enable biometrics'); + + if (!isAuthenticated) { + return false; + } + + await _secureStorageService.write(kSecuredPinCode, pinCode); + + return true; + } + + Future authenticate(BuildContext context, String? message) async { + String errorMessage = ""; + + try { + return await _localAuthService.authenticate(message); + } on PlatformException catch (error) { + switch (error.code) { + case "NotEnrolled": + _log.warning("User is not enrolled in biometrics"); + errorMessage = "biometric_no_options".tr(); + break; + case "NotAvailable": + _log.warning("Biometric authentication is not available"); + errorMessage = "biometric_not_available".tr(); + break; + case "LockedOut": + _log.warning("User is locked out of biometric authentication"); + errorMessage = "biometric_locked_out".tr(); + break; + default: + _log.warning("Failed to authenticate with unknown reason"); + errorMessage = 'failed_to_authenticate'.tr(); + } + } catch (error) { + _log.warning("Error during authentication: $error"); + errorMessage = 'failed_to_authenticate'.tr(); + } finally { + if (errorMessage.isNotEmpty) { + context.showSnackBar( + SnackBar( + content: Text( + errorMessage, + style: context.textTheme.labelLarge, + ), + duration: const Duration(seconds: 3), + backgroundColor: context.colorScheme.errorContainer, + ), + ); + } + } + + return false; + } +} diff --git a/mobile/lib/providers/map/map_service.provider.dart b/mobile/lib/providers/map/map_service.provider.dart index 0d998c5173..4ae199789f 100644 --- a/mobile/lib/providers/map/map_service.provider.dart +++ b/mobile/lib/providers/map/map_service.provider.dart @@ -6,4 +6,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'map_service.provider.g.dart'; @riverpod -MapSerivce mapService(Ref ref) => MapSerivce(ref.watch(apiServiceProvider)); +MapService mapService(Ref ref) => MapService(ref.watch(apiServiceProvider)); diff --git a/mobile/lib/providers/map/map_service.provider.g.dart b/mobile/lib/providers/map/map_service.provider.g.dart index 70e44da621..0bb5094c61 100644 --- a/mobile/lib/providers/map/map_service.provider.g.dart +++ b/mobile/lib/providers/map/map_service.provider.g.dart @@ -6,11 +6,11 @@ part of 'map_service.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$mapServiceHash() => r'7b26bcd231ed5728ac51fe015dddbf8f91491abb'; +String _$mapServiceHash() => r'ffc8f38b726083452b9df236ed58903879348987'; /// See also [mapService]. @ProviderFor(mapService) -final mapServiceProvider = AutoDisposeProvider.internal( +final mapServiceProvider = AutoDisposeProvider.internal( mapService, name: r'mapServiceProvider', debugGetCreateSourceHash: @@ -21,6 +21,6 @@ final mapServiceProvider = AutoDisposeProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef MapServiceRef = AutoDisposeProviderRef; +typedef MapServiceRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart new file mode 100644 index 0000000000..a5b903e312 --- /dev/null +++ b/mobile/lib/providers/routes.provider.dart @@ -0,0 +1,3 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +final inLockedViewProvider = StateProvider((ref) => false); diff --git a/mobile/lib/providers/search/recently_added_asset.provider.dart b/mobile/lib/providers/search/recently_taken_asset.provider.dart similarity index 68% rename from mobile/lib/providers/search/recently_added_asset.provider.dart rename to mobile/lib/providers/search/recently_taken_asset.provider.dart index c4819d9d44..157e7c2a74 100644 --- a/mobile/lib/providers/search/recently_added_asset.provider.dart +++ b/mobile/lib/providers/search/recently_taken_asset.provider.dart @@ -2,8 +2,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/services/asset.service.dart'; -final recentlyAddedAssetProvider = FutureProvider>((ref) async { +final recentlyTakenAssetProvider = FutureProvider>((ref) async { final assetService = ref.read(assetServiceProvider); - return assetService.getRecentlyAddedAssets(); + return assetService.getRecentlyTakenAssets(); }); diff --git a/mobile/lib/providers/secure_storage.provider.dart b/mobile/lib/providers/secure_storage.provider.dart new file mode 100644 index 0000000000..0194e527e9 --- /dev/null +++ b/mobile/lib/providers/secure_storage.provider.dart @@ -0,0 +1,10 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +final secureStorageProvider = + StateNotifierProvider((ref) { + return SecureStorageProvider(); +}); + +class SecureStorageProvider extends StateNotifier { + SecureStorageProvider() : super(null); +} diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index f857d8aa6c..b2c763cdfa 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -73,3 +73,8 @@ final assetsTimelineProvider = null, ); }); + +final lockedTimelineProvider = StreamProvider((ref) { + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchLockedTimelineProvider(); +}); diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index cda2b25e4d..c6f8539167 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -1,4 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; @@ -225,10 +226,12 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { } @override - Future> getRecentlyAddedAssets(String userId) { + Future> getRecentlyTakenAssets(String userId) { return db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) + .filter() + .visibilityEqualTo(AssetVisibilityEnum.timeline) .sortByFileCreatedAtDesc() .findAll(); } @@ -239,6 +242,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository { .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) .filter() + .visibilityEqualTo(AssetVisibilityEnum.timeline) .livePhotoVideoIdIsNotNull() .findAll(); } diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index f4fcd8a6dd..45442c2d61 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -1,4 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/interfaces/asset_api.interface.dart'; import 'package:immich_mobile/providers/api.provider.dart'; @@ -48,4 +49,27 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository { } return result; } + + @override + Future updateVisibility( + List ids, + AssetVisibilityEnum visibility, + ) async { + return _api.updateAssets( + AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)), + ); + } + + _mapVisibility(AssetVisibilityEnum visibility) { + switch (visibility) { + case AssetVisibilityEnum.timeline: + return AssetVisibility.timeline; + case AssetVisibilityEnum.hidden: + return AssetVisibility.hidden; + case AssetVisibilityEnum.locked: + return AssetVisibility.locked; + case AssetVisibilityEnum.archive: + return AssetVisibility.archive; + } + } } diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index f3a1d52de3..4015ffd7bc 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -55,4 +55,26 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository { userId: dto.userId, ); } + + @override + Future unlockPinCode(String pinCode) async { + try { + await _apiService.authenticationApi + .unlockAuthSession(SessionUnlockDto(pinCode: pinCode)); + return true; + } catch (_) { + return false; + } + } + + @override + Future setupPinCode(String pinCode) { + return _apiService.authenticationApi + .setupPinCode(PinCodeSetupDto(pinCode: pinCode)); + } + + @override + Future lockPinCode() { + return _apiService.authenticationApi.lockAuthSession(); + } } diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart new file mode 100644 index 0000000000..588fa44797 --- /dev/null +++ b/mobile/lib/repositories/biometric.repository.dart @@ -0,0 +1,35 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/interfaces/biometric.interface.dart'; +import 'package:immich_mobile/models/auth/biometric_status.model.dart'; +import 'package:local_auth/local_auth.dart'; + +final biometricRepositoryProvider = + Provider((ref) => BiometricRepository(LocalAuthentication())); + +class BiometricRepository implements IBiometricRepository { + final LocalAuthentication _localAuth; + + BiometricRepository(this._localAuth); + + @override + Future getStatus() async { + final bool canAuthenticateWithBiometrics = + await _localAuth.canCheckBiometrics; + final bool canAuthenticate = + canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); + final availableBiometric = await _localAuth.getAvailableBiometrics(); + + return BiometricStatus( + canAuthenticate: canAuthenticate, + availableBiometrics: availableBiometric, + ); + } + + @override + Future authenticate(String? message) async { + return _localAuth.authenticate( + localizedReason: message ?? 'please_auth_to_access'.tr(), + ); + } +} diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index 522d7e7a05..c2e234d14d 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -3,21 +3,23 @@ import 'package:immich_mobile/interfaces/local_files_manager.interface.dart'; import 'package:immich_mobile/utils/local_files_manager.dart'; final localFilesManagerRepositoryProvider = - Provider((ref) => LocalFilesManagerRepository()); + Provider((ref) => const LocalFilesManagerRepository()); class LocalFilesManagerRepository implements ILocalFilesManager { + const LocalFilesManagerRepository(); + @override - Future moveToTrash(String fileName) async { - return await LocalFilesManager.moveToTrash(fileName); + Future moveToTrash(List mediaUrls) async { + return await LocalFilesManager.moveToTrash(mediaUrls); } @override - Future restoreFromTrash(String fileName) async { - return await LocalFilesManager.restoreFromTrash(fileName); + Future restoreFromTrash(String fileName, int type) async { + return await LocalFilesManager.restoreFromTrash(fileName, type); } @override - Future requestManageStoragePermission() async { - return await LocalFilesManager.requestManageStoragePermission(); + Future requestManageMediaPermission() async { + return await LocalFilesManager.requestManageMediaPermission(); } } diff --git a/mobile/lib/repositories/secure_storage.repository.dart b/mobile/lib/repositories/secure_storage.repository.dart new file mode 100644 index 0000000000..fc641bcc91 --- /dev/null +++ b/mobile/lib/repositories/secure_storage.repository.dart @@ -0,0 +1,27 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/interfaces/secure_storage.interface.dart'; + +final secureStorageRepositoryProvider = + Provider((ref) => SecureStorageRepository(const FlutterSecureStorage())); + +class SecureStorageRepository implements ISecureStorageRepository { + final FlutterSecureStorage _secureStorage; + + SecureStorageRepository(this._secureStorage); + + @override + Future read(String key) { + return _secureStorage.read(key: key); + } + + @override + Future write(String key, String value) { + return _secureStorage.write(key: key, value: value); + } + + @override + Future delete(String key) { + return _secureStorage.delete(key: key); + } +} diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index 319ce3e5b4..039013f7d7 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -45,8 +45,8 @@ class TimelineRepository extends DatabaseRepository .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) .filter() - .isArchivedEqualTo(true) .isTrashedEqualTo(false) + .visibilityEqualTo(AssetVisibilityEnum.archive) .sortByFileCreatedAtDesc(); return _watchRenderList(query, GroupAssetsBy.none); @@ -59,6 +59,8 @@ class TimelineRepository extends DatabaseRepository .ownerIdEqualToAnyChecksum(fastHash(userId)) .filter() .isFavoriteEqualTo(true) + .not() + .visibilityEqualTo(AssetVisibilityEnum.locked) .isTrashedEqualTo(false) .sortByFileCreatedAtDesc(); @@ -70,7 +72,12 @@ class TimelineRepository extends DatabaseRepository Album album, GroupAssetsBy groupAssetByOption, ) { - final query = album.assets.filter().isTrashedEqualTo(false); + final query = album.assets + .filter() + .isTrashedEqualTo(false) + .not() + .visibilityEqualTo(AssetVisibilityEnum.locked); + final withSortedOption = switch (album.sortOrder) { SortOrder.asc => query.sortByFileCreatedAt(), SortOrder.desc => query.sortByFileCreatedAtDesc(), @@ -94,8 +101,8 @@ class TimelineRepository extends DatabaseRepository Stream watchAllVideosTimeline() { final query = db.assets .filter() - .isArchivedEqualTo(false) .isTrashedEqualTo(false) + .visibilityEqualTo(AssetVisibilityEnum.timeline) .typeEqualTo(AssetType.video) .sortByFileCreatedAtDesc(); @@ -111,9 +118,9 @@ class TimelineRepository extends DatabaseRepository .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) .filter() - .isArchivedEqualTo(false) .isTrashedEqualTo(false) .stackPrimaryAssetIdIsNull() + .visibilityEqualTo(AssetVisibilityEnum.timeline) .sortByFileCreatedAtDesc(); return _watchRenderList(query, groupAssetByOption); @@ -129,8 +136,8 @@ class TimelineRepository extends DatabaseRepository .where() .anyOf(isarUserIds, (qb, id) => qb.ownerIdEqualToAnyChecksum(id)) .filter() - .isArchivedEqualTo(false) .isTrashedEqualTo(false) + .visibilityEqualTo(AssetVisibilityEnum.timeline) .stackPrimaryAssetIdIsNull() .sortByFileCreatedAtDesc(); return _watchRenderList(query, groupAssetByOption); @@ -151,6 +158,7 @@ class TimelineRepository extends DatabaseRepository .remoteIdIsNotNull() .filter() .ownerIdEqualTo(fastHash(userId)) + .visibilityEqualTo(AssetVisibilityEnum.timeline) .isTrashedEqualTo(false) .stackPrimaryAssetIdIsNull() .sortByFileCreatedAtDesc(); @@ -158,6 +166,22 @@ class TimelineRepository extends DatabaseRepository return _watchRenderList(query, GroupAssetsBy.none); } + @override + Stream watchLockedTimeline( + String userId, + GroupAssetsBy getGroupByOption, + ) { + final query = db.assets + .where() + .ownerIdEqualToAnyChecksum(fastHash(userId)) + .filter() + .visibilityEqualTo(AssetVisibilityEnum.locked) + .isTrashedEqualTo(false) + .sortByFileCreatedAtDesc(); + + return _watchRenderList(query, getGroupByOption); + } + Stream _watchRenderList( QueryBuilder query, GroupAssetsBy groupAssetsBy, diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart new file mode 100644 index 0000000000..44662c0b8b --- /dev/null +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -0,0 +1,52 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +class AppNavigationObserver extends AutoRouterObserver { + /// Riverpod Instance + final WidgetRef ref; + + AppNavigationObserver({ + required this.ref, + }); + + @override + Future didChangeTabRoute( + TabPageRoute route, + TabPageRoute previousRoute, + ) async { + Future( + () => ref.read(inLockedViewProvider.notifier).state = false, + ); + } + + @override + void didPush(Route route, Route? previousRoute) { + _handleLockedViewState(route, previousRoute); + } + + _handleLockedViewState(Route route, Route? previousRoute) { + final isInLockedView = ref.read(inLockedViewProvider); + final isFromLockedViewToDetailView = + route.settings.name == GalleryViewerRoute.name && + previousRoute?.settings.name == LockedRoute.name; + + final isFromDetailViewToInfoPanelView = route.settings.name == null && + previousRoute?.settings.name == GalleryViewerRoute.name && + isInLockedView; + + if (route.settings.name == LockedRoute.name || + isFromLockedViewToDetailView || + isFromDetailViewToInfoPanelView) { + Future( + () => ref.read(inLockedViewProvider.notifier).state = true, + ); + } else { + Future( + () => ref.read(inLockedViewProvider.notifier).state = false, + ); + } + } +} diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart new file mode 100644 index 0000000000..d731c7942c --- /dev/null +++ b/mobile/lib/routing/locked_guard.dart @@ -0,0 +1,89 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/services.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/routing/router.dart'; + +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/local_auth.service.dart'; +import 'package:immich_mobile/services/secure_storage.service.dart'; +import 'package:local_auth/error_codes.dart' as auth_error; +import 'package:logging/logging.dart'; +// ignore: import_rule_openapi +import 'package:openapi/api.dart'; + +class LockedGuard extends AutoRouteGuard { + final ApiService _apiService; + final SecureStorageService _secureStorageService; + final LocalAuthService _localAuth; + final _log = Logger("AuthGuard"); + + LockedGuard( + this._apiService, + this._secureStorageService, + this._localAuth, + ); + + @override + void onNavigation(NavigationResolver resolver, StackRouter router) async { + final authStatus = await _apiService.authenticationApi.getAuthStatus(); + + if (authStatus == null) { + resolver.next(false); + return; + } + + /// Check if a pincode has been created but this user. Show the form to create if not exist + if (!authStatus.pinCode) { + router.push(PinAuthRoute(createPinCode: true)); + } + + if (authStatus.isElevated) { + resolver.next(true); + return; + } + + /// Check if the user has the pincode saved in secure storage, meaning + /// the user has enabled the biometric authentication + final securePinCode = await _secureStorageService.read(kSecuredPinCode); + if (securePinCode == null) { + router.push(PinAuthRoute()); + return; + } + + try { + final bool isAuth = await _localAuth.authenticate(); + + if (!isAuth) { + resolver.next(false); + return; + } + + await _apiService.authenticationApi.unlockAuthSession( + SessionUnlockDto(pinCode: securePinCode), + ); + + resolver.next(true); + } on PlatformException catch (error) { + switch (error.code) { + case auth_error.notAvailable: + _log.severe("notAvailable: $error"); + break; + case auth_error.notEnrolled: + _log.severe("not enrolled"); + break; + default: + _log.severe("error"); + break; + } + + resolver.next(false); + } on ApiException { + // PIN code has changed, need to re-enter to access + await _secureStorageService.delete(kSecuredPinCode); + router.push(PinAuthRoute()); + } catch (error) { + _log.severe("Failed to access locked page", error); + resolver.next(false); + } + } +} diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index d7edc6fd28..317ce7cc54 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -39,6 +39,8 @@ import 'package:immich_mobile/pages/library/favorite.page.dart'; import 'package:immich_mobile/pages/library/folder/folder.page.dart'; import 'package:immich_mobile/pages/library/library.page.dart'; import 'package:immich_mobile/pages/library/local_albums.page.dart'; +import 'package:immich_mobile/pages/library/locked/locked.page.dart'; +import 'package:immich_mobile/pages/library/locked/pin_auth.page.dart'; import 'package:immich_mobile/pages/library/partner/partner.page.dart'; import 'package:immich_mobile/pages/library/partner/partner_detail.page.dart'; import 'package:immich_mobile/pages/library/people/people_collection.page.dart'; @@ -58,7 +60,7 @@ import 'package:immich_mobile/pages/search/all_videos.page.dart'; import 'package:immich_mobile/pages/search/map/map.page.dart'; import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; -import 'package:immich_mobile/pages/search/recently_added.page.dart'; +import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/providers/api.provider.dart'; @@ -67,24 +69,41 @@ import 'package:immich_mobile/routing/auth_guard.dart'; import 'package:immich_mobile/routing/backup_permission_guard.dart'; import 'package:immich_mobile/routing/custom_transition_builders.dart'; import 'package:immich_mobile/routing/duplicate_guard.dart'; +import 'package:immich_mobile/routing/locked_guard.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/local_auth.service.dart'; +import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; part 'router.gr.dart'; +final appRouterProvider = Provider( + (ref) => AppRouter( + ref.watch(apiServiceProvider), + ref.watch(galleryPermissionNotifier.notifier), + ref.watch(secureStorageServiceProvider), + ref.watch(localAuthServiceProvider), + ), +); + @AutoRouterConfig(replaceInRouteName: 'Page,Route') class AppRouter extends RootStackRouter { late final AuthGuard _authGuard; late final DuplicateGuard _duplicateGuard; late final BackupPermissionGuard _backupPermissionGuard; + late final LockedGuard _lockedGuard; AppRouter( ApiService apiService, GalleryPermissionNotifier galleryPermissionNotifier, + SecureStorageService secureStorageService, + LocalAuthService localAuthService, ) { _authGuard = AuthGuard(apiService); _duplicateGuard = DuplicateGuard(); + _lockedGuard = + LockedGuard(apiService, secureStorageService, localAuthService); _backupPermissionGuard = BackupPermissionGuard(galleryPermissionNotifier); } @@ -160,7 +179,7 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], ), AutoRoute( - page: RecentlyAddedRoute.page, + page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard], ), CustomRoute( @@ -289,12 +308,13 @@ class AppRouter extends RootStackRouter { page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: LockedRoute.page, + guards: [_authGuard, _lockedGuard, _duplicateGuard], + ), + AutoRoute( + page: PinAuthRoute.page, + guards: [_authGuard, _duplicateGuard], + ), ]; } - -final appRouterProvider = Provider( - (ref) => AppRouter( - ref.watch(apiServiceProvider), - ref.watch(galleryPermissionNotifier.notifier), - ), -); diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index a78371e05e..da488779e6 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -956,6 +956,25 @@ class LocalAlbumsRoute extends PageRouteInfo { ); } +/// generated route for +/// [LockedPage] +class LockedRoute extends PageRouteInfo { + const LockedRoute({List? children}) + : super( + LockedRoute.name, + initialChildren: children, + ); + + static const String name = 'LockedRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const LockedPage(); + }, + ); +} + /// generated route for /// [LoginPage] class LoginRoute extends PageRouteInfo { @@ -1024,10 +1043,17 @@ class MapLocationPickerRouteArgs { /// generated route for /// [MapPage] -class MapRoute extends PageRouteInfo { - const MapRoute({List? children}) - : super( +class MapRoute extends PageRouteInfo { + MapRoute({ + Key? key, + LatLng? initialLocation, + List? children, + }) : super( MapRoute.name, + args: MapRouteArgs( + key: key, + initialLocation: initialLocation, + ), initialChildren: children, ); @@ -1036,11 +1062,32 @@ class MapRoute extends PageRouteInfo { static PageInfo page = PageInfo( name, builder: (data) { - return const MapPage(); + final args = + data.argsAs(orElse: () => const MapRouteArgs()); + return MapPage( + key: args.key, + initialLocation: args.initialLocation, + ); }, ); } +class MapRouteArgs { + const MapRouteArgs({ + this.key, + this.initialLocation, + }); + + final Key? key; + + final LatLng? initialLocation; + + @override + String toString() { + return 'MapRouteArgs{key: $key, initialLocation: $initialLocation}'; + } +} + /// generated route for /// [MemoryPage] class MemoryRoute extends PageRouteInfo { @@ -1331,12 +1378,66 @@ class PhotosRoute extends PageRouteInfo { ); } +/// generated route for +/// [PinAuthPage] +class PinAuthRoute extends PageRouteInfo { + PinAuthRoute({ + Key? key, + bool createPinCode = false, + List? children, + }) : super( + PinAuthRoute.name, + args: PinAuthRouteArgs( + key: key, + createPinCode: createPinCode, + ), + initialChildren: children, + ); + + static const String name = 'PinAuthRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = + data.argsAs(orElse: () => const PinAuthRouteArgs()); + return PinAuthPage( + key: args.key, + createPinCode: args.createPinCode, + ); + }, + ); +} + +class PinAuthRouteArgs { + const PinAuthRouteArgs({ + this.key, + this.createPinCode = false, + }); + + final Key? key; + + final bool createPinCode; + + @override + String toString() { + return 'PinAuthRouteArgs{key: $key, createPinCode: $createPinCode}'; + } +} + /// generated route for /// [PlacesCollectionPage] -class PlacesCollectionRoute extends PageRouteInfo { - const PlacesCollectionRoute({List? children}) - : super( +class PlacesCollectionRoute extends PageRouteInfo { + PlacesCollectionRoute({ + Key? key, + LatLng? currentLocation, + List? children, + }) : super( PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), initialChildren: children, ); @@ -1345,26 +1446,47 @@ class PlacesCollectionRoute extends PageRouteInfo { static PageInfo page = PageInfo( name, builder: (data) { - return const PlacesCollectionPage(); + final args = data.argsAs( + orElse: () => const PlacesCollectionRouteArgs()); + return PlacesCollectionPage( + key: args.key, + currentLocation: args.currentLocation, + ); }, ); } +class PlacesCollectionRouteArgs { + const PlacesCollectionRouteArgs({ + this.key, + this.currentLocation, + }); + + final Key? key; + + final LatLng? currentLocation; + + @override + String toString() { + return 'PlacesCollectionRouteArgs{key: $key, currentLocation: $currentLocation}'; + } +} + /// generated route for -/// [RecentlyAddedPage] -class RecentlyAddedRoute extends PageRouteInfo { - const RecentlyAddedRoute({List? children}) +/// [RecentlyTakenPage] +class RecentlyTakenRoute extends PageRouteInfo { + const RecentlyTakenRoute({List? children}) : super( - RecentlyAddedRoute.name, + RecentlyTakenRoute.name, initialChildren: children, ); - static const String name = 'RecentlyAddedRoute'; + static const String name = 'RecentlyTakenRoute'; static PageInfo page = PageInfo( name, builder: (data) { - return const RecentlyAddedPage(); + return const RecentlyTakenPage(); }, ); } diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart deleted file mode 100644 index d95820885e..0000000000 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; -import 'package:immich_mobile/providers/memory.provider.dart'; -import 'package:immich_mobile/providers/server_info.provider.dart'; - -class TabNavigationObserver extends AutoRouterObserver { - /// Riverpod Instance - final WidgetRef ref; - - TabNavigationObserver({ - required this.ref, - }); - - @override - Future didChangeTabRoute( - TabPageRoute route, - TabPageRoute previousRoute, - ) async { - if (route.name == 'HomeRoute') { - ref.invalidate(memoryFutureProvider); - Future(() => ref.read(assetProvider.notifier).getAllAsset()); - - // Update user info - try { - ref.read(userServiceProvider).refreshMyUser(); - ref.read(serverInfoProvider.notifier).getServerVersion(); - } catch (e) { - debugPrint("Error refreshing user info $e"); - } - } - } -} diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index 92b077ef59..24bdccc04d 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/utils/url_helper.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/user_agent.dart'; class ApiService implements Authentication { late ApiClient _apiClient; @@ -48,6 +49,7 @@ class ApiService implements Authentication { setEndpoint(String endpoint) { _apiClient = ApiClient(basePath: endpoint, authentication: this); + _setUserAgentHeader(); if (_accessToken != null) { setAccessToken(_accessToken!); } @@ -72,6 +74,11 @@ class ApiService implements Authentication { memoriesApi = MemoriesApi(_apiClient); } + Future _setUserAgentHeader() async { + final userAgent = await getUserAgentString(); + _apiClient.addDefaultHeader('User-Agent', userAgent); + } + Future resolveAndSetEndpoint(String serverUrl) async { final endpoint = await resolveEndpoint(serverUrl); setEndpoint(endpoint); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index d187284d07..a52d6e6368 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/interfaces/exif.interface.dart'; import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -197,7 +198,7 @@ class AssetService { ids: assets.map((e) => e.remoteId!).toList(), dateTimeOriginal: updateAssetDto.dateTimeOriginal, isFavorite: updateAssetDto.isFavorite, - isArchived: updateAssetDto.isArchived, + visibility: updateAssetDto.visibility, latitude: updateAssetDto.latitude, longitude: updateAssetDto.longitude, ), @@ -229,10 +230,19 @@ class AssetService { bool isArchived, ) async { try { - await updateAssets(assets, UpdateAssetDto(isArchived: isArchived)); + await updateAssets( + assets, + UpdateAssetDto( + visibility: + isArchived ? AssetVisibility.archive : AssetVisibility.timeline, + ), + ); for (var element in assets) { element.isArchived = isArchived; + element.visibility = isArchived + ? AssetVisibilityEnum.archive + : AssetVisibilityEnum.timeline; } await _syncService.upsertAssetsWithExif(assets); @@ -452,6 +462,7 @@ class AssetService { bool shouldDeletePermanently = false, }) async { final candidates = assets.where((a) => a.isRemote); + if (candidates.isEmpty) { return; } @@ -469,6 +480,7 @@ class AssetService { .where((asset) => asset.storage == AssetState.merged) .map((asset) { asset.remoteId = null; + asset.visibility = AssetVisibilityEnum.timeline; return asset; }) : assets.where((asset) => asset.isRemote).map((asset) { @@ -514,13 +526,30 @@ class AssetService { return _assetRepository.watchAsset(id, fireImmediately: fireImmediately); } - Future> getRecentlyAddedAssets() { + Future> getRecentlyTakenAssets() { final me = _userService.getMyUser(); - return _assetRepository.getRecentlyAddedAssets(me.id); + return _assetRepository.getRecentlyTakenAssets(me.id); } Future> getMotionAssets() { final me = _userService.getMyUser(); return _assetRepository.getMotionAssets(me.id); } + + Future setVisibility( + List assets, + AssetVisibilityEnum visibility, + ) async { + await _assetApiRepository.updateVisibility( + assets.map((asset) => asset.remoteId!).toList(), + visibility, + ); + + final updatedAssets = assets.map((asset) { + asset.visibility = visibility; + return asset; + }).toList(); + + await _assetRepository.updateAll(updatedAssets); + } } diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 20fa62dc4b..41709b714c 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -3,12 +3,14 @@ import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/interfaces/auth.interface.dart'; import 'package:immich_mobile/interfaces/auth_api.interface.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/repositories/auth.repository.dart'; import 'package:immich_mobile/repositories/auth_api.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -22,6 +24,7 @@ final authServiceProvider = Provider( ref.watch(authRepositoryProvider), ref.watch(apiServiceProvider), ref.watch(networkServiceProvider), + ref.watch(backgroundSyncProvider), ), ); @@ -30,6 +33,7 @@ class AuthService { final IAuthRepository _authRepository; final ApiService _apiService; final NetworkService _networkService; + final BackgroundSyncManager _backgroundSyncManager; final _log = Logger("AuthService"); @@ -38,6 +42,7 @@ class AuthService { this._authRepository, this._apiService, this._networkService, + this._backgroundSyncManager, ); /// Validates the provided server URL by resolving and setting the endpoint. @@ -115,8 +120,10 @@ class AuthService { /// - Asset ETag /// /// All deletions are executed in parallel using [Future.wait]. - Future clearLocalData() { - return Future.wait([ + Future clearLocalData() async { + // Cancel any ongoing background sync operations before clearing data + await _backgroundSyncManager.cancel(); + await Future.wait([ _authRepository.clearLocalData(), Store.delete(StoreKey.currentUser), Store.delete(StoreKey.accessToken), @@ -194,4 +201,16 @@ class AuthService { return null; } + + Future unlockPinCode(String pinCode) { + return _authApiRepository.unlockPinCode(pinCode); + } + + Future lockPinCode() { + return _authApiRepository.lockPinCode(); + } + + Future setupPinCode(String pinCode) { + return _authApiRepository.setupPinCode(pinCode); + } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 2d63e1fc18..335f71acab 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -32,7 +32,7 @@ import 'package:immich_mobile/services/localization.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/diff.dart'; -import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:path_provider_foundation/path_provider_foundation.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; @@ -359,7 +359,7 @@ class BackgroundService { ], ); - HttpOverrides.global = HttpSSLCertOverride(); + HttpSSLOptions.apply(); ref .read(apiServiceProvider) .setAccessToken(Store.get(StoreKey.accessToken)); @@ -562,7 +562,7 @@ class BackgroundService { void _onBackupError(ErrorUploadAsset errorAssetInfo) { _showErrorNotification( title: "backup_background_service_upload_failure_notification" - .tr(args: [errorAssetInfo.fileName]), + .tr(namedArgs: {'filename': errorAssetInfo.fileName}), individualTag: errorAssetInfo.id, ); } @@ -577,7 +577,7 @@ class BackgroundService { _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(args: [currentUploadAsset.fileName]); + .tr(namedArgs: {'filename': currentUploadAsset.fileName}); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 4953382722..41d29a496b 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -389,7 +389,6 @@ class BackupService { ); baseRequest.headers.addAll(ApiService.getRequestHeaders()); - baseRequest.headers["Transfer-Encoding"] = "chunked"; baseRequest.fields['deviceAssetId'] = asset.localId!; baseRequest.fields['deviceId'] = deviceId; baseRequest.fields['fileCreatedAt'] = diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart new file mode 100644 index 0000000000..f797e9065a --- /dev/null +++ b/mobile/lib/services/local_auth.service.dart @@ -0,0 +1,26 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/interfaces/biometric.interface.dart'; +import 'package:immich_mobile/models/auth/biometric_status.model.dart'; +import 'package:immich_mobile/repositories/biometric.repository.dart'; + +final localAuthServiceProvider = Provider( + (ref) => LocalAuthService( + ref.watch(biometricRepositoryProvider), + ), +); + +class LocalAuthService { + // final _log = Logger("LocalAuthService"); + + final IBiometricRepository _biometricRepository; + + LocalAuthService(this._biometricRepository); + + Future getStatus() { + return _biometricRepository.getStatus(); + } + + Future authenticate([String? message]) async { + return _biometricRepository.authenticate(message); + } +} diff --git a/mobile/lib/services/localization.service.dart b/mobile/lib/services/localization.service.dart index c8ef662896..8bee710544 100644 --- a/mobile/lib/services/localization.service.dart +++ b/mobile/lib/services/localization.service.dart @@ -1,10 +1,10 @@ // ignore_for_file: implementation_imports -import 'package:flutter/foundation.dart'; -import 'package:easy_localization/src/asset_loader.dart'; import 'package:easy_localization/src/easy_localization_controller.dart'; import 'package:easy_localization/src/localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:immich_mobile/constants/locales.dart'; +import 'package:immich_mobile/generated/codegen_loader.g.dart'; /// Workaround to manually load translations in another Isolate Future loadTranslations() async { @@ -14,7 +14,7 @@ Future loadTranslations() async { supportedLocales: locales.values.toList(), useFallbackTranslations: true, saveLocale: true, - assetLoader: const RootBundleAssetLoader(), + assetLoader: const CodegenLoader(), path: translationsPath, useOnlyLangCode: false, onLoadError: (e) => debugPrint(e.toString()), diff --git a/mobile/lib/services/map.service.dart b/mobile/lib/services/map.service.dart index 26a0746414..2d236f77ef 100644 --- a/mobile/lib/services/map.service.dart +++ b/mobile/lib/services/map.service.dart @@ -2,13 +2,22 @@ import 'package:immich_mobile/mixins/error_logger.mixin.dart'; import 'package:immich_mobile/models/map/map_marker.model.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:immich_mobile/utils/user_agent.dart'; -class MapSerivce with ErrorLoggerMixin { +class MapService with ErrorLoggerMixin { final ApiService _apiService; @override final logger = Logger("MapService"); - MapSerivce(this._apiService); + MapService(this._apiService) { + _setMapUserAgentHeader(); + } + + Future _setMapUserAgentHeader() async { + final userAgent = await getUserAgentString(); + setHttpHeaders({'User-Agent': userAgent}); + } Future> getMapMarkers({ bool? isFavorite, diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index 6ae8e1d0bb..d6c44278c7 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -1,10 +1,10 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/interfaces/asset.interface.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/translation.dart'; import 'package:logging/logging.dart'; final memoryServiceProvider = StateProvider((ref) { @@ -40,9 +40,7 @@ class MemoryService { .getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = yearsAgo <= 1 - ? 'memories_year_ago'.tr() - : 'memories_years_ago'.tr(args: [yearsAgo.toString()]); + final String title = t('years_ago', {'years': yearsAgo.toString()}); memories.add( Memory( title: title, diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index ddd97522f8..9a54a8d7c9 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -13,6 +13,8 @@ class OAuthService { Future getOAuthServerUrl( String serverUrl, + String state, + String codeChallenge, ) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); @@ -22,7 +24,11 @@ class OAuthService { ); final dto = await _apiService.oAuthApi.startOAuth( - OAuthConfigDto(redirectUri: redirectUri), + OAuthConfigDto( + redirectUri: redirectUri, + state: state, + codeChallenge: codeChallenge, + ), ); final authUrl = dto?.url; @@ -31,7 +37,11 @@ class OAuthService { return authUrl; } - Future oAuthLogin(String oauthUrl) async { + Future oAuthLogin( + String oauthUrl, + String state, + String codeVerifier, + ) async { String result = await FlutterWebAuth2.authenticate( url: oauthUrl, callbackUrlScheme: callbackUrlScheme, @@ -49,6 +59,8 @@ class OAuthService { return await _apiService.oAuthApi.finishOAuth( OAuthCallbackDto( url: result, + state: state, + codeVerifier: codeVerifier, ), ); } diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index 44ace78852..bcf67889c0 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -68,7 +68,9 @@ class SearchService { model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - isArchived: filter.display.isArchive ? true : null, + visibility: filter.display.isArchive + ? AssetVisibility.archive + : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), @@ -95,7 +97,9 @@ class SearchService { model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - isArchived: filter.display.isArchive ? true : null, + visibility: filter.display.isArchive + ? AssetVisibility.archive + : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart new file mode 100644 index 0000000000..77803f29c3 --- /dev/null +++ b/mobile/lib/services/secure_storage.service.dart @@ -0,0 +1,29 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/interfaces/secure_storage.interface.dart'; +import 'package:immich_mobile/repositories/secure_storage.repository.dart'; + +final secureStorageServiceProvider = Provider( + (ref) => SecureStorageService( + ref.watch(secureStorageRepositoryProvider), + ), +); + +class SecureStorageService { + // final _log = Logger("LocalAuthService"); + + final ISecureStorageRepository _secureStorageRepository; + + SecureStorageService(this._secureStorageRepository); + + Future write(String key, String value) async { + await _secureStorageRepository.write(key, value); + } + + Future delete(String key) async { + await _secureStorageRepository.delete(key); + } + + Future read(String key) async { + return _secureStorageRepository.read(key); + } +} diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 547e49c1a0..80950d8c00 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -255,9 +255,12 @@ class SyncService { .where((asset) => idsToDelete.contains(asset.remoteId)) .toList(); - for (var asset in matchedAssets) { - _localFilesManager.moveToTrash(asset.fileName); - } + final mediaUrls = await Future.wait( + matchedAssets + .map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), + ); + + await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } /// Deletes remote-only assets, updates merged assets to be local-only @@ -819,13 +822,29 @@ class SyncService { } Future _toggleTrashStatusForAssets(List assetsList) async { - for (var asset in assetsList) { + final trashMediaUrls = []; + + for (final asset in assetsList) { if (asset.isTrashed) { - _localFilesManager.moveToTrash(asset.fileName); + final mediaUrl = await asset.local?.getMediaUrl(); + if (mediaUrl == null) { + _log.warning( + "Failed to get media URL for asset ${asset.name} while moving to trash", + ); + continue; + } + trashMediaUrls.add(mediaUrl); } else { - _localFilesManager.restoreFromTrash(asset.fileName); + await _localFilesManager.restoreFromTrash( + asset.fileName, + asset.type.index, + ); } } + + if (trashMediaUrls.isNotEmpty) { + await _localFilesManager.moveToTrash(trashMediaUrls); + } } /// Inserts or updates the assets in the database with their ExifInfo (if any) diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 4e91d27a7c..7ecad43ca7 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -105,4 +105,13 @@ class TimelineService { return GroupAssetsBy .values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; } + + Stream watchLockedTimelineProvider() async* { + final user = _userService.getMyUser(); + + yield* _timelineRepository.watchLockedTimeline( + user.id, + _getGroupByOption(), + ); + } } diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index 2a593ffb38..a351b09093 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -42,7 +42,7 @@ ThemeData getThemeData({ titleTextStyle: TextStyle( color: colorScheme.primary, fontFamily: _getFontFamilyFromLocale(locale), - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, fontSize: 18, ), backgroundColor: @@ -54,28 +54,28 @@ ThemeData getThemeData({ ), textTheme: const TextTheme( displayLarge: TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, + fontSize: 18, + fontWeight: FontWeight.w600, ), displayMedium: TextStyle( fontSize: 14, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, ), displaySmall: TextStyle( fontSize: 12, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, ), titleSmall: TextStyle( fontSize: 16.0, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, ), titleMedium: TextStyle( fontSize: 18.0, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, ), titleLarge: TextStyle( fontSize: 26.0, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, ), ), elevatedButtonTheme: ElevatedButtonThemeData( diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 570752c6d9..26f3b49242 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -48,11 +48,15 @@ abstract final class Bootstrap { ); } - static Future initDomain(Isar db) async { + static Future initDomain( + Isar db, { + bool shouldBufferLogs = true, + }) async { await StoreService.init(storeRepository: IsarStoreRepository(db)); await LogService.init( logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db), + shouldBuffer: shouldBufferLogs, ); } } diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index ce0384b998..f64757cf9d 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -1,16 +1,20 @@ import 'dart:io'; -import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:logging/logging.dart'; class HttpSSLCertOverride extends HttpOverrides { static final Logger _log = Logger("HttpSSLCertOverride"); + final bool _allowSelfSignedSSLCert; + final String? _serverHost; final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride() : _clientCert = SSLClientCertStoreVal.load() { + HttpSSLCertOverride( + this._allowSelfSignedSSLCert, + this._serverHost, + this._clientCert, + ) { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { @@ -47,28 +51,15 @@ class HttpSSLCertOverride extends HttpOverrides { return super.createHttpClient(context) ..badCertificateCallback = (X509Certificate cert, String host, int port) { - AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; - - // Check if user has allowed self signed SSL certificates. - bool selfSignedCertsAllowed = - Store.get(setting.storeKey as StoreKey, setting.defaultValue); - - bool isLoggedIn = Store.tryGet(StoreKey.currentUser) != null; - - // Conduct server host checks if user is logged in to avoid making - // insecure SSL connections to services that are not the immich server. - if (isLoggedIn && selfSignedCertsAllowed) { - String serverHost = - Uri.parse(Store.tryGet(StoreKey.serverEndpoint) ?? "").host; - - selfSignedCertsAllowed &= serverHost.contains(host); + if (_allowSelfSignedSSLCert) { + // Conduct server host checks if user is logged in to avoid making + // insecure SSL connections to services that are not the immich server. + if (_serverHost == null || _serverHost.contains(host)) { + return true; + } } - - if (!selfSignedCertsAllowed) { - _log.severe("Invalid SSL certificate for $host:$port"); - } - - return selfSignedCertsAllowed; + _log.severe("Invalid SSL certificate for $host:$port"); + return false; }; } } diff --git a/mobile/lib/utils/http_ssl_options.dart b/mobile/lib/utils/http_ssl_options.dart new file mode 100644 index 0000000000..04c01d36d9 --- /dev/null +++ b/mobile/lib/utils/http_ssl_options.dart @@ -0,0 +1,47 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:logging/logging.dart'; + +class HttpSSLOptions { + static const MethodChannel _channel = MethodChannel('immich/httpSSLOptions'); + + static void apply() { + AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; + bool allowSelfSignedSSLCert = + Store.get(setting.storeKey as StoreKey, setting.defaultValue); + _apply(allowSelfSignedSSLCert); + } + + static void applyFromSettings(bool newValue) { + _apply(newValue); + } + + static void _apply(bool allowSelfSignedSSLCert) { + String? serverHost; + if (allowSelfSignedSSLCert && Store.tryGet(StoreKey.currentUser) != null) { + serverHost = Uri.parse(Store.tryGet(StoreKey.serverEndpoint) ?? "").host; + } + + SSLClientCertStoreVal? clientCert = SSLClientCertStoreVal.load(); + + HttpOverrides.global = + HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); + + if (Platform.isAndroid) { + _channel.invokeMethod("apply", [ + allowSelfSignedSSLCert, + serverHost, + clientCert?.data, + clientCert?.password, + ]).onError((e, _) { + final log = Logger("HttpSSLOptions"); + log.severe('Failed to set SSL options', e.message); + }); + } + } +} diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart new file mode 100644 index 0000000000..cfbb1b544f --- /dev/null +++ b/mobile/lib/utils/isolate.dart @@ -0,0 +1,69 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:logging/logging.dart'; +import 'package:worker_manager/worker_manager.dart'; + +class InvalidIsolateUsageException implements Exception { + const InvalidIsolateUsageException(); + + @override + String toString() => + "IsolateHelper should only be used from the root isolate"; +} + +// !! Should be used only from the root isolate +Cancelable runInIsolateGentle({ + required Future Function(ProviderContainer ref) computation, + String? debugLabel, +}) { + final token = RootIsolateToken.instance; + if (token == null) { + throw const InvalidIsolateUsageException(); + } + + return workerManager.executeGentle((cancelledChecker) async { + BackgroundIsolateBinaryMessenger.ensureInitialized(token); + DartPluginRegistrant.ensureInitialized(); + + final db = await Bootstrap.initIsar(); + await Bootstrap.initDomain(db, shouldBufferLogs: false); + final ref = ProviderContainer( + overrides: [ + // TODO: Remove once isar is removed + dbProvider.overrideWithValue(db), + isarProvider.overrideWithValue(db), + cancellationProvider.overrideWithValue(cancelledChecker), + ], + ); + + Logger log = Logger("IsolateLogger"); + + try { + return await computation(ref); + } on CanceledError { + log.warning( + "Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}", + ); + } catch (error, stack) { + log.severe( + "Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", + error, + stack, + ); + } finally { + // Wait for the logs to flush + await Future.delayed(const Duration(seconds: 2)); + // Always close the new db connection on Isolate end + ref.read(driftProvider).close(); + ref.read(isarProvider).close(); + } + return null; + }); +} diff --git a/mobile/lib/utils/local_files_manager.dart b/mobile/lib/utils/local_files_manager.dart index da9308c3cf..a4cf41a6e6 100644 --- a/mobile/lib/utils/local_files_manager.dart +++ b/mobile/lib/utils/local_files_manager.dart @@ -1,38 +1,37 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; -class LocalFilesManager { +abstract final class LocalFilesManager { + static final Logger _logger = Logger('LocalFilesManager'); static const MethodChannel _channel = MethodChannel('file_trash'); - static Future moveToTrash(String fileName) async { + static Future moveToTrash(List mediaUrls) async { try { - final bool success = - await _channel.invokeMethod('moveToTrash', {'fileName': fileName}); - return success; - } on PlatformException catch (e) { - debugPrint('Error moving to trash: ${e.message}'); + return await _channel + .invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); + } catch (e, s) { + _logger.warning('Error moving file to trash', e, s); return false; } } - static Future restoreFromTrash(String fileName) async { + static Future restoreFromTrash(String fileName, int type) async { try { - final bool success = await _channel - .invokeMethod('restoreFromTrash', {'fileName': fileName}); - return success; - } on PlatformException catch (e) { - debugPrint('Error restoring file: ${e.message}'); + return await _channel.invokeMethod( + 'restoreFromTrash', + {'fileName': fileName, 'type': type}, + ); + } catch (e, s) { + _logger.warning('Error restore file from trash', e, s); return false; } } - static Future requestManageStoragePermission() async { + static Future requestManageMediaPermission() async { try { - final bool success = - await _channel.invokeMethod('requestManageStoragePermission'); - return success; - } on PlatformException catch (e) { - debugPrint('Error requesting permission: ${e.message}'); + return await _channel.invokeMethod('requestManageMediaPermission'); + } catch (e, s) { + _logger.warning('Error requesting manage media permission', e, s); return false; } } diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 44f7ebf271..df1ff28d8f 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -64,12 +64,13 @@ class MapUtils { 'features': markers.map(_addFeature).toList(), }; - static Future<(Position?, LocationPermission?)> checkPermAndGetLocation( - BuildContext context, - ) async { + static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ + required BuildContext context, + bool silent = false, + }) async { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); - if (!serviceEnabled) { + if (!serviceEnabled && !silent) { showDialog( context: context, builder: (context) => _LocationServiceDisabledDialog(), @@ -80,7 +81,7 @@ class MapUtils { LocationPermission permission = await Geolocator.checkPermission(); bool shouldRequestPermission = false; - if (permission == LocationPermission.denied) { + if (permission == LocationPermission.denied && !silent) { shouldRequestPermission = await showDialog( context: context, builder: (context) => _LocationPermissionDisabledDialog(), @@ -94,15 +95,19 @@ class MapUtils { permission == LocationPermission.deniedForever) { // Open app settings only if you did not request for permission before if (permission == LocationPermission.deniedForever && - !shouldRequestPermission) { + !shouldRequestPermission && + !silent) { await Geolocator.openAppSettings(); } return (null, LocationPermission.deniedForever); } Position currentUserLocation = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.medium, - timeLimit: const Duration(seconds: 5), + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 0, + timeLimit: Duration(seconds: 5), + ), ); return (currentUserLocation, null); } catch (error, stack) { diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index bebd7a027b..4519c6d803 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/android_device_asset.entity.dart'; @@ -17,8 +17,10 @@ import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:isar/isar.dart'; +// ignore: import_rule_photo_manager +import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 10; +const int targetVersion = 11; Future migrateDatabaseIfNeeded(Isar db) async { final int version = Store.get(StoreKey.version, targetVersion); @@ -69,14 +71,45 @@ Future _migrateDeviceAsset(Isar db) async { : (await db.iOSDeviceAssets.where().findAll()) .map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)) .toList(); - final localAssets = (await db.assets - .where() - .anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)) - .findAll()) - .map((a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt)) - .toList(); - debugPrint("Device Asset Ids length - ${ids.length}"); - debugPrint("Local Asset Ids length - ${localAssets.length}"); + + final PermissionState ps = await PhotoManager.requestPermissionExtend(); + if (!ps.hasAccess) { + if (kDebugMode) { + debugPrint( + "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", + ); + } + + return; + } + + List<_DeviceAsset> localAssets = []; + final List paths = + await PhotoManager.getAssetPathList(onlyAll: true); + + if (paths.isEmpty) { + localAssets = (await db.assets + .where() + .anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)) + .findAll()) + .map( + (a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt), + ) + .toList(); + } else { + final AssetPathEntity albumWithAll = paths.first; + final int assetCount = await albumWithAll.assetCountAsync; + + final List allDeviceAssets = + await albumWithAll.getAssetListRange(start: 0, end: assetCount); + + localAssets = allDeviceAssets + .map((a) => _DeviceAsset(assetId: a.id, dateTime: a.modifiedDateTime)) + .toList(); + } + + debugPrint("[MIGRATION] Device Asset Ids length - ${ids.length}"); + debugPrint("[MIGRATION] Local Asset Ids length - ${localAssets.length}"); ids.sort((a, b) => a.assetId.compareTo(b.assetId)); localAssets.sort((a, b) => a.assetId.compareTo(b.assetId)); final List toAdd = []; @@ -95,15 +128,27 @@ Future _migrateDeviceAsset(Isar db) async { return false; }, onlyFirst: (deviceAsset) { - debugPrint( - 'DeviceAsset not found in local assets: ${deviceAsset.assetId}', - ); + if (kDebugMode) { + debugPrint( + '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', + ); + } }, onlySecond: (asset) { - debugPrint('Local asset not found in DeviceAsset: ${asset.assetId}'); + if (kDebugMode) { + debugPrint( + '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', + ); + } }, ); - debugPrint("Total number of device assets migrated - ${toAdd.length}"); + + if (kDebugMode) { + debugPrint( + "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", + ); + } + await db.writeTxn(() async { await db.deviceAssetEntitys.putAll(toAdd); }); diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 708aec603f..7c7d9bab88 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -31,6 +31,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); } break; + case 'AssetResponseDto': + if (value is Map) { + addDefault(value, 'visibility', 'timeline'); + } + break; case 'UserAdminResponseDto': if (value is Map) { addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index 4a9fcc8c99..1ae583bedd 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -2,12 +2,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/asset_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/share.service.dart'; +import 'package:immich_mobile/utils/translation.dart'; import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; @@ -57,12 +59,13 @@ Future handleArchiveAssets( .read(assetProvider.notifier) .toggleArchive(selection, shouldArchive); - final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; - final archiveOrLibrary = shouldArchive ? 'archive' : 'library'; + final message = shouldArchive + ? t('moved_to_archive', {'count': selection.length}) + : t('moved_to_library', {'count': selection.length}); if (context.mounted) { ImmichToast.show( context: context, - msg: 'Moved ${selection.length} $assetOrAssets to $archiveOrLibrary', + msg: message, gravity: toastGravity, ); } @@ -155,3 +158,29 @@ Future handleEditLocation( ref.read(assetServiceProvider).changeLocation(selection.toList(), location); } + +Future handleSetAssetsVisibility( + WidgetRef ref, + BuildContext context, + AssetVisibilityEnum visibility, + List selection, { + ToastGravity toastGravity = ToastGravity.BOTTOM, +}) async { + if (selection.isNotEmpty) { + await ref + .watch(assetProvider.notifier) + .setLockedView(selection, visibility); + + final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; + final toastMessage = visibility == AssetVisibilityEnum.locked + ? 'Added ${selection.length} $assetOrAssets to locked folder' + : 'Removed ${selection.length} $assetOrAssets from locked folder'; + if (context.mounted) { + ImmichToast.show( + context: context, + msg: toastMessage, + gravity: ToastGravity.BOTTOM, + ); + } + } +} diff --git a/mobile/lib/utils/translation.dart b/mobile/lib/utils/translation.dart new file mode 100644 index 0000000000..461e88ead7 --- /dev/null +++ b/mobile/lib/utils/translation.dart @@ -0,0 +1,14 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:intl/message_format.dart'; + +String t(String key, [Map? args]) { + try { + String message = key.tr(); + if (args != null) { + return MessageFormat(message).format(args); + } + return message; + } catch (e) { + return key; + } +} diff --git a/mobile/lib/utils/user_agent.dart b/mobile/lib/utils/user_agent.dart new file mode 100644 index 0000000000..232bcaec38 --- /dev/null +++ b/mobile/lib/utils/user_agent.dart @@ -0,0 +1,15 @@ +import 'dart:io' show Platform; +import 'package:package_info_plus/package_info_plus.dart'; + +Future getUserAgentString() async { + final packageInfo = await PackageInfo.fromPlatform(); + String platform; + if (Platform.isAndroid) { + platform = 'Android'; + } else if (Platform.isIOS) { + platform = 'iOS'; + } else { + platform = 'Unknown'; + } + return 'Immich_${platform}_${packageInfo.version}'; +} diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index f6f834e00d..79944ef15f 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -61,7 +61,8 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]); + owner = 'album_thumbnail_shared_by' + .tr(namedArgs: {'user': album.ownerName!}); } } @@ -74,10 +75,9 @@ class AlbumThumbnailCard extends ConsumerWidget { children: [ TextSpan( text: album.assetCount == 1 - ? 'album_thumbnail_card_item' - .tr(args: ['${album.assetCount}']) + ? 'album_thumbnail_card_item'.tr() : 'album_thumbnail_card_items' - .tr(args: ['${album.assetCount}']), + .tr(namedArgs: {'count': '${album.assetCount}'}), ), if (owner != null) const TextSpan(text: ' â€ĸ '), if (owner != null) TextSpan(text: owner), diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index 3c1e3a8ee0..17c2a6bd12 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -2,9 +2,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; @@ -96,7 +96,7 @@ class AlbumThumbnailListTile extends StatelessWidget { style: const TextStyle( fontSize: 12, ), - ).tr(args: ['${album.assetCount}']), + ).tr(namedArgs: {'count': '${album.assetCount}'}), if (album.shared) const Text( 'album_thumbnail_card_shared', diff --git a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart index 7a049fa7fd..892e7e5b8a 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/album/add_to_album_sliverlist.dart'; import 'package:immich_mobile/models/asset_selection_state.dart'; import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; @@ -37,6 +38,7 @@ class ControlBottomAppBar extends HookConsumerWidget { final void Function()? onEditTime; final void Function()? onEditLocation; final void Function()? onRemoveFromAlbum; + final void Function()? onToggleLocked; final bool enabled; final bool unfavorite; @@ -58,6 +60,7 @@ class ControlBottomAppBar extends HookConsumerWidget { this.onEditTime, this.onEditLocation, this.onRemoveFromAlbum, + this.onToggleLocked, this.selectionAssetState = const AssetSelectionState(), this.enabled = true, this.unarchive = false, @@ -77,6 +80,7 @@ class ControlBottomAppBar extends HookConsumerWidget { ref.watch(albumProvider).where((a) => a.shared).toList(); const bottomPadding = 0.20; final scrollController = useDraggableScrollController(); + final isInLockedView = ref.watch(inLockedViewProvider); void minimize() { scrollController.animateTo( @@ -133,11 +137,12 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "share".tr(), onPressed: enabled ? () => onShare(true) : null, ), - ControlBoxButton( - iconData: Icons.link_rounded, - label: "control_bottom_app_bar_share_link".tr(), - onPressed: enabled ? () => onShare(false) : null, - ), + if (!isInLockedView) + ControlBoxButton( + iconData: Icons.link_rounded, + label: "share_link".tr(), + onPressed: enabled ? () => onShare(false) : null, + ), if (hasRemote && onArchive != null) ControlBoxButton( iconData: @@ -153,7 +158,7 @@ class ControlBottomAppBar extends HookConsumerWidget { label: (unfavorite ? "unfavorite" : "favorite").tr(), onPressed: enabled ? onFavorite : null, ), - if (hasLocal && hasRemote && onDelete != null) + if (hasLocal && hasRemote && onDelete != null && !isInLockedView) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), child: ControlBoxButton( @@ -166,7 +171,7 @@ class ControlBottomAppBar extends HookConsumerWidget { enabled ? () => showForceDeleteDialog(onDelete!) : null, ), ), - if (hasRemote && onDeleteServer != null) + if (hasRemote && onDeleteServer != null && !isInLockedView) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 85), child: ControlBoxButton( @@ -189,9 +194,23 @@ class ControlBottomAppBar extends HookConsumerWidget { : null, ), ), - if (hasLocal && onDeleteLocal != null) + if (isInLockedView) ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 85), + constraints: const BoxConstraints(maxWidth: 110), + child: ControlBoxButton( + iconData: Icons.delete_forever, + label: "delete_dialog_title".tr(), + onPressed: enabled + ? () => showForceDeleteDialog( + onDeleteServer!, + alertMsg: "delete_dialog_alert_remote", + ) + : null, + ), + ), + if (hasLocal && onDeleteLocal != null && !isInLockedView) + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 95), child: ControlBoxButton( iconData: Icons.no_cell_outlined, label: "control_bottom_app_bar_delete_from_local".tr(), @@ -231,6 +250,19 @@ class ControlBottomAppBar extends HookConsumerWidget { onPressed: enabled ? onEditLocation : null, ), ), + if (hasRemote) + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 100), + child: ControlBoxButton( + iconData: isInLockedView + ? Icons.lock_open_rounded + : Icons.lock_outline_rounded, + label: isInLockedView + ? "remove_from_locked_folder".tr() + : "move_to_locked_folder".tr(), + onPressed: enabled ? onToggleLocked : null, + ), + ), if (!selectionAssetState.hasLocal && selectionAssetState.selectedCount > 1 && onStack != null) @@ -269,20 +301,40 @@ class ControlBottomAppBar extends HookConsumerWidget { ]; } + getInitialSize() { + if (isInLockedView) { + return 0.20; + } + if (hasRemote) { + return 0.35; + } + return bottomPadding; + } + + getMaxChildSize() { + if (isInLockedView) { + return 0.20; + } + if (hasRemote) { + return 0.65; + } + return bottomPadding; + } + return DraggableScrollableSheet( controller: scrollController, - initialChildSize: hasRemote ? 0.35 : bottomPadding, + initialChildSize: getInitialSize(), minChildSize: bottomPadding, - maxChildSize: hasRemote ? 0.65 : bottomPadding, + maxChildSize: getMaxChildSize(), snap: true, builder: ( BuildContext context, ScrollController scrollController, ) { return Card( - color: context.colorScheme.surfaceContainerLow, - surfaceTintColor: Colors.transparent, - elevation: 18.0, + color: context.colorScheme.surfaceContainerHigh, + surfaceTintColor: context.colorScheme.surfaceContainerHigh, + elevation: 6.0, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), @@ -300,27 +352,27 @@ class ControlBottomAppBar extends HookConsumerWidget { const CustomDraggingHandle(), const SizedBox(height: 12), SizedBox( - height: 100, + height: 120, child: ListView( shrinkWrap: true, scrollDirection: Axis.horizontal, children: renderActionButtons(), ), ), - if (hasRemote) + if (hasRemote && !isInLockedView) ...[ const Divider( indent: 16, endIndent: 16, thickness: 1, ), - if (hasRemote) _AddToAlbumTitleRow( onCreateNewAlbum: enabled ? onCreateNewAlbum : null, ), + ], ], ), ), - if (hasRemote) + if (hasRemote && !isInLockedView) SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16), sliver: AddToAlbumSliverList( @@ -352,12 +404,9 @@ class _AddToAlbumTitleRow extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( "add_to_album", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + style: context.textTheme.titleSmall, ).tr(), TextButton.icon( onPressed: onCreateNewAlbum, diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index 2ec01e871f..da4c47e466 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -97,6 +97,7 @@ class ImmichAssetGrid extends HookConsumerWidget { ); if (7 - scaleFactor.value.toInt() != perRow.value) { perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); } }; }), diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart index a7141c33b2..060898e270 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -755,7 +755,7 @@ class _MonthTitle extends StatelessWidget { key: Key("month-$title"), padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( - title, + toBeginningOfSentenceCase(title, context.locale.languageCode), style: const TextStyle( fontSize: 26, fontWeight: FontWeight.w500, @@ -786,7 +786,7 @@ class _Title extends StatelessWidget { @override Widget build(BuildContext context) { return GroupDividerTitle( - text: title, + text: toBeginningOfSentenceCase(title, context.locale.languageCode), multiselectEnabled: selectionActive, onSelect: () => selectAssets(assets), onDeselect: () => deselectAssets(assets), diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index 5ec59e3eeb..8cc725ab77 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -7,24 +7,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/collection_extensions.dart'; -import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:immich_mobile/services/album.service.dart'; -import 'package:immich_mobile/services/stack.service.dart'; -import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; -import 'package:immich_mobile/models/asset_selection_state.dart'; -import 'package:immich_mobile/providers/multiselect.provider.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; -import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart'; -import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/collection_extensions.dart'; +import 'package:immich_mobile/models/asset_selection_state.dart'; +import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; +import 'package:immich_mobile/providers/multiselect.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/album.service.dart'; +import 'package:immich_mobile/services/stack.service.dart'; import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/utils/selection_handlers.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart'; +import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class MultiselectGrid extends HookConsumerWidget { const MultiselectGrid({ @@ -190,8 +192,9 @@ class MultiselectGrid extends HookConsumerWidget { context: context, msg: force ? 'assets_deleted_permanently' - .tr(args: ["${selection.value.length}"]) - : 'assets_trashed'.tr(args: ["${selection.value.length}"]), + .tr(namedArgs: {'count': "${selection.value.length}"}) + : 'assets_trashed' + .tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); selectionEnabledHook.value = false; @@ -225,7 +228,7 @@ class MultiselectGrid extends HookConsumerWidget { ImmichToast.show( context: context, msg: 'assets_removed_permanently_from_device' - .tr(args: ["$deletedCount"]), + .tr(namedArgs: {'count': "$deletedCount"}), gravity: ToastGravity.BOTTOM, ); @@ -254,8 +257,9 @@ class MultiselectGrid extends HookConsumerWidget { context: context, msg: shouldDeletePermanently ? 'assets_deleted_permanently_from_server' - .tr(args: ["${toDelete.length}"]) - : 'assets_trashed_from_server'.tr(args: ["${toDelete.length}"]), + .tr(namedArgs: {'count': "${toDelete.length}"}) + : 'assets_trashed_from_server' + .tr(namedArgs: {'count': "${toDelete.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -393,6 +397,32 @@ class MultiselectGrid extends HookConsumerWidget { } } + void onToggleLockedVisibility() async { + processing.value = true; + try { + final remoteAssets = ownedRemoteSelection( + localErrorMessage: 'home_page_locked_error_local'.tr(), + ownerErrorMessage: 'home_page_locked_error_partner'.tr(), + ); + if (remoteAssets.isNotEmpty) { + final isInLockedView = ref.read(inLockedViewProvider); + final visibility = isInLockedView + ? AssetVisibilityEnum.timeline + : AssetVisibilityEnum.locked; + + await handleSetAssetsVisibility( + ref, + context, + visibility, + remoteAssets.toList(), + ); + } + } finally { + processing.value = false; + selectionEnabledHook.value = false; + } + } + Future Function() wrapLongRunningFun( Future Function() fun, { bool showOverlay = true, @@ -458,6 +488,7 @@ class MultiselectGrid extends HookConsumerWidget { onEditLocation: editEnabled ? onEditLocation : null, unfavorite: unfavorite, unarchive: unarchive, + onToggleLocked: onToggleLockedVisibility, onRemoveFromAlbum: onRemoveFromAlbum != null ? wrapLongRunningFun( () => onRemoveFromAlbum!(selection.value), diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 8bfcdc12ca..1ff8596c43 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -15,6 +15,7 @@ import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -46,6 +47,7 @@ class BottomGalleryBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final isInLockedView = ref.watch(inLockedViewProvider); final asset = ref.watch(currentAssetProvider); if (asset == null) { return const SizedBox(); @@ -277,7 +279,7 @@ class BottomGalleryBar extends ConsumerWidget { tooltip: 'share'.tr(), ): (_) => shareAsset(), }, - if (asset.isImage) + if (asset.isImage && !isInLockedView) { BottomNavigationBarItem( icon: const Icon(Icons.tune_outlined), @@ -285,7 +287,7 @@ class BottomGalleryBar extends ConsumerWidget { tooltip: 'edit'.tr(), ): (_) => handleEdit(), }, - if (isOwner) + if (isOwner && !isInLockedView) { asset.isArchived ? BottomNavigationBarItem( @@ -299,7 +301,7 @@ class BottomGalleryBar extends ConsumerWidget { tooltip: 'archive'.tr(), ): (_) => handleArchive(), }, - if (isOwner && asset.stackCount > 0) + if (isOwner && asset.stackCount > 0 && !isInLockedView) { BottomNavigationBarItem( icon: const Icon(Icons.burst_mode_outlined), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index 712e939ad5..8d2a38c700 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -2,13 +2,13 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/providers/asset_viewer/asset_people.provider.dart'; import 'package:immich_mobile/models/search/search_curated_content.model.dart'; +import 'package:immich_mobile/providers/asset_viewer/asset_people.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/search/curated_people_row.dart'; import 'package:immich_mobile/widgets/search/person_name_edit_form.dart'; -import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; class PeopleInfo extends ConsumerWidget { final Asset asset; @@ -109,13 +109,13 @@ class PeopleInfo extends ConsumerWidget { if (ageInMonths <= 11) { return "exif_bottom_sheet_person_age_months" - .tr(args: [ageInMonths.toString()]); + .tr(namedArgs: {'months': ageInMonths.toString()}); } else if (ageInMonths > 12 && ageInMonths <= 23) { return "exif_bottom_sheet_person_age_year_months" - .tr(args: [(ageInMonths - 12).toString()]); + .tr(namedArgs: {'months': (ageInMonths - 12).toString()}); } else { return "exif_bottom_sheet_person_age_years" - .tr(args: [ageInYears.toString()]); + .tr(namedArgs: {'years': ageInYears.toString()}); } } diff --git a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart index 937d1adf32..64cb1c619f 100644 --- a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/providers/activity_statistics.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/motion_photo_button.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; @@ -39,6 +40,7 @@ class TopControlAppBar extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final isInLockedView = ref.watch(inLockedViewProvider); const double iconSize = 22.0; final a = ref.watch(assetWatcher(asset)).value ?? asset; final album = ref.watch(currentAlbumProvider); @@ -178,15 +180,22 @@ class TopControlAppBar extends HookConsumerWidget { shape: const Border(), actions: [ if (asset.isRemote && isOwner) buildFavoriteButton(a), - if (isOwner && !isInHomePage && !(isInTrash ?? false)) + if (isOwner && + !isInHomePage && + !(isInTrash ?? false) && + !isInLockedView) buildLocateButton(), if (asset.livePhotoVideoId != null) const MotionPhotoButton(), if (asset.isLocal && !asset.isRemote) buildUploadButton(), if (asset.isRemote && !asset.isLocal && isOwner) buildDownloadButton(), - if (asset.isRemote && (isOwner || isPartner) && !asset.isTrashed) + if (asset.isRemote && + (isOwner || isPartner) && + !asset.isTrashed && + !isInLockedView) buildAddToAlbumButton(), if (asset.isTrashed) buildRestoreButton(), - if (album != null && album.shared) buildActivitiesButton(), + if (album != null && album.shared && !isInLockedView) + buildActivitiesButton(), buildMoreInfoButton(), ], ); diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart index 222e516cf7..98bcc2b3da 100644 --- a/mobile/lib/widgets/backup/asset_info_table.dart +++ b/mobile/lib/widgets/backup/asset_info_table.dart @@ -56,9 +56,12 @@ class BackupAssetInfoTable extends ConsumerWidget { fontSize: 10.0, ), ).tr( - args: isUploadInProgress - ? [asset.fileName, asset.fileType.toLowerCase()] - : ["-", "-"], + namedArgs: isUploadInProgress + ? { + 'filename': asset.fileName, + 'size': asset.fileType.toLowerCase(), + } + : {'filename': "-", 'size': "-"}, ), ), ), @@ -78,9 +81,11 @@ class BackupAssetInfoTable extends ConsumerWidget { fontSize: 10.0, ), ).tr( - args: [ - isUploadInProgress ? _getAssetCreationDate(asset) : "-", - ], + namedArgs: { + 'date': isUploadInProgress + ? _getAssetCreationDate(asset) + : "-", + }, ), ), ), @@ -99,9 +104,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontSize: 10.0, ), ).tr( - args: [ - isUploadInProgress ? asset.id : "-", - ], + namedArgs: {'id': isUploadInProgress ? asset.id : "-"}, ), ), ), diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart index 540e136722..38c527ccfa 100644 --- a/mobile/lib/widgets/backup/error_chip_text.dart +++ b/mobile/lib/widgets/backup/error_chip_text.dart @@ -21,8 +21,6 @@ class BackupErrorChipText extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 11, ), - ).tr( - args: [count.toString()], - ); + ).tr(namedArgs: {'count': count.toString()}); } } diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index da863ef142..b2b24bd01c 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -5,17 +5,17 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; +import 'package:immich_mobile/providers/asset.provider.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; -import 'package:immich_mobile/providers/auth.provider.dart'; -import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_server_info.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; -import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:url_launcher/url_launcher.dart'; class ImmichAppBarDialog extends HookConsumerWidget { @@ -200,10 +200,10 @@ class ImmichAppBarDialog extends HookConsumerWidget { padding: const EdgeInsets.only(top: 12.0), child: const Text('backup_controller_page_storage_format').tr( - args: [ - usedDiskSpace, - totalDiskSpace, - ], + namedArgs: { + 'used': usedDiskSpace, + 'total': totalDiskSpace, + }, ), ), ], diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index 45addd0c2e..923050bcc6 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -35,7 +35,9 @@ class ControlBoxButton extends StatelessWidget { Widget build(BuildContext context) { return MaterialButton( padding: const EdgeInsets.all(10), - shape: const CircleBorder(), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), onPressed: onPressed, onLongPress: onLongPressed, minWidth: 75.0, @@ -47,8 +49,8 @@ class ControlBoxButton extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle(fontSize: 12.0), - maxLines: 2, + style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400), + maxLines: 3, textAlign: TextAlign.center, ), ], diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 51b4faa014..4f95e657d9 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -1,11 +1,13 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -178,6 +180,11 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { child: action, ), ), + if (kDebugMode) + IconButton( + onPressed: () => ref.read(backgroundSyncProvider).sync(), + icon: const Icon(Icons.sync), + ), if (showUploadButton) Padding( padding: const EdgeInsets.only(right: 20), diff --git a/mobile/lib/widgets/common/immich_image.dart b/mobile/lib/widgets/common/immich_image.dart index 243ef55412..cbad9037c0 100644 --- a/mobile/lib/widgets/common/immich_image.dart +++ b/mobile/lib/widgets/common/immich_image.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -76,39 +75,32 @@ class ImmichImage extends StatelessWidget { ); } + final imageProviderInstance = ImmichImage.imageProvider( + asset: asset, + width: context.width, + height: context.height, + ); + return OctoImage( fadeInDuration: const Duration(milliseconds: 0), - fadeOutDuration: const Duration(milliseconds: 200), + fadeOutDuration: const Duration(milliseconds: 100), placeholderBuilder: (context) { if (placeholder != null) { - // Use the gray box placeholder return placeholder!; } - // No placeholder return const SizedBox(); }, - image: ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + image: imageProviderInstance, width: width, height: height, fit: fit, errorBuilder: (context, error, stackTrace) { - if (error is PlatformException && - error.code == "The asset not found!") { - debugPrint( - "Asset ${asset?.localId} does not exist anymore on device!", - ); - } else { - debugPrint( - "Error getting thumb for assetId=${asset?.localId}: $error", - ); - } + imageProviderInstance.evict(); + return Icon( Icons.image_not_supported_outlined, - color: context.primaryColor, + size: 32, + color: Colors.red[200], ); }, ); diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 9f7725aa12..43987878cb 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -12,14 +12,11 @@ class ImmichLogo extends StatelessWidget { @override Widget build(BuildContext context) { - return Hero( - tag: heroTag, - child: Image( - image: const AssetImage('assets/immich-logo.png'), - width: size, - filterQuality: FilterQuality.high, - isAntiAlias: true, - ), + return Image( + image: const AssetImage('assets/immich-logo.png'), + width: size, + filterQuality: FilterQuality.high, + isAntiAlias: true, ); } } diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 2ebead0083..58613a43ec 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart'; import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -9,8 +9,9 @@ import 'package:immich_mobile/utils/hooks/blurhash_hook.dart'; import 'package:immich_mobile/widgets/common/immich_image.dart'; import 'package:immich_mobile/widgets/common/thumbhash_placeholder.dart'; import 'package:octo_image/octo_image.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; -class ImmichThumbnail extends HookWidget { +class ImmichThumbnail extends HookConsumerWidget { const ImmichThumbnail({ this.asset, this.width = 250, @@ -31,6 +32,7 @@ class ImmichThumbnail extends HookWidget { static ImageProvider imageProvider({ Asset? asset, String? assetId, + String? userId, int thumbnailSize = 256, }) { if (asset == null && assetId == null) { @@ -48,6 +50,7 @@ class ImmichThumbnail extends HookWidget { asset: asset, height: thumbnailSize, width: thumbnailSize, + userId: userId, ); } else { return ImmichRemoteThumbnailProvider( @@ -59,8 +62,10 @@ class ImmichThumbnail extends HookWidget { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { Uint8List? blurhash = useBlurHashRef(asset).value; + final userId = ref.watch(currentUserProvider)?.id; + if (asset == null) { return Container( color: Colors.grey, @@ -72,14 +77,28 @@ class ImmichThumbnail extends HookWidget { ); } + final thumbnailProviderInstance = ImmichThumbnail.imageProvider( + asset: asset, + userId: userId, + ); + + customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) { + thumbnailProviderInstance.evict(); + + final originalErrorWidgetBuilder = + blurHashErrorBuilder(blurhash, fit: fit); + return originalErrorWidgetBuilder(ctx, error, stackTrace); + } + return OctoImage.fromSet( placeholderFadeInDuration: Duration.zero, fadeInDuration: Duration.zero, fadeOutDuration: const Duration(milliseconds: 100), - octoSet: blurHashOrPlaceholder(blurhash), - image: ImmichThumbnail.imageProvider( - asset: asset, + octoSet: OctoSet( + placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), + errorBuilder: customErrorBuilder, ), + image: thumbnailProviderInstance, width: width, height: height, fit: fit, diff --git a/mobile/lib/widgets/common/immich_toast.dart b/mobile/lib/widgets/common/immich_toast.dart index 7f3207032b..945568a74c 100644 --- a/mobile/lib/widgets/common/immich_toast.dart +++ b/mobile/lib/widgets/common/immich_toast.dart @@ -40,7 +40,7 @@ class ImmichToast { child: Container( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5.0), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), color: context.colorScheme.surfaceContainer, border: Border.all( color: context.colorScheme.outline.withValues(alpha: .5), @@ -59,14 +59,23 @@ class ImmichToast { msg, style: TextStyle( color: getColor(toastType, context), - fontWeight: FontWeight.bold, - fontSize: 15, + fontWeight: FontWeight.w600, + fontSize: 14, ), ), ), ], ), ), + positionedToastBuilder: (context, child, gravity) { + return Positioned( + top: gravity == ToastGravity.TOP ? 150 : null, + bottom: gravity == ToastGravity.BOTTOM ? 150 : null, + left: MediaQuery.of(context).size.width / 2 - 150, + right: MediaQuery.of(context).size.width / 2 - 150, + child: child, + ); + }, gravity: gravity, toastDuration: Duration(seconds: durationInSecond), ); diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 7af52b413d..5374d1ef33 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; import 'dart:io'; +import 'dart:math'; import 'package:auto_route/auto_route.dart'; +import 'package:crypto/crypto.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -203,13 +206,51 @@ class LoginForm extends HookConsumerWidget { } } + String generateRandomString(int length) { + const chars = + 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + final random = Random.secure(); + return String.fromCharCodes( + Iterable.generate( + length, + (_) => chars.codeUnitAt(random.nextInt(chars.length)), + ), + ); + } + + List randomBytes(int length) { + final random = Random.secure(); + return List.generate(length, (i) => random.nextInt(256)); + } + + /// Per specification, the code verifier must be 43-128 characters long + /// and consist of characters [A-Z, a-z, 0-9, "-", ".", "_", "~"] + /// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 + String randomCodeVerifier() { + return base64Url.encode(randomBytes(42)); + } + + Future generatePKCECodeChallenge(String codeVerifier) async { + var bytes = utf8.encode(codeVerifier); + var digest = sha256.convert(bytes); + return base64Url.encode(digest.bytes).replaceAll('=', ''); + } + oAuthLogin() async { var oAuthService = ref.watch(oAuthServiceProvider); String? oAuthServerUrl; + final state = generateRandomString(32); + + final codeVerifier = randomCodeVerifier(); + final codeChallenge = await generatePKCECodeChallenge(codeVerifier); + try { - oAuthServerUrl = await oAuthService - .getOAuthServerUrl(sanitizeUrl(serverEndpointController.text)); + oAuthServerUrl = await oAuthService.getOAuthServerUrl( + sanitizeUrl(serverEndpointController.text), + state, + codeChallenge, + ); isLoading.value = true; @@ -230,8 +271,11 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = - await oAuthService.oAuthLogin(oAuthServerUrl); + final loginResponseDto = await oAuthService.oAuthLogin( + oAuthServerUrl, + state, + codeVerifier, + ); if (loginResponseDto == null) { return; diff --git a/mobile/lib/widgets/forms/pin_input.dart b/mobile/lib/widgets/forms/pin_input.dart new file mode 100644 index 0000000000..1588a65c60 --- /dev/null +++ b/mobile/lib/widgets/forms/pin_input.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:pinput/pinput.dart'; + +class PinInput extends StatelessWidget { + final Function(String)? onCompleted; + final Function(String)? onChanged; + final int? length; + final bool? obscureText; + final bool? autoFocus; + final bool? hasError; + final String? label; + final TextEditingController? controller; + + const PinInput({ + super.key, + this.onCompleted, + this.onChanged, + this.length, + this.obscureText, + this.autoFocus, + this.hasError, + this.label, + this.controller, + }); + + @override + Widget build(BuildContext context) { + getPinSize() { + final minimumPadding = 18.0; + final gapWidth = 3.0; + final screenWidth = context.width; + final pinWidth = + (screenWidth - (minimumPadding * 2) - (gapWidth * 5)) / (length ?? 6); + + if (pinWidth > 60) { + return const Size(60, 64); + } + + final pinHeight = pinWidth / (60 / 64); + return Size(pinWidth, pinHeight); + } + + final defaultPinTheme = PinTheme( + width: getPinSize().width, + height: getPinSize().height, + textStyle: TextStyle( + fontSize: 24, + color: context.colorScheme.onSurface, + fontFamily: 'Overpass Mono', + ), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(19)), + border: Border.all(color: context.colorScheme.surfaceBright), + color: context.colorScheme.surfaceContainerHigh, + ), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label != null) ...[ + Text( + label!, + style: context.textTheme.displayLarge + ?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + ), + const SizedBox(height: 4), + ], + Pinput( + controller: controller, + forceErrorState: hasError ?? false, + autofocus: autoFocus ?? false, + obscureText: obscureText ?? false, + obscuringWidget: Icon( + Icons.vpn_key_rounded, + color: context.primaryColor, + size: 20, + ), + separatorBuilder: (index) => const SizedBox( + height: 64, + width: 3, + ), + cursor: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + margin: const EdgeInsets.only(bottom: 9), + width: 18, + height: 2, + color: context.primaryColor, + ), + ], + ), + defaultPinTheme: defaultPinTheme, + focusedPinTheme: defaultPinTheme.copyWith( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(19)), + border: Border.all( + color: context.primaryColor.withValues(alpha: 0.5), + width: 2, + ), + color: context.colorScheme.surfaceContainerHigh, + ), + ), + errorPinTheme: defaultPinTheme.copyWith( + decoration: BoxDecoration( + color: context.colorScheme.error.withAlpha(15), + borderRadius: const BorderRadius.all(Radius.circular(19)), + border: Border.all( + color: context.colorScheme.error.withAlpha(100), + width: 2, + ), + ), + ), + pinputAutovalidateMode: PinputAutovalidateMode.onSubmit, + length: length ?? 6, + onChanged: onChanged, + onCompleted: onCompleted, + ), + ], + ); + } +} diff --git a/mobile/lib/widgets/forms/pin_registration_form.dart b/mobile/lib/widgets/forms/pin_registration_form.dart new file mode 100644 index 0000000000..c3cfd3a864 --- /dev/null +++ b/mobile/lib/widgets/forms/pin_registration_form.dart @@ -0,0 +1,128 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/widgets/forms/pin_input.dart'; + +class PinRegistrationForm extends HookConsumerWidget { + final Function() onDone; + + const PinRegistrationForm({ + super.key, + required this.onDone, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final hasError = useState(false); + final newPinCodeController = useTextEditingController(); + final confirmPinCodeController = useTextEditingController(); + + bool validatePinCode() { + if (confirmPinCodeController.text.length != 6) { + return false; + } + + if (newPinCodeController.text != confirmPinCodeController.text) { + return false; + } + + return true; + } + + createNewPinCode() async { + final isValid = validatePinCode(); + if (!isValid) { + hasError.value = true; + return; + } + + try { + await ref.read(authProvider.notifier).setupPinCode( + newPinCodeController.text, + ); + + onDone(); + } catch (error) { + hasError.value = true; + context.showSnackBar( + SnackBar(content: Text(error.toString())), + ); + } + } + + return Form( + child: Column( + children: [ + Icon( + Icons.pin_outlined, + size: 64, + color: context.primaryColor, + ), + const SizedBox(height: 32), + SizedBox( + width: context.width * 0.7, + child: Text( + 'setup_pin_code'.tr(), + style: context.textTheme.labelLarge!.copyWith( + fontSize: 24, + ), + textAlign: TextAlign.center, + ), + ), + SizedBox( + width: context.width * 0.8, + child: Text( + 'new_pin_code_subtitle'.tr(), + style: context.textTheme.bodyLarge!.copyWith( + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 32), + PinInput( + controller: newPinCodeController, + label: 'new_pin_code'.tr(), + length: 6, + autoFocus: true, + hasError: hasError.value, + onChanged: (input) { + if (input.length < 6) { + hasError.value = false; + } + }, + ), + const SizedBox(height: 32), + PinInput( + controller: confirmPinCodeController, + label: 'confirm_new_pin_code'.tr(), + length: 6, + hasError: hasError.value, + onChanged: (input) { + if (input.length < 6) { + hasError.value = false; + } + }, + ), + const SizedBox(height: 48), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: createNewPinCode, + child: Text('create'.tr()), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/forms/pin_verification_form.dart b/mobile/lib/widgets/forms/pin_verification_form.dart new file mode 100644 index 0000000000..f4ebf4272f --- /dev/null +++ b/mobile/lib/widgets/forms/pin_verification_form.dart @@ -0,0 +1,94 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/widgets/forms/pin_input.dart'; + +class PinVerificationForm extends HookConsumerWidget { + final Function(String) onSuccess; + final VoidCallback? onError; + final bool? autoFocus; + final String? description; + final IconData? icon; + final IconData? successIcon; + + const PinVerificationForm({ + super.key, + required this.onSuccess, + this.onError, + this.autoFocus, + this.description, + this.icon, + this.successIcon, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final hasError = useState(false); + final isVerified = useState(false); + + verifyPin(String pinCode) async { + final isUnlocked = + await ref.read(authProvider.notifier).unlockPinCode(pinCode); + + if (isUnlocked) { + isVerified.value = true; + + await Future.delayed(const Duration(seconds: 1)); + onSuccess(pinCode); + } else { + hasError.value = true; + onError?.call(); + } + } + + return Form( + child: Column( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isVerified.value + ? Icon( + successIcon ?? Icons.lock_open_rounded, + size: 64, + color: Colors.green[300], + ) + : Icon( + icon ?? Icons.lock_outline_rounded, + size: 64, + color: hasError.value + ? context.colorScheme.error + : context.primaryColor, + ), + ), + const SizedBox(height: 36), + SizedBox( + width: context.width * 0.7, + child: Text( + description ?? 'enter_your_pin_code_subtitle'.tr(), + style: context.textTheme.labelLarge!.copyWith( + fontSize: 18, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 18), + PinInput( + obscureText: true, + autoFocus: autoFocus, + hasError: hasError.value, + length: 6, + onChanged: (pinCode) { + if (pinCode.length < 6) { + hasError.value = false; + } + }, + onCompleted: verifyPin, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 18003cf293..4a3bb69cc0 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -1,20 +1,21 @@ import 'dart:math' as math; + import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; -import 'package:immich_mobile/providers/timeline.provider.dart'; -import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/models/map/map_event.model.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/db.provider.dart'; -import 'package:immich_mobile/widgets/common/drag_sheet.dart'; +import 'package:immich_mobile/providers/timeline.provider.dart'; import 'package:immich_mobile/utils/color_filter_generator.dart'; import 'package:immich_mobile/utils/throttle.dart'; +import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; +import 'package:immich_mobile/widgets/common/drag_sheet.dart'; import 'package:logging/logging.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; @@ -46,12 +47,39 @@ class MapAssetGrid extends HookConsumerWidget { final gridScrollThrottler = useThrottler(interval: const Duration(milliseconds: 300)); + // Add a cache for assets we've already loaded + final assetCache = useRef>({}); + void handleMapEvents(MapEvent event) async { if (event is MapAssetsInBoundsUpdated) { - assetsInBounds.value = await ref - .read(dbProvider) - .assets - .getAllByRemoteId(event.assetRemoteIds); + final assetIds = event.assetRemoteIds; + final missingIds = []; + final currentAssets = []; + + for (final id in assetIds) { + final asset = assetCache.value[id]; + if (asset != null) { + currentAssets.add(asset); + } else { + missingIds.add(id); + } + } + + // Only fetch missing assets + if (missingIds.isNotEmpty) { + final newAssets = + await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); + + // Add new assets to cache and current list + for (final asset in newAssets) { + if (asset.remoteId != null) { + assetCache.value[asset.remoteId!] = asset; + currentAssets.add(asset); + } + } + } + + assetsInBounds.value = currentAssets; return; } } @@ -124,7 +152,7 @@ class MapAssetGrid extends HookConsumerWidget { alignment: Alignment.bottomCenter, child: FractionallySizedBox( // Place it just below the drag handle - heightFactor: 0.80, + heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty ? ref .watch(assetsTimelineProvider(assetsInBounds.value)) @@ -225,7 +253,8 @@ class _MapSheetDragRegion extends StatelessWidget { @override Widget build(BuildContext context) { final assetsInBoundsText = assetsInBoundCount > 0 - ? "map_assets_in_bounds".tr(args: [assetsInBoundCount.toString()]) + ? "map_assets_in_bounds" + .tr(namedArgs: {'count': assetsInBoundCount.toString()}) : "map_no_assets_in_bounds".tr(); return SingleChildScrollView( @@ -251,8 +280,18 @@ class _MapSheetDragRegion extends StatelessWidget { const SizedBox(height: 15), const CustomDraggingHandle(), const SizedBox(height: 15), - Text(assetsInBoundsText, style: context.textTheme.bodyLarge), - const Divider(height: 35), + Center( + child: Text( + assetsInBoundsText, + style: TextStyle( + fontSize: 20, + color: context.textTheme.displayLarge?.color + ?.withValues(alpha: 0.75), + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(height: 8), ], ), ValueListenableBuilder( @@ -260,14 +299,14 @@ class _MapSheetDragRegion extends StatelessWidget { builder: (_, value, __) => Visibility( visible: value != null, child: Positioned( - right: 15, - top: 15, + right: 18, + top: 24, child: IconButton( icon: Icon( Icons.map_outlined, color: context.textTheme.displayLarge?.color, ), - iconSize: 20, + iconSize: 24, tooltip: 'Zoom to bounds', onPressed: () => onZoomToAsset?.call(value!), ), diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart index c613029886..e23716af95 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart @@ -44,13 +44,13 @@ class MapTimeDropDown extends StatelessWidget { DropdownMenuEntry( value: 7, label: "map_settings_date_range_option_days".tr( - args: ["7"], + namedArgs: {'days': "7"}, ), ), DropdownMenuEntry( value: 30, label: "map_settings_date_range_option_days".tr( - args: ["30"], + namedArgs: {'days': "30"}, ), ), DropdownMenuEntry( @@ -81,7 +81,8 @@ class MapTimeDropDown extends StatelessWidget { ), ) .inDays, - label: "map_settings_date_range_option_years".tr(args: ["3"]), + label: "map_settings_date_range_option_years" + .tr(namedArgs: {'years': "3"}), ), ], ), diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index 0a5cae0fa0..80d7427d82 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -169,7 +169,7 @@ class PhotoViewCoreState extends State scale: newScale, position: widget.enablePanAlways ? delta - : clampPosition(position: delta * details.scale), + : clampPosition(position: delta, scale: details.scale), rotation: widget.enableRotation ? _rotationBefore! + details.rotation : null, rotationFocusPoint: widget.enableRotation ? details.focalPoint : null, diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index b4a12ab826..78af8f936b 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -20,7 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { return ThumbnailWithInfoContainer( label: 'search_page_your_map'.tr(), onTap: () { - context.pushRoute(const MapRoute()); + context.pushRoute(MapRoute()); }, child: IgnorePointer( child: MapThumbnail( diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 98c8728298..eb13c67640 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -10,7 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; -import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart'; import 'package:immich_mobile/widgets/settings/local_storage_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -49,7 +49,7 @@ class AdvancedSettings extends HookConsumerWidget { DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; int sdkVersion = androidInfo.version.sdkInt; - return sdkVersion >= 30; + return sdkVersion >= 31; } return false; } @@ -74,7 +74,7 @@ class AdvancedSettings extends HookConsumerWidget { if (value) { final result = await ref .read(localFilesManagerRepositoryProvider) - .requestManageStoragePermission(); + .requestManageMediaPermission(); manageLocalMediaAndroid.value = result; } }, @@ -85,7 +85,8 @@ class AdvancedSettings extends HookConsumerWidget { }, ), SettingsSliderListTile( - text: "advanced_settings_log_level_title".tr(args: [logLevel]), + text: "advanced_settings_log_level_title" + .tr(namedArgs: {'level': logLevel}), valueNotifier: levelId, maxValue: 8, minValue: 1, @@ -103,7 +104,7 @@ class AdvancedSettings extends HookConsumerWidget { valueNotifier: allowSelfSignedSSLCert, title: "advanced_settings_self_signed_ssl_title".tr(), subtitle: "advanced_settings_self_signed_ssl_subtitle".tr(), - onChanged: (_) => HttpOverrides.global = HttpSSLCertOverride(), + onChanged: HttpSSLOptions.applyFromSettings, ), const CustomeProxyHeaderSettings(), SslClientCertSettings(isLoggedIn: ref.read(currentUserProvider) != null), diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index 4584b7e688..72402c8d55 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class LayoutSettings extends HookConsumerWidget { const LayoutSettings({ @@ -30,7 +30,7 @@ class LayoutSettings extends HookConsumerWidget { SettingsSliderListTile( valueNotifier: tilesPerRow, text: 'theme_setting_asset_list_tiles_per_row_title' - .tr(args: ["${tilesPerRow.value}"]), + .tr(namedArgs: {'count': "${tilesPerRow.value}"}), label: "${tilesPerRow.value}", maxValue: 6, minValue: 2, diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index 4cdeb501c1..d628309050 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -164,10 +164,14 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 }; String formatBackupDelaySliderValue(int v) => switch (v) { - 0 => 'setting_notifications_notify_seconds'.tr(args: const ['5']), - 1 => 'setting_notifications_notify_seconds'.tr(args: const ['30']), - 2 => 'setting_notifications_notify_minutes'.tr(args: const ['2']), - _ => 'setting_notifications_notify_minutes'.tr(args: const ['10']), + 0 => 'setting_notifications_notify_seconds' + .tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds' + .tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes' + .tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes' + .tr(namedArgs: {'count': '10'}), }; final backupTriggerDelay = @@ -221,7 +225,9 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { SettingsSliderListTile( valueNotifier: triggerDelay, text: 'backup_controller_page_background_delay'.tr( - args: [formatBackupDelaySliderValue(triggerDelay.value)], + namedArgs: { + 'duration': formatBackupDelaySliderValue(triggerDelay.value), + }, ), maxValue: 3.0, noDivisons: 3, diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart index 5b21d9bd4d..06db78cb97 100644 --- a/mobile/lib/widgets/settings/local_storage_settings.dart +++ b/mobile/lib/widgets/settings/local_storage_settings.dart @@ -1,9 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' show useEffect, useState; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/db.provider.dart'; @@ -35,7 +35,7 @@ class LocalStorageSettings extends HookConsumerWidget { style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, ), - ).tr(args: ["${cacheItemCount.value}"]), + ).tr(namedArgs: {'count': "${cacheItemCount.value}"}), subtitle: Text( "cache_settings_duplicated_assets_subtitle", style: context.textTheme.bodyMedium?.copyWith( diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart index 6aef8f29b0..cf6745199e 100644 --- a/mobile/lib/widgets/settings/notification_setting.dart +++ b/mobile/lib/widgets/settings/notification_setting.dart @@ -4,11 +4,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/settings/settings_button_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:permission_handler/permission_handler.dart'; class NotificationSetting extends HookConsumerWidget { @@ -90,7 +90,7 @@ class NotificationSetting extends HookConsumerWidget { enabled: hasPermission, valueNotifier: sliderValue, text: 'setting_notifications_notify_failures_grace_period' - .tr(args: [formattedValue]), + .tr(namedArgs: {'duration': formattedValue}), maxValue: 5.0, noDivisons: 5, label: formattedValue, @@ -105,13 +105,14 @@ String _formatSliderValue(double v) { if (v == 0.0) { return 'setting_notifications_notify_immediately'.tr(); } else if (v == 1.0) { - return 'setting_notifications_notify_minutes'.tr(args: const ['30']); + return 'setting_notifications_notify_minutes' + .tr(namedArgs: {'count': '30'}); } else if (v == 2.0) { - return 'setting_notifications_notify_hours'.tr(args: const ['2']); + return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '2'}); } else if (v == 3.0) { - return 'setting_notifications_notify_hours'.tr(args: const ['8']); + return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '8'}); } else if (v == 4.0) { - return 'setting_notifications_notify_hours'.tr(args: const ['24']); + return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '24'}); } else { return 'setting_notifications_notify_never'.tr(); } diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index d8ea51dddd..6fdbb156d9 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; class SslClientCertSettings extends StatefulWidget { const SslClientCertSettings({super.key, required this.isLoggedIn}); @@ -103,7 +104,7 @@ class _SslClientCertSettingsState extends State { return; } cert.save(); - HttpOverrides.global = HttpSSLCertOverride(); + HttpSSLOptions.apply(); setState( () => isCertExist = true, ); @@ -152,7 +153,7 @@ class _SslClientCertSettingsState extends State { void removeCert(BuildContext context) { SSLClientCertStoreVal.delete(); - HttpOverrides.global = HttpSSLCertOverride(); + HttpSSLOptions.apply(); setState( () => isCertExist = false, ); diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index 5fdffa0537..69e763ea09 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -1,4 +1,5 @@ import 'dart:math' as math; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -6,15 +7,15 @@ import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/widgets/search/thumbnail_with_info.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/shared_link.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/providers/server_info.provider.dart'; -import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:immich_mobile/utils/url_helper.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/search/thumbnail_with_info.dart'; class SharedLinkItem extends ConsumerWidget { final SharedLink sharedLink; @@ -44,17 +45,17 @@ class SharedLinkItem extends ConsumerWidget { if (difference.inHours % 24 > 12) { dayDifference += 1; } - expiresText = - "shared_link_expires_days".tr(args: [dayDifference.toString()]); + expiresText = "shared_link_expires_days" + .tr(namedArgs: {'count': dayDifference.toString()}); } else if (difference.inHours > 0) { expiresText = "shared_link_expires_hours" - .tr(args: [difference.inHours.toString()]); + .tr(namedArgs: {'count': difference.inHours.toString()}); } else if (difference.inMinutes > 0) { expiresText = "shared_link_expires_minutes" - .tr(args: [difference.inMinutes.toString()]); + .tr(namedArgs: {'count': difference.inMinutes.toString()}); } else if (difference.inSeconds > 0) { expiresText = "shared_link_expires_seconds" - .tr(args: [difference.inSeconds.toString()]); + .tr(namedArgs: {'count': difference.inSeconds.toString()}); } } return Text( diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 0ae07e9efd..24faee28ad 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.131.3 +- API version: 1.134.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen @@ -100,7 +100,6 @@ Class | Method | HTTP request | Description *AssetsApi* | [**getAllUserAssetsByDeviceId**](doc//AssetsApi.md#getalluserassetsbydeviceid) | **GET** /assets/device/{deviceId} | getAllUserAssetsByDeviceId *AssetsApi* | [**getAssetInfo**](doc//AssetsApi.md#getassetinfo) | **GET** /assets/{id} | *AssetsApi* | [**getAssetStatistics**](doc//AssetsApi.md#getassetstatistics) | **GET** /assets/statistics | -*AssetsApi* | [**getMemoryLane**](doc//AssetsApi.md#getmemorylane) | **GET** /assets/memory-lane | *AssetsApi* | [**getRandom**](doc//AssetsApi.md#getrandom) | **GET** /assets/random | *AssetsApi* | [**playAssetVideo**](doc//AssetsApi.md#playassetvideo) | **GET** /assets/{id}/video/playback | *AssetsApi* | [**replaceAsset**](doc//AssetsApi.md#replaceasset) | **PUT** /assets/{id}/original | replaceAsset @@ -110,9 +109,15 @@ Class | Method | HTTP request | Description *AssetsApi* | [**uploadAsset**](doc//AssetsApi.md#uploadasset) | **POST** /assets | *AssetsApi* | [**viewAsset**](doc//AssetsApi.md#viewasset) | **GET** /assets/{id}/thumbnail | *AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password | +*AuthenticationApi* | [**changePinCode**](doc//AuthenticationApi.md#changepincode) | **PUT** /auth/pin-code | +*AuthenticationApi* | [**getAuthStatus**](doc//AuthenticationApi.md#getauthstatus) | **GET** /auth/status | +*AuthenticationApi* | [**lockAuthSession**](doc//AuthenticationApi.md#lockauthsession) | **POST** /auth/session/lock | *AuthenticationApi* | [**login**](doc//AuthenticationApi.md#login) | **POST** /auth/login | *AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout | +*AuthenticationApi* | [**resetPinCode**](doc//AuthenticationApi.md#resetpincode) | **DELETE** /auth/pin-code | +*AuthenticationApi* | [**setupPinCode**](doc//AuthenticationApi.md#setuppincode) | **POST** /auth/pin-code | *AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up | +*AuthenticationApi* | [**unlockAuthSession**](doc//AuthenticationApi.md#unlockauthsession) | **POST** /auth/session/unlock | *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken | *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random | *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive | @@ -122,9 +127,6 @@ Class | Method | HTTP request | Description *FacesApi* | [**deleteFace**](doc//FacesApi.md#deleteface) | **DELETE** /faces/{id} | *FacesApi* | [**getFaces**](doc//FacesApi.md#getfaces) | **GET** /faces | *FacesApi* | [**reassignFacesById**](doc//FacesApi.md#reassignfacesbyid) | **PUT** /faces/{id} | -*FileReportsApi* | [**fixAuditFiles**](doc//FileReportsApi.md#fixauditfiles) | **POST** /reports/fix | -*FileReportsApi* | [**getAuditFiles**](doc//FileReportsApi.md#getauditfiles) | **GET** /reports | -*FileReportsApi* | [**getFileChecksums**](doc//FileReportsApi.md#getfilechecksums) | **POST** /reports/checksum | *JobsApi* | [**createJob**](doc//JobsApi.md#createjob) | **POST** /jobs | *JobsApi* | [**getAllJobsStatus**](doc//JobsApi.md#getalljobsstatus) | **GET** /jobs | *JobsApi* | [**sendJobCommand**](doc//JobsApi.md#sendjobcommand) | **PUT** /jobs/{id} | @@ -145,8 +147,15 @@ Class | Method | HTTP request | Description *MemoriesApi* | [**removeMemoryAssets**](doc//MemoriesApi.md#removememoryassets) | **DELETE** /memories/{id}/assets | *MemoriesApi* | [**searchMemories**](doc//MemoriesApi.md#searchmemories) | **GET** /memories | *MemoriesApi* | [**updateMemory**](doc//MemoriesApi.md#updatememory) | **PUT** /memories/{id} | -*NotificationsApi* | [**getNotificationTemplate**](doc//NotificationsApi.md#getnotificationtemplate) | **POST** /notifications/templates/{name} | -*NotificationsApi* | [**sendTestEmail**](doc//NotificationsApi.md#sendtestemail) | **POST** /notifications/test-email | +*NotificationsApi* | [**deleteNotification**](doc//NotificationsApi.md#deletenotification) | **DELETE** /notifications/{id} | +*NotificationsApi* | [**deleteNotifications**](doc//NotificationsApi.md#deletenotifications) | **DELETE** /notifications | +*NotificationsApi* | [**getNotification**](doc//NotificationsApi.md#getnotification) | **GET** /notifications/{id} | +*NotificationsApi* | [**getNotifications**](doc//NotificationsApi.md#getnotifications) | **GET** /notifications | +*NotificationsApi* | [**updateNotification**](doc//NotificationsApi.md#updatenotification) | **PUT** /notifications/{id} | +*NotificationsApi* | [**updateNotifications**](doc//NotificationsApi.md#updatenotifications) | **PUT** /notifications | +*NotificationsAdminApi* | [**createNotification**](doc//NotificationsAdminApi.md#createnotification) | **POST** /admin/notifications | +*NotificationsAdminApi* | [**getNotificationTemplateAdmin**](doc//NotificationsAdminApi.md#getnotificationtemplateadmin) | **POST** /admin/notifications/templates/{name} | +*NotificationsAdminApi* | [**sendTestEmailAdmin**](doc//NotificationsAdminApi.md#sendtestemailadmin) | **POST** /admin/notifications/test-email | *OAuthApi* | [**finishOAuth**](doc//OAuthApi.md#finishoauth) | **POST** /oauth/callback | *OAuthApi* | [**linkOAuthAccount**](doc//OAuthApi.md#linkoauthaccount) | **POST** /oauth/link | *OAuthApi* | [**redirectOAuthToMobile**](doc//OAuthApi.md#redirectoauthtomobile) | **GET** /oauth/mobile-redirect | @@ -183,12 +192,15 @@ Class | Method | HTTP request | Description *ServerApi* | [**getStorage**](doc//ServerApi.md#getstorage) | **GET** /server/storage | *ServerApi* | [**getSupportedMediaTypes**](doc//ServerApi.md#getsupportedmediatypes) | **GET** /server/media-types | *ServerApi* | [**getTheme**](doc//ServerApi.md#gettheme) | **GET** /server/theme | +*ServerApi* | [**getVersionCheck**](doc//ServerApi.md#getversioncheck) | **GET** /server/version-check | *ServerApi* | [**getVersionHistory**](doc//ServerApi.md#getversionhistory) | **GET** /server/version-history | *ServerApi* | [**pingServer**](doc//ServerApi.md#pingserver) | **GET** /server/ping | *ServerApi* | [**setServerLicense**](doc//ServerApi.md#setserverlicense) | **PUT** /server/license | +*SessionsApi* | [**createSession**](doc//SessionsApi.md#createsession) | **POST** /sessions | *SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions | *SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} | *SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions | +*SessionsApi* | [**lockSession**](doc//SessionsApi.md#locksession) | **POST** /sessions/{id}/lock | *SharedLinksApi* | [**addSharedLinkAssets**](doc//SharedLinksApi.md#addsharedlinkassets) | **PUT** /shared-links/{id}/assets | *SharedLinksApi* | [**createSharedLink**](doc//SharedLinksApi.md#createsharedlink) | **POST** /shared-links | *SharedLinksApi* | [**getAllSharedLinks**](doc//SharedLinksApi.md#getallsharedlinks) | **GET** /shared-links | @@ -215,6 +227,7 @@ Class | Method | HTTP request | Description *SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config | *SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding | *SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state | +*SystemMetadataApi* | [**getVersionCheckState**](doc//SystemMetadataApi.md#getversioncheckstate) | **GET** /system-metadata/version-check-state | *SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding | *TagsApi* | [**bulkTagAssets**](doc//TagsApi.md#bulktagassets) | **PUT** /tags/assets | *TagsApi* | [**createTag**](doc//TagsApi.md#createtag) | **POST** /tags | @@ -246,6 +259,7 @@ Class | Method | HTTP request | Description *UsersAdminApi* | [**deleteUserAdmin**](doc//UsersAdminApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} | *UsersAdminApi* | [**getUserAdmin**](doc//UsersAdminApi.md#getuseradmin) | **GET** /admin/users/{id} | *UsersAdminApi* | [**getUserPreferencesAdmin**](doc//UsersAdminApi.md#getuserpreferencesadmin) | **GET** /admin/users/{id}/preferences | +*UsersAdminApi* | [**getUserStatisticsAdmin**](doc//UsersAdminApi.md#getuserstatisticsadmin) | **GET** /admin/users/{id}/statistics | *UsersAdminApi* | [**restoreUserAdmin**](doc//UsersAdminApi.md#restoreuseradmin) | **POST** /admin/users/{id}/restore | *UsersAdminApi* | [**searchUsersAdmin**](doc//UsersAdminApi.md#searchusersadmin) | **GET** /admin/users | *UsersAdminApi* | [**updateUserAdmin**](doc//UsersAdminApi.md#updateuseradmin) | **PUT** /admin/users/{id} | @@ -299,8 +313,9 @@ Class | Method | HTTP request | Description - [AssetStackResponseDto](doc//AssetStackResponseDto.md) - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md) - [AssetTypeEnum](doc//AssetTypeEnum.md) + - [AssetVisibility](doc//AssetVisibility.md) - [AudioCodec](doc//AudioCodec.md) - - [AvatarResponse](doc//AvatarResponse.md) + - [AuthStatusResponseDto](doc//AuthStatusResponseDto.md) - [AvatarUpdate](doc//AvatarUpdate.md) - [BulkIdResponseDto](doc//BulkIdResponseDto.md) - [BulkIdsDto](doc//BulkIdsDto.md) @@ -326,11 +341,6 @@ Class | Method | HTTP request | Description - [ExifResponseDto](doc//ExifResponseDto.md) - [FaceDto](doc//FaceDto.md) - [FacialRecognitionConfig](doc//FacialRecognitionConfig.md) - - [FileChecksumDto](doc//FileChecksumDto.md) - - [FileChecksumResponseDto](doc//FileChecksumResponseDto.md) - - [FileReportDto](doc//FileReportDto.md) - - [FileReportFixDto](doc//FileReportFixDto.md) - - [FileReportItemDto](doc//FileReportItemDto.md) - [FoldersResponse](doc//FoldersResponse.md) - [FoldersUpdate](doc//FoldersUpdate.md) - [ImageFormat](doc//ImageFormat.md) @@ -355,20 +365,25 @@ Class | Method | HTTP request | Description - [MemoriesResponse](doc//MemoriesResponse.md) - [MemoriesUpdate](doc//MemoriesUpdate.md) - [MemoryCreateDto](doc//MemoryCreateDto.md) - - [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md) - [MemoryResponseDto](doc//MemoryResponseDto.md) - [MemoryType](doc//MemoryType.md) - [MemoryUpdateDto](doc//MemoryUpdateDto.md) - [MergePersonDto](doc//MergePersonDto.md) - [MetadataSearchDto](doc//MetadataSearchDto.md) + - [NotificationCreateDto](doc//NotificationCreateDto.md) + - [NotificationDeleteAllDto](doc//NotificationDeleteAllDto.md) + - [NotificationDto](doc//NotificationDto.md) + - [NotificationLevel](doc//NotificationLevel.md) + - [NotificationType](doc//NotificationType.md) + - [NotificationUpdateAllDto](doc//NotificationUpdateAllDto.md) + - [NotificationUpdateDto](doc//NotificationUpdateDto.md) - [OAuthAuthorizeResponseDto](doc//OAuthAuthorizeResponseDto.md) - [OAuthCallbackDto](doc//OAuthCallbackDto.md) - [OAuthConfigDto](doc//OAuthConfigDto.md) + - [OAuthTokenEndpointAuthMethod](doc//OAuthTokenEndpointAuthMethod.md) - [OnThisDayDto](doc//OnThisDayDto.md) - [PartnerDirection](doc//PartnerDirection.md) - [PartnerResponseDto](doc//PartnerResponseDto.md) - - [PathEntityType](doc//PathEntityType.md) - - [PathType](doc//PathType.md) - [PeopleResponse](doc//PeopleResponse.md) - [PeopleResponseDto](doc//PeopleResponseDto.md) - [PeopleUpdate](doc//PeopleUpdate.md) @@ -380,6 +395,9 @@ Class | Method | HTTP request | Description - [PersonStatisticsResponseDto](doc//PersonStatisticsResponseDto.md) - [PersonUpdateDto](doc//PersonUpdateDto.md) - [PersonWithFacesResponseDto](doc//PersonWithFacesResponseDto.md) + - [PinCodeChangeDto](doc//PinCodeChangeDto.md) + - [PinCodeResetDto](doc//PinCodeResetDto.md) + - [PinCodeSetupDto](doc//PinCodeSetupDto.md) - [PlacesResponseDto](doc//PlacesResponseDto.md) - [PurchaseResponse](doc//PurchaseResponse.md) - [PurchaseUpdate](doc//PurchaseUpdate.md) @@ -408,7 +426,10 @@ Class | Method | HTTP request | Description - [ServerThemeDto](doc//ServerThemeDto.md) - [ServerVersionHistoryResponseDto](doc//ServerVersionHistoryResponseDto.md) - [ServerVersionResponseDto](doc//ServerVersionResponseDto.md) + - [SessionCreateDto](doc//SessionCreateDto.md) + - [SessionCreateResponseDto](doc//SessionCreateResponseDto.md) - [SessionResponseDto](doc//SessionResponseDto.md) + - [SessionUnlockDto](doc//SessionUnlockDto.md) - [SharedLinkCreateDto](doc//SharedLinkCreateDto.md) - [SharedLinkEditDto](doc//SharedLinkEditDto.md) - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) @@ -424,6 +445,10 @@ Class | Method | HTTP request | Description - [SyncAckDeleteDto](doc//SyncAckDeleteDto.md) - [SyncAckDto](doc//SyncAckDto.md) - [SyncAckSetDto](doc//SyncAckSetDto.md) + - [SyncAlbumDeleteV1](doc//SyncAlbumDeleteV1.md) + - [SyncAlbumUserDeleteV1](doc//SyncAlbumUserDeleteV1.md) + - [SyncAlbumUserV1](doc//SyncAlbumUserV1.md) + - [SyncAlbumV1](doc//SyncAlbumV1.md) - [SyncAssetDeleteV1](doc//SyncAssetDeleteV1.md) - [SyncAssetExifV1](doc//SyncAssetExifV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) @@ -475,8 +500,8 @@ Class | Method | HTTP request | Description - [TemplateDto](doc//TemplateDto.md) - [TemplateResponseDto](doc//TemplateResponseDto.md) - [TestEmailResponseDto](doc//TestEmailResponseDto.md) - - [TimeBucketResponseDto](doc//TimeBucketResponseDto.md) - - [TimeBucketSize](doc//TimeBucketSize.md) + - [TimeBucketAssetResponseDto](doc//TimeBucketAssetResponseDto.md) + - [TimeBucketsResponseDto](doc//TimeBucketsResponseDto.md) - [ToneMapping](doc//ToneMapping.md) - [TranscodeHWAccel](doc//TranscodeHWAccel.md) - [TranscodePolicy](doc//TranscodePolicy.md) @@ -502,6 +527,7 @@ Class | Method | HTTP request | Description - [ValidateLibraryDto](doc//ValidateLibraryDto.md) - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md) - [ValidateLibraryResponseDto](doc//ValidateLibraryResponseDto.md) + - [VersionCheckStateResponseDto](doc//VersionCheckStateResponseDto.md) - [VideoCodec](doc//VideoCodec.md) - [VideoContainer](doc//VideoContainer.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 3986362c96..d3a342db6c 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -39,12 +39,12 @@ part 'api/deprecated_api.dart'; part 'api/download_api.dart'; part 'api/duplicates_api.dart'; part 'api/faces_api.dart'; -part 'api/file_reports_api.dart'; part 'api/jobs_api.dart'; part 'api/libraries_api.dart'; part 'api/map_api.dart'; part 'api/memories_api.dart'; part 'api/notifications_api.dart'; +part 'api/notifications_admin_api.dart'; part 'api/o_auth_api.dart'; part 'api/partners_api.dart'; part 'api/people_api.dart'; @@ -106,8 +106,9 @@ part 'model/asset_response_dto.dart'; part 'model/asset_stack_response_dto.dart'; part 'model/asset_stats_response_dto.dart'; part 'model/asset_type_enum.dart'; +part 'model/asset_visibility.dart'; part 'model/audio_codec.dart'; -part 'model/avatar_response.dart'; +part 'model/auth_status_response_dto.dart'; part 'model/avatar_update.dart'; part 'model/bulk_id_response_dto.dart'; part 'model/bulk_ids_dto.dart'; @@ -133,11 +134,6 @@ part 'model/email_notifications_update.dart'; part 'model/exif_response_dto.dart'; part 'model/face_dto.dart'; part 'model/facial_recognition_config.dart'; -part 'model/file_checksum_dto.dart'; -part 'model/file_checksum_response_dto.dart'; -part 'model/file_report_dto.dart'; -part 'model/file_report_fix_dto.dart'; -part 'model/file_report_item_dto.dart'; part 'model/folders_response.dart'; part 'model/folders_update.dart'; part 'model/image_format.dart'; @@ -162,20 +158,25 @@ part 'model/map_reverse_geocode_response_dto.dart'; part 'model/memories_response.dart'; part 'model/memories_update.dart'; part 'model/memory_create_dto.dart'; -part 'model/memory_lane_response_dto.dart'; part 'model/memory_response_dto.dart'; part 'model/memory_type.dart'; part 'model/memory_update_dto.dart'; part 'model/merge_person_dto.dart'; part 'model/metadata_search_dto.dart'; +part 'model/notification_create_dto.dart'; +part 'model/notification_delete_all_dto.dart'; +part 'model/notification_dto.dart'; +part 'model/notification_level.dart'; +part 'model/notification_type.dart'; +part 'model/notification_update_all_dto.dart'; +part 'model/notification_update_dto.dart'; part 'model/o_auth_authorize_response_dto.dart'; part 'model/o_auth_callback_dto.dart'; part 'model/o_auth_config_dto.dart'; +part 'model/o_auth_token_endpoint_auth_method.dart'; part 'model/on_this_day_dto.dart'; part 'model/partner_direction.dart'; part 'model/partner_response_dto.dart'; -part 'model/path_entity_type.dart'; -part 'model/path_type.dart'; part 'model/people_response.dart'; part 'model/people_response_dto.dart'; part 'model/people_update.dart'; @@ -187,6 +188,9 @@ part 'model/person_response_dto.dart'; part 'model/person_statistics_response_dto.dart'; part 'model/person_update_dto.dart'; part 'model/person_with_faces_response_dto.dart'; +part 'model/pin_code_change_dto.dart'; +part 'model/pin_code_reset_dto.dart'; +part 'model/pin_code_setup_dto.dart'; part 'model/places_response_dto.dart'; part 'model/purchase_response.dart'; part 'model/purchase_update.dart'; @@ -215,7 +219,10 @@ part 'model/server_storage_response_dto.dart'; part 'model/server_theme_dto.dart'; part 'model/server_version_history_response_dto.dart'; part 'model/server_version_response_dto.dart'; +part 'model/session_create_dto.dart'; +part 'model/session_create_response_dto.dart'; part 'model/session_response_dto.dart'; +part 'model/session_unlock_dto.dart'; part 'model/shared_link_create_dto.dart'; part 'model/shared_link_edit_dto.dart'; part 'model/shared_link_response_dto.dart'; @@ -231,6 +238,10 @@ part 'model/stack_update_dto.dart'; part 'model/sync_ack_delete_dto.dart'; part 'model/sync_ack_dto.dart'; part 'model/sync_ack_set_dto.dart'; +part 'model/sync_album_delete_v1.dart'; +part 'model/sync_album_user_delete_v1.dart'; +part 'model/sync_album_user_v1.dart'; +part 'model/sync_album_v1.dart'; part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_v1.dart'; @@ -282,8 +293,8 @@ part 'model/tags_update.dart'; part 'model/template_dto.dart'; part 'model/template_response_dto.dart'; part 'model/test_email_response_dto.dart'; -part 'model/time_bucket_response_dto.dart'; -part 'model/time_bucket_size.dart'; +part 'model/time_bucket_asset_response_dto.dart'; +part 'model/time_buckets_response_dto.dart'; part 'model/tone_mapping.dart'; part 'model/transcode_hw_accel.dart'; part 'model/transcode_policy.dart'; @@ -309,6 +320,7 @@ part 'model/validate_access_token_response_dto.dart'; part 'model/validate_library_dto.dart'; part 'model/validate_library_import_path_response_dto.dart'; part 'model/validate_library_response_dto.dart'; +part 'model/version_check_state_response_dto.dart'; part 'model/video_codec.dart'; part 'model/video_container.dart'; diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index f52c70b37f..06965e1f8b 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -342,12 +342,12 @@ class AssetsApi { /// Performs an HTTP 'GET /assets/statistics' operation and returns the [Response]. /// Parameters: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: - Future getAssetStatisticsWithHttpInfo({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async { + /// + /// * [AssetVisibility] visibility: + Future getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/statistics'; @@ -358,15 +358,15 @@ class AssetsApi { final headerParams = {}; final formParams = {}; - if (isArchived != null) { - queryParams.addAll(_queryParams('', 'isArchived', isArchived)); - } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } if (isTrashed != null) { queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } const contentTypes = []; @@ -384,13 +384,13 @@ class AssetsApi { /// Parameters: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: - Future getAssetStatistics({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async { - final response = await getAssetStatisticsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, ); + /// + /// * [AssetVisibility] visibility: + Future getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { + final response = await getAssetStatisticsWithHttpInfo( isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -404,63 +404,6 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/memory-lane' operation and returns the [Response]. - /// Parameters: - /// - /// * [int] day (required): - /// - /// * [int] month (required): - Future getMemoryLaneWithHttpInfo(int day, int month,) async { - // ignore: prefer_const_declarations - final apiPath = r'/assets/memory-lane'; - - // ignore: prefer_final_locals - Object? postBody; - - final queryParams = []; - final headerParams = {}; - final formParams = {}; - - queryParams.addAll(_queryParams('', 'day', day)); - queryParams.addAll(_queryParams('', 'month', month)); - - const contentTypes = []; - - - return apiClient.invokeAPI( - apiPath, - 'GET', - queryParams, - postBody, - headerParams, - formParams, - contentTypes.isEmpty ? null : contentTypes.first, - ); - } - - /// Parameters: - /// - /// * [int] day (required): - /// - /// * [int] month (required): - Future?> getMemoryLane(int day, int month,) async { - final response = await getMemoryLaneWithHttpInfo(day, month,); - if (response.statusCode >= HttpStatus.badRequest) { - throw ApiException(response.statusCode, await _decodeBodyBytes(response)); - } - // When a remote server returns no body with a status of 204, we shall not decode it. - // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" - // FormatException when trying to decode an empty string. - if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() - .toList(growable: false); - - } - return null; - } - /// This property was deprecated in v1.116.0 /// /// Note: This method returns the HTTP [Response]. @@ -845,16 +788,14 @@ class AssetsApi { /// /// * [String] duration: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// - /// * [bool] isVisible: - /// /// * [String] livePhotoVideoId: /// /// * [MultipartFile] sidecarData: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isVisible, String? livePhotoVideoId, MultipartFile? sidecarData, }) async { + /// + /// * [AssetVisibility] visibility: + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -902,18 +843,10 @@ class AssetsApi { hasFields = true; mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt); } - if (isArchived != null) { - hasFields = true; - mp.fields[r'isArchived'] = parameterToString(isArchived); - } if (isFavorite != null) { hasFields = true; mp.fields[r'isFavorite'] = parameterToString(isFavorite); } - if (isVisible != null) { - hasFields = true; - mp.fields[r'isVisible'] = parameterToString(isVisible); - } if (livePhotoVideoId != null) { hasFields = true; mp.fields[r'livePhotoVideoId'] = parameterToString(livePhotoVideoId); @@ -923,6 +856,10 @@ class AssetsApi { mp.fields[r'sidecarData'] = sidecarData.field; mp.files.add(sidecarData); } + if (visibility != null) { + hasFields = true; + mp.fields[r'visibility'] = parameterToString(visibility); + } if (hasFields) { postBody = mp; } @@ -957,17 +894,15 @@ class AssetsApi { /// /// * [String] duration: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// - /// * [bool] isVisible: - /// /// * [String] livePhotoVideoId: /// /// * [MultipartFile] sidecarData: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isVisible, String? livePhotoVideoId, MultipartFile? sidecarData, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, isArchived: isArchived, isFavorite: isFavorite, isVisible: isVisible, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, ); + /// + /// * [AssetVisibility] visibility: + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index bf987f441e..5482a9fc51 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -63,6 +63,119 @@ class AuthenticationApi { return null; } + /// Performs an HTTP 'PUT /auth/pin-code' operation and returns the [Response]. + /// Parameters: + /// + /// * [PinCodeChangeDto] pinCodeChangeDto (required): + Future changePinCodeWithHttpInfo(PinCodeChangeDto pinCodeChangeDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/pin-code'; + + // ignore: prefer_final_locals + Object? postBody = pinCodeChangeDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [PinCodeChangeDto] pinCodeChangeDto (required): + Future changePinCode(PinCodeChangeDto pinCodeChangeDto,) async { + final response = await changePinCodeWithHttpInfo(pinCodeChangeDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'GET /auth/status' operation and returns the [Response]. + Future getAuthStatusWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/status'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getAuthStatus() async { + final response = await getAuthStatusWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AuthStatusResponseDto',) as AuthStatusResponseDto; + + } + return null; + } + + /// Performs an HTTP 'POST /auth/session/lock' operation and returns the [Response]. + Future lockAuthSessionWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/session/lock'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future lockAuthSession() async { + final response = await lockAuthSessionWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'POST /auth/login' operation and returns the [Response]. /// Parameters: /// @@ -151,6 +264,84 @@ class AuthenticationApi { return null; } + /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. + /// Parameters: + /// + /// * [PinCodeResetDto] pinCodeResetDto (required): + Future resetPinCodeWithHttpInfo(PinCodeResetDto pinCodeResetDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/pin-code'; + + // ignore: prefer_final_locals + Object? postBody = pinCodeResetDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [PinCodeResetDto] pinCodeResetDto (required): + Future resetPinCode(PinCodeResetDto pinCodeResetDto,) async { + final response = await resetPinCodeWithHttpInfo(pinCodeResetDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'POST /auth/pin-code' operation and returns the [Response]. + /// Parameters: + /// + /// * [PinCodeSetupDto] pinCodeSetupDto (required): + Future setupPinCodeWithHttpInfo(PinCodeSetupDto pinCodeSetupDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/pin-code'; + + // ignore: prefer_final_locals + Object? postBody = pinCodeSetupDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [PinCodeSetupDto] pinCodeSetupDto (required): + Future setupPinCode(PinCodeSetupDto pinCodeSetupDto,) async { + final response = await setupPinCodeWithHttpInfo(pinCodeSetupDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'POST /auth/admin-sign-up' operation and returns the [Response]. /// Parameters: /// @@ -198,6 +389,45 @@ class AuthenticationApi { return null; } + /// Performs an HTTP 'POST /auth/session/unlock' operation and returns the [Response]. + /// Parameters: + /// + /// * [SessionUnlockDto] sessionUnlockDto (required): + Future unlockAuthSessionWithHttpInfo(SessionUnlockDto sessionUnlockDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/auth/session/unlock'; + + // ignore: prefer_final_locals + Object? postBody = sessionUnlockDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [SessionUnlockDto] sessionUnlockDto (required): + Future unlockAuthSession(SessionUnlockDto sessionUnlockDto,) async { + final response = await unlockAuthSessionWithHttpInfo(sessionUnlockDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'POST /auth/validateToken' operation and returns the [Response]. Future validateAccessTokenWithHttpInfo() async { // ignore: prefer_const_declarations diff --git a/mobile/openapi/lib/api/file_reports_api.dart b/mobile/openapi/lib/api/notifications_admin_api.dart similarity index 51% rename from mobile/openapi/lib/api/file_reports_api.dart rename to mobile/openapi/lib/api/notifications_admin_api.dart index 73b3feaedb..409683a950 100644 --- a/mobile/openapi/lib/api/file_reports_api.dart +++ b/mobile/openapi/lib/api/notifications_admin_api.dart @@ -11,21 +11,21 @@ part of openapi.api; -class FileReportsApi { - FileReportsApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; +class NotificationsAdminApi { + NotificationsAdminApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; final ApiClient apiClient; - /// Performs an HTTP 'POST /reports/fix' operation and returns the [Response]. + /// Performs an HTTP 'POST /admin/notifications' operation and returns the [Response]. /// Parameters: /// - /// * [FileReportFixDto] fileReportFixDto (required): - Future fixAuditFilesWithHttpInfo(FileReportFixDto fileReportFixDto,) async { + /// * [NotificationCreateDto] notificationCreateDto (required): + Future createNotificationWithHttpInfo(NotificationCreateDto notificationCreateDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/reports/fix'; + final apiPath = r'/admin/notifications'; // ignore: prefer_final_locals - Object? postBody = fileReportFixDto; + Object? postBody = notificationCreateDto; final queryParams = []; final headerParams = {}; @@ -47,42 +47,9 @@ class FileReportsApi { /// Parameters: /// - /// * [FileReportFixDto] fileReportFixDto (required): - Future fixAuditFiles(FileReportFixDto fileReportFixDto,) async { - final response = await fixAuditFilesWithHttpInfo(fileReportFixDto,); - if (response.statusCode >= HttpStatus.badRequest) { - throw ApiException(response.statusCode, await _decodeBodyBytes(response)); - } - } - - /// Performs an HTTP 'GET /reports' operation and returns the [Response]. - Future getAuditFilesWithHttpInfo() async { - // ignore: prefer_const_declarations - final apiPath = r'/reports'; - - // ignore: prefer_final_locals - Object? postBody; - - final queryParams = []; - final headerParams = {}; - final formParams = {}; - - const contentTypes = []; - - - return apiClient.invokeAPI( - apiPath, - 'GET', - queryParams, - postBody, - headerParams, - formParams, - contentTypes.isEmpty ? null : contentTypes.first, - ); - } - - Future getAuditFiles() async { - final response = await getAuditFilesWithHttpInfo(); + /// * [NotificationCreateDto] notificationCreateDto (required): + Future createNotification(NotificationCreateDto notificationCreateDto,) async { + final response = await createNotificationWithHttpInfo(notificationCreateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -90,22 +57,25 @@ class FileReportsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'FileReportDto',) as FileReportDto; + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; } return null; } - /// Performs an HTTP 'POST /reports/checksum' operation and returns the [Response]. + /// Performs an HTTP 'POST /admin/notifications/templates/{name}' operation and returns the [Response]. /// Parameters: /// - /// * [FileChecksumDto] fileChecksumDto (required): - Future getFileChecksumsWithHttpInfo(FileChecksumDto fileChecksumDto,) async { + /// * [String] name (required): + /// + /// * [TemplateDto] templateDto (required): + Future getNotificationTemplateAdminWithHttpInfo(String name, TemplateDto templateDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/reports/checksum'; + final apiPath = r'/admin/notifications/templates/{name}' + .replaceAll('{name}', name); // ignore: prefer_final_locals - Object? postBody = fileChecksumDto; + Object? postBody = templateDto; final queryParams = []; final headerParams = {}; @@ -127,9 +97,11 @@ class FileReportsApi { /// Parameters: /// - /// * [FileChecksumDto] fileChecksumDto (required): - Future?> getFileChecksums(FileChecksumDto fileChecksumDto,) async { - final response = await getFileChecksumsWithHttpInfo(fileChecksumDto,); + /// * [String] name (required): + /// + /// * [TemplateDto] templateDto (required): + Future getNotificationTemplateAdmin(String name, TemplateDto templateDto,) async { + final response = await getNotificationTemplateAdminWithHttpInfo(name, templateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -137,11 +109,55 @@ class FileReportsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() - .toList(growable: false); + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TemplateResponseDto',) as TemplateResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /admin/notifications/test-email' operation and returns the [Response]. + /// Parameters: + /// + /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): + Future sendTestEmailAdminWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/notifications/test-email'; + + // ignore: prefer_final_locals + Object? postBody = systemConfigSmtpDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): + Future sendTestEmailAdmin(SystemConfigSmtpDto systemConfigSmtpDto,) async { + final response = await sendTestEmailAdminWithHttpInfo(systemConfigSmtpDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TestEmailResponseDto',) as TestEmailResponseDto; + } return null; } diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 518a1baa4a..501cc70a29 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,30 +16,28 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /notifications/templates/{name}' operation and returns the [Response]. + /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. /// Parameters: /// - /// * [String] name (required): - /// - /// * [TemplateDto] templateDto (required): - Future getNotificationTemplateWithHttpInfo(String name, TemplateDto templateDto,) async { + /// * [String] id (required): + Future deleteNotificationWithHttpInfo(String id,) async { // ignore: prefer_const_declarations - final apiPath = r'/notifications/templates/{name}' - .replaceAll('{name}', name); + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = templateDto; + Object? postBody; final queryParams = []; final headerParams = {}; final formParams = {}; - const contentTypes = ['application/json']; + const contentTypes = []; return apiClient.invokeAPI( apiPath, - 'POST', + 'DELETE', queryParams, postBody, headerParams, @@ -50,34 +48,24 @@ class NotificationsApi { /// Parameters: /// - /// * [String] name (required): - /// - /// * [TemplateDto] templateDto (required): - Future getNotificationTemplate(String name, TemplateDto templateDto,) async { - final response = await getNotificationTemplateWithHttpInfo(name, templateDto,); + /// * [String] id (required): + Future deleteNotification(String id,) async { + final response = await deleteNotificationWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } - // When a remote server returns no body with a status of 204, we shall not decode it. - // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" - // FormatException when trying to decode an empty string. - if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TemplateResponseDto',) as TemplateResponseDto; - - } - return null; } - /// Performs an HTTP 'POST /notifications/test-email' operation and returns the [Response]. + /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. /// Parameters: /// - /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future sendTestEmailWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto,) async { + /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): + Future deleteNotificationsWithHttpInfo(NotificationDeleteAllDto notificationDeleteAllDto,) async { // ignore: prefer_const_declarations - final apiPath = r'/notifications/test-email'; + final apiPath = r'/notifications'; // ignore: prefer_final_locals - Object? postBody = systemConfigSmtpDto; + Object? postBody = notificationDeleteAllDto; final queryParams = []; final headerParams = {}; @@ -88,7 +76,7 @@ class NotificationsApi { return apiClient.invokeAPI( apiPath, - 'POST', + 'DELETE', queryParams, postBody, headerParams, @@ -99,9 +87,49 @@ class NotificationsApi { /// Parameters: /// - /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future sendTestEmail(SystemConfigSmtpDto systemConfigSmtpDto,) async { - final response = await sendTestEmailWithHttpInfo(systemConfigSmtpDto,); + /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): + Future deleteNotifications(NotificationDeleteAllDto notificationDeleteAllDto,) async { + final response = await deleteNotificationsWithHttpInfo(notificationDeleteAllDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future getNotificationWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future getNotification(String id,) async { + final response = await getNotificationWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -109,9 +137,175 @@ class NotificationsApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TestEmailResponseDto',) as TestEmailResponseDto; + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; } return null; } + + /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id: + /// + /// * [NotificationLevel] level: + /// + /// * [NotificationType] type: + /// + /// * [bool] unread: + Future getNotificationsWithHttpInfo({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (id != null) { + queryParams.addAll(_queryParams('', 'id', id)); + } + if (level != null) { + queryParams.addAll(_queryParams('', 'level', level)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (unread != null) { + queryParams.addAll(_queryParams('', 'unread', unread)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id: + /// + /// * [NotificationLevel] level: + /// + /// * [NotificationType] type: + /// + /// * [bool] unread: + Future?> getNotifications({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { + final response = await getNotificationsWithHttpInfo( id: id, level: level, type: type, unread: unread, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [NotificationUpdateDto] notificationUpdateDto (required): + Future updateNotificationWithHttpInfo(String id, NotificationUpdateDto notificationUpdateDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = notificationUpdateDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [NotificationUpdateDto] notificationUpdateDto (required): + Future updateNotification(String id, NotificationUpdateDto notificationUpdateDto,) async { + final response = await updateNotificationWithHttpInfo(id, notificationUpdateDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'NotificationDto',) as NotificationDto; + + } + return null; + } + + /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// Parameters: + /// + /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): + Future updateNotificationsWithHttpInfo(NotificationUpdateAllDto notificationUpdateAllDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/notifications'; + + // ignore: prefer_final_locals + Object? postBody = notificationUpdateAllDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): + Future updateNotifications(NotificationUpdateAllDto notificationUpdateAllDto,) async { + final response = await updateNotificationsWithHttpInfo(notificationUpdateAllDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } } diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 629949db32..a0fd54f3d2 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -418,6 +418,47 @@ class ServerApi { return null; } + /// Performs an HTTP 'GET /server/version-check' operation and returns the [Response]. + Future getVersionCheckWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/server/version-check'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getVersionCheck() async { + final response = await getVersionCheckWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto; + + } + return null; + } + /// Performs an HTTP 'GET /server/version-history' operation and returns the [Response]. Future getVersionHistoryWithHttpInfo() async { // ignore: prefer_const_declarations diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index 203f801b72..3228d31e91 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -16,6 +16,53 @@ class SessionsApi { final ApiClient apiClient; + /// Performs an HTTP 'POST /sessions' operation and returns the [Response]. + /// Parameters: + /// + /// * [SessionCreateDto] sessionCreateDto (required): + Future createSessionWithHttpInfo(SessionCreateDto sessionCreateDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/sessions'; + + // ignore: prefer_final_locals + Object? postBody = sessionCreateDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [SessionCreateDto] sessionCreateDto (required): + Future createSession(SessionCreateDto sessionCreateDto,) async { + final response = await createSessionWithHttpInfo(sessionCreateDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SessionCreateResponseDto',) as SessionCreateResponseDto; + + } + return null; + } + /// Performs an HTTP 'DELETE /sessions' operation and returns the [Response]. Future deleteAllSessionsWithHttpInfo() async { // ignore: prefer_const_declarations @@ -132,4 +179,44 @@ class SessionsApi { } return null; } + + /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + Future lockSessionWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/sessions/{id}/lock' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + Future lockSession(String id,) async { + final response = await lockSessionWithHttpInfo(id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } } diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 3bd8bddcac..3fcceb8e42 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -98,6 +98,47 @@ class SystemMetadataApi { return null; } + /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response]. + Future getVersionCheckStateWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/system-metadata/version-check-state'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getVersionCheckState() async { + final response = await getVersionCheckStateWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'VersionCheckStateResponseDto',) as VersionCheckStateResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response]. /// Parameters: /// diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 7ea7189b00..399e7bde86 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -19,14 +19,10 @@ class TimelineApi { /// Performs an HTTP 'GET /timeline/bucket' operation and returns the [Response]. /// Parameters: /// - /// * [TimeBucketSize] size (required): - /// /// * [String] timeBucket (required): /// /// * [String] albumId: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: @@ -35,16 +31,22 @@ class TimelineApi { /// /// * [AssetOrder] order: /// + /// * [num] page: + /// + /// * [num] pageSize: + /// /// * [String] personId: /// /// * [String] tagId: /// /// * [String] userId: /// + /// * [AssetVisibility] visibility: + /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future getTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, num? page, num? pageSize, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -58,9 +60,6 @@ class TimelineApi { if (albumId != null) { queryParams.addAll(_queryParams('', 'albumId', albumId)); } - if (isArchived != null) { - queryParams.addAll(_queryParams('', 'isArchived', isArchived)); - } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } @@ -73,10 +72,15 @@ class TimelineApi { if (order != null) { queryParams.addAll(_queryParams('', 'order', order)); } + if (page != null) { + queryParams.addAll(_queryParams('', 'page', page)); + } + if (pageSize != null) { + queryParams.addAll(_queryParams('', 'pageSize', pageSize)); + } if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } - queryParams.addAll(_queryParams('', 'size', size)); if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -84,6 +88,9 @@ class TimelineApi { if (userId != null) { queryParams.addAll(_queryParams('', 'userId', userId)); } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -107,14 +114,10 @@ class TimelineApi { /// Parameters: /// - /// * [TimeBucketSize] size (required): - /// /// * [String] timeBucket (required): /// /// * [String] albumId: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: @@ -123,17 +126,23 @@ class TimelineApi { /// /// * [AssetOrder] order: /// + /// * [num] page: + /// + /// * [num] pageSize: + /// /// * [String] personId: /// /// * [String] tagId: /// /// * [String] userId: /// + /// * [AssetVisibility] visibility: + /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future?> getTimeBucket(TimeBucketSize size, String timeBucket, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(size, timeBucket, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, num? page, num? pageSize, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, page: page, pageSize: pageSize, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -141,11 +150,8 @@ class TimelineApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() - .toList(growable: false); - + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'TimeBucketAssetResponseDto',) as TimeBucketAssetResponseDto; + } return null; } @@ -153,12 +159,8 @@ class TimelineApi { /// Performs an HTTP 'GET /timeline/buckets' operation and returns the [Response]. /// Parameters: /// - /// * [TimeBucketSize] size (required): - /// /// * [String] albumId: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: @@ -173,10 +175,12 @@ class TimelineApi { /// /// * [String] userId: /// + /// * [AssetVisibility] visibility: + /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -190,9 +194,6 @@ class TimelineApi { if (albumId != null) { queryParams.addAll(_queryParams('', 'albumId', albumId)); } - if (isArchived != null) { - queryParams.addAll(_queryParams('', 'isArchived', isArchived)); - } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } @@ -208,13 +209,15 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } - queryParams.addAll(_queryParams('', 'size', size)); if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } if (userId != null) { queryParams.addAll(_queryParams('', 'userId', userId)); } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -238,12 +241,8 @@ class TimelineApi { /// Parameters: /// - /// * [TimeBucketSize] size (required): - /// /// * [String] albumId: /// - /// * [bool] isArchived: - /// /// * [bool] isFavorite: /// /// * [bool] isTrashed: @@ -258,11 +257,13 @@ class TimelineApi { /// /// * [String] userId: /// + /// * [AssetVisibility] visibility: + /// /// * [bool] withPartners: /// /// * [bool] withStacked: - Future?> getTimeBuckets(TimeBucketSize size, { String? albumId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo(size, albumId: albumId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -271,8 +272,8 @@ class TimelineApi { // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { final responseBody = await _decodeBodyBytes(response); - return (await apiClient.deserializeAsync(responseBody, 'List') as List) - .cast() + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() .toList(growable: false); } diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index b4508d7dcd..58263504ce 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -211,6 +211,76 @@ class UsersAdminApi { return null; } + /// Performs an HTTP 'GET /admin/users/{id}/statistics' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [bool] isFavorite: + /// + /// * [bool] isTrashed: + /// + /// * [AssetVisibility] visibility: + Future getUserStatisticsAdminWithHttpInfo(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/users/{id}/statistics' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isTrashed != null) { + queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); + } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [bool] isFavorite: + /// + /// * [bool] isTrashed: + /// + /// * [AssetVisibility] visibility: + Future getUserStatisticsAdmin(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { + final response = await getUserStatisticsAdminWithHttpInfo(id, isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetStatsResponseDto',) as AssetStatsResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response]. /// Parameters: /// @@ -262,8 +332,10 @@ class UsersAdminApi { /// Performs an HTTP 'GET /admin/users' operation and returns the [Response]. /// Parameters: /// + /// * [String] id: + /// /// * [bool] withDeleted: - Future searchUsersAdminWithHttpInfo({ bool? withDeleted, }) async { + Future searchUsersAdminWithHttpInfo({ String? id, bool? withDeleted, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users'; @@ -274,6 +346,9 @@ class UsersAdminApi { final headerParams = {}; final formParams = {}; + if (id != null) { + queryParams.addAll(_queryParams('', 'id', id)); + } if (withDeleted != null) { queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); } @@ -294,9 +369,11 @@ class UsersAdminApi { /// Parameters: /// + /// * [String] id: + /// /// * [bool] withDeleted: - Future?> searchUsersAdmin({ bool? withDeleted, }) async { - final response = await searchUsersAdminWithHttpInfo( withDeleted: withDeleted, ); + Future?> searchUsersAdmin({ String? id, bool? withDeleted, }) async { + final response = await searchUsersAdminWithHttpInfo( id: id, withDeleted: withDeleted, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 5759217f41..cc01fd2c06 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -268,10 +268,12 @@ class ApiClient { return AssetStatsResponseDto.fromJson(value); case 'AssetTypeEnum': return AssetTypeEnumTypeTransformer().decode(value); + case 'AssetVisibility': + return AssetVisibilityTypeTransformer().decode(value); case 'AudioCodec': return AudioCodecTypeTransformer().decode(value); - case 'AvatarResponse': - return AvatarResponse.fromJson(value); + case 'AuthStatusResponseDto': + return AuthStatusResponseDto.fromJson(value); case 'AvatarUpdate': return AvatarUpdate.fromJson(value); case 'BulkIdResponseDto': @@ -322,16 +324,6 @@ class ApiClient { return FaceDto.fromJson(value); case 'FacialRecognitionConfig': return FacialRecognitionConfig.fromJson(value); - case 'FileChecksumDto': - return FileChecksumDto.fromJson(value); - case 'FileChecksumResponseDto': - return FileChecksumResponseDto.fromJson(value); - case 'FileReportDto': - return FileReportDto.fromJson(value); - case 'FileReportFixDto': - return FileReportFixDto.fromJson(value); - case 'FileReportItemDto': - return FileReportItemDto.fromJson(value); case 'FoldersResponse': return FoldersResponse.fromJson(value); case 'FoldersUpdate': @@ -380,8 +372,6 @@ class ApiClient { return MemoriesUpdate.fromJson(value); case 'MemoryCreateDto': return MemoryCreateDto.fromJson(value); - case 'MemoryLaneResponseDto': - return MemoryLaneResponseDto.fromJson(value); case 'MemoryResponseDto': return MemoryResponseDto.fromJson(value); case 'MemoryType': @@ -392,22 +382,34 @@ class ApiClient { return MergePersonDto.fromJson(value); case 'MetadataSearchDto': return MetadataSearchDto.fromJson(value); + case 'NotificationCreateDto': + return NotificationCreateDto.fromJson(value); + case 'NotificationDeleteAllDto': + return NotificationDeleteAllDto.fromJson(value); + case 'NotificationDto': + return NotificationDto.fromJson(value); + case 'NotificationLevel': + return NotificationLevelTypeTransformer().decode(value); + case 'NotificationType': + return NotificationTypeTypeTransformer().decode(value); + case 'NotificationUpdateAllDto': + return NotificationUpdateAllDto.fromJson(value); + case 'NotificationUpdateDto': + return NotificationUpdateDto.fromJson(value); case 'OAuthAuthorizeResponseDto': return OAuthAuthorizeResponseDto.fromJson(value); case 'OAuthCallbackDto': return OAuthCallbackDto.fromJson(value); case 'OAuthConfigDto': return OAuthConfigDto.fromJson(value); + case 'OAuthTokenEndpointAuthMethod': + return OAuthTokenEndpointAuthMethodTypeTransformer().decode(value); case 'OnThisDayDto': return OnThisDayDto.fromJson(value); case 'PartnerDirection': return PartnerDirectionTypeTransformer().decode(value); case 'PartnerResponseDto': return PartnerResponseDto.fromJson(value); - case 'PathEntityType': - return PathEntityTypeTypeTransformer().decode(value); - case 'PathType': - return PathTypeTypeTransformer().decode(value); case 'PeopleResponse': return PeopleResponse.fromJson(value); case 'PeopleResponseDto': @@ -430,6 +432,12 @@ class ApiClient { return PersonUpdateDto.fromJson(value); case 'PersonWithFacesResponseDto': return PersonWithFacesResponseDto.fromJson(value); + case 'PinCodeChangeDto': + return PinCodeChangeDto.fromJson(value); + case 'PinCodeResetDto': + return PinCodeResetDto.fromJson(value); + case 'PinCodeSetupDto': + return PinCodeSetupDto.fromJson(value); case 'PlacesResponseDto': return PlacesResponseDto.fromJson(value); case 'PurchaseResponse': @@ -486,8 +494,14 @@ class ApiClient { return ServerVersionHistoryResponseDto.fromJson(value); case 'ServerVersionResponseDto': return ServerVersionResponseDto.fromJson(value); + case 'SessionCreateDto': + return SessionCreateDto.fromJson(value); + case 'SessionCreateResponseDto': + return SessionCreateResponseDto.fromJson(value); case 'SessionResponseDto': return SessionResponseDto.fromJson(value); + case 'SessionUnlockDto': + return SessionUnlockDto.fromJson(value); case 'SharedLinkCreateDto': return SharedLinkCreateDto.fromJson(value); case 'SharedLinkEditDto': @@ -518,6 +532,14 @@ class ApiClient { return SyncAckDto.fromJson(value); case 'SyncAckSetDto': return SyncAckSetDto.fromJson(value); + case 'SyncAlbumDeleteV1': + return SyncAlbumDeleteV1.fromJson(value); + case 'SyncAlbumUserDeleteV1': + return SyncAlbumUserDeleteV1.fromJson(value); + case 'SyncAlbumUserV1': + return SyncAlbumUserV1.fromJson(value); + case 'SyncAlbumV1': + return SyncAlbumV1.fromJson(value); case 'SyncAssetDeleteV1': return SyncAssetDeleteV1.fromJson(value); case 'SyncAssetExifV1': @@ -620,10 +642,10 @@ class ApiClient { return TemplateResponseDto.fromJson(value); case 'TestEmailResponseDto': return TestEmailResponseDto.fromJson(value); - case 'TimeBucketResponseDto': - return TimeBucketResponseDto.fromJson(value); - case 'TimeBucketSize': - return TimeBucketSizeTypeTransformer().decode(value); + case 'TimeBucketAssetResponseDto': + return TimeBucketAssetResponseDto.fromJson(value); + case 'TimeBucketsResponseDto': + return TimeBucketsResponseDto.fromJson(value); case 'ToneMapping': return ToneMappingTypeTransformer().decode(value); case 'TranscodeHWAccel': @@ -674,6 +696,8 @@ class ApiClient { return ValidateLibraryImportPathResponseDto.fromJson(value); case 'ValidateLibraryResponseDto': return ValidateLibraryResponseDto.fromJson(value); + case 'VersionCheckStateResponseDto': + return VersionCheckStateResponseDto.fromJson(value); case 'VideoCodec': return VideoCodecTypeTransformer().decode(value); case 'VideoContainer': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 1ebf8314ad..1618f4a670 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -73,6 +73,9 @@ String parameterToString(dynamic value) { if (value is AssetTypeEnum) { return AssetTypeEnumTypeTransformer().encode(value).toString(); } + if (value is AssetVisibility) { + return AssetVisibilityTypeTransformer().encode(value).toString(); + } if (value is AudioCodec) { return AudioCodecTypeTransformer().encode(value).toString(); } @@ -100,15 +103,18 @@ String parameterToString(dynamic value) { if (value is MemoryType) { return MemoryTypeTypeTransformer().encode(value).toString(); } + if (value is NotificationLevel) { + return NotificationLevelTypeTransformer().encode(value).toString(); + } + if (value is NotificationType) { + return NotificationTypeTypeTransformer().encode(value).toString(); + } + if (value is OAuthTokenEndpointAuthMethod) { + return OAuthTokenEndpointAuthMethodTypeTransformer().encode(value).toString(); + } if (value is PartnerDirection) { return PartnerDirectionTypeTransformer().encode(value).toString(); } - if (value is PathEntityType) { - return PathEntityTypeTypeTransformer().encode(value).toString(); - } - if (value is PathType) { - return PathTypeTypeTransformer().encode(value).toString(); - } if (value is Permission) { return PermissionTypeTransformer().encode(value).toString(); } @@ -133,9 +139,6 @@ String parameterToString(dynamic value) { if (value is SyncRequestType) { return SyncRequestTypeTypeTransformer().encode(value).toString(); } - if (value is TimeBucketSize) { - return TimeBucketSizeTypeTransformer().encode(value).toString(); - } if (value is ToneMapping) { return ToneMappingTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/api_key_update_dto.dart b/mobile/openapi/lib/model/api_key_update_dto.dart index 7295d1ea1f..60ac168fdb 100644 --- a/mobile/openapi/lib/model/api_key_update_dto.dart +++ b/mobile/openapi/lib/model/api_key_update_dto.dart @@ -14,25 +14,31 @@ class APIKeyUpdateDto { /// Returns a new [APIKeyUpdateDto] instance. APIKeyUpdateDto({ required this.name, + this.permissions = const [], }); String name; + List permissions; + @override bool operator ==(Object other) => identical(this, other) || other is APIKeyUpdateDto && - other.name == name; + other.name == name && + _deepEquality.equals(other.permissions, permissions); @override int get hashCode => // ignore: unnecessary_parenthesis - (name.hashCode); + (name.hashCode) + + (permissions.hashCode); @override - String toString() => 'APIKeyUpdateDto[name=$name]'; + String toString() => 'APIKeyUpdateDto[name=$name, permissions=$permissions]'; Map toJson() { final json = {}; json[r'name'] = this.name; + json[r'permissions'] = this.permissions; return json; } @@ -46,6 +52,7 @@ class APIKeyUpdateDto { return APIKeyUpdateDto( name: mapValueOfType(json, r'name')!, + permissions: Permission.listFromJson(json[r'permissions']), ); } return null; @@ -94,6 +101,7 @@ class APIKeyUpdateDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { 'name', + 'permissions', }; } diff --git a/mobile/openapi/lib/model/asset_bulk_update_dto.dart b/mobile/openapi/lib/model/asset_bulk_update_dto.dart index 0b5a2c30d9..571badf029 100644 --- a/mobile/openapi/lib/model/asset_bulk_update_dto.dart +++ b/mobile/openapi/lib/model/asset_bulk_update_dto.dart @@ -14,13 +14,14 @@ class AssetBulkUpdateDto { /// Returns a new [AssetBulkUpdateDto] instance. AssetBulkUpdateDto({ this.dateTimeOriginal, + this.description, this.duplicateId, this.ids = const [], - this.isArchived, this.isFavorite, this.latitude, this.longitude, this.rating, + this.visibility, }); /// @@ -31,17 +32,17 @@ class AssetBulkUpdateDto { /// String? dateTimeOriginal; - String? duplicateId; - - List ids; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated /// source code must fall back to having a nullable type. /// Consider adding a "default:" property in the specification file to hide this note. /// - bool? isArchived; + String? description; + + String? duplicateId; + + List ids; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -77,31 +78,41 @@ class AssetBulkUpdateDto { /// num? rating; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetVisibility? visibility; + @override bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto && other.dateTimeOriginal == dateTimeOriginal && + other.description == description && other.duplicateId == duplicateId && _deepEquality.equals(other.ids, ids) && - other.isArchived == isArchived && other.isFavorite == isFavorite && other.latitude == latitude && other.longitude == longitude && - other.rating == rating; + other.rating == rating && + other.visibility == visibility; @override int get hashCode => // ignore: unnecessary_parenthesis (dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) + + (description == null ? 0 : description!.hashCode) + (duplicateId == null ? 0 : duplicateId!.hashCode) + (ids.hashCode) + - (isArchived == null ? 0 : isArchived!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + (latitude == null ? 0 : latitude!.hashCode) + (longitude == null ? 0 : longitude!.hashCode) + - (rating == null ? 0 : rating!.hashCode); + (rating == null ? 0 : rating!.hashCode) + + (visibility == null ? 0 : visibility!.hashCode); @override - String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, duplicateId=$duplicateId, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating]'; + String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, description=$description, duplicateId=$duplicateId, ids=$ids, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating, visibility=$visibility]'; Map toJson() { final json = {}; @@ -110,17 +121,17 @@ class AssetBulkUpdateDto { } else { // json[r'dateTimeOriginal'] = null; } + if (this.description != null) { + json[r'description'] = this.description; + } else { + // json[r'description'] = null; + } if (this.duplicateId != null) { json[r'duplicateId'] = this.duplicateId; } else { // json[r'duplicateId'] = null; } json[r'ids'] = this.ids; - if (this.isArchived != null) { - json[r'isArchived'] = this.isArchived; - } else { - // json[r'isArchived'] = null; - } if (this.isFavorite != null) { json[r'isFavorite'] = this.isFavorite; } else { @@ -141,6 +152,11 @@ class AssetBulkUpdateDto { } else { // json[r'rating'] = null; } + if (this.visibility != null) { + json[r'visibility'] = this.visibility; + } else { + // json[r'visibility'] = null; + } return json; } @@ -154,15 +170,16 @@ class AssetBulkUpdateDto { return AssetBulkUpdateDto( dateTimeOriginal: mapValueOfType(json, r'dateTimeOriginal'), + description: mapValueOfType(json, r'description'), duplicateId: mapValueOfType(json, r'duplicateId'), ids: json[r'ids'] is Iterable ? (json[r'ids'] as Iterable).cast().toList(growable: false) : const [], - isArchived: mapValueOfType(json, r'isArchived'), isFavorite: mapValueOfType(json, r'isFavorite'), latitude: num.parse('${json[r'latitude']}'), longitude: num.parse('${json[r'longitude']}'), rating: num.parse('${json[r'rating']}'), + visibility: AssetVisibility.fromJson(json[r'visibility']), ); } return null; diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 5f01f84419..3d85b779cc 100644 --- a/mobile/openapi/lib/model/asset_response_dto.dart +++ b/mobile/openapi/lib/model/asset_response_dto.dart @@ -43,6 +43,7 @@ class AssetResponseDto { required this.type, this.unassignedFaces = const [], required this.updatedAt, + required this.visibility, }); /// base64 encoded sha1 hash @@ -132,6 +133,8 @@ class AssetResponseDto { DateTime updatedAt; + AssetVisibility visibility; + @override bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto && other.checksum == checksum && @@ -163,7 +166,8 @@ class AssetResponseDto { other.thumbhash == thumbhash && other.type == type && _deepEquality.equals(other.unassignedFaces, unassignedFaces) && - other.updatedAt == updatedAt; + other.updatedAt == updatedAt && + other.visibility == visibility; @override int get hashCode => @@ -197,10 +201,11 @@ class AssetResponseDto { (thumbhash == null ? 0 : thumbhash!.hashCode) + (type.hashCode) + (unassignedFaces.hashCode) + - (updatedAt.hashCode); + (updatedAt.hashCode) + + (visibility.hashCode); @override - String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt]'; + String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt, visibility=$visibility]'; Map toJson() { final json = {}; @@ -270,6 +275,7 @@ class AssetResponseDto { json[r'type'] = this.type; json[r'unassignedFaces'] = this.unassignedFaces; json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + json[r'visibility'] = this.visibility; return json; } @@ -312,6 +318,7 @@ class AssetResponseDto { type: AssetTypeEnum.fromJson(json[r'type'])!, unassignedFaces: AssetFaceWithoutPersonResponseDto.listFromJson(json[r'unassignedFaces']), updatedAt: mapDateTime(json, r'updatedAt', r'')!, + visibility: AssetVisibility.fromJson(json[r'visibility'])!, ); } return null; @@ -378,6 +385,7 @@ class AssetResponseDto { 'thumbhash', 'type', 'updatedAt', + 'visibility', }; } diff --git a/mobile/openapi/lib/model/asset_visibility.dart b/mobile/openapi/lib/model/asset_visibility.dart new file mode 100644 index 0000000000..498bf17c38 --- /dev/null +++ b/mobile/openapi/lib/model/asset_visibility.dart @@ -0,0 +1,91 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class AssetVisibility { + /// Instantiate a new enum with the provided [value]. + const AssetVisibility._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const archive = AssetVisibility._(r'archive'); + static const timeline = AssetVisibility._(r'timeline'); + static const hidden = AssetVisibility._(r'hidden'); + static const locked = AssetVisibility._(r'locked'); + + /// List of all possible values in this [enum][AssetVisibility]. + static const values = [ + archive, + timeline, + hidden, + locked, + ]; + + static AssetVisibility? fromJson(dynamic value) => AssetVisibilityTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetVisibility.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [AssetVisibility] to String, +/// and [decode] dynamic data back to [AssetVisibility]. +class AssetVisibilityTypeTransformer { + factory AssetVisibilityTypeTransformer() => _instance ??= const AssetVisibilityTypeTransformer._(); + + const AssetVisibilityTypeTransformer._(); + + String encode(AssetVisibility data) => data.value; + + /// Decodes a [dynamic value][data] to a AssetVisibility. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + AssetVisibility? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'archive': return AssetVisibility.archive; + case r'timeline': return AssetVisibility.timeline; + case r'hidden': return AssetVisibility.hidden; + case r'locked': return AssetVisibility.locked; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [AssetVisibilityTypeTransformer] instance. + static AssetVisibilityTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/auth_status_response_dto.dart b/mobile/openapi/lib/model/auth_status_response_dto.dart new file mode 100644 index 0000000000..4e823506ee --- /dev/null +++ b/mobile/openapi/lib/model/auth_status_response_dto.dart @@ -0,0 +1,149 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AuthStatusResponseDto { + /// Returns a new [AuthStatusResponseDto] instance. + AuthStatusResponseDto({ + this.expiresAt, + required this.isElevated, + required this.password, + required this.pinCode, + this.pinExpiresAt, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? expiresAt; + + bool isElevated; + + bool password; + + bool pinCode; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? pinExpiresAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is AuthStatusResponseDto && + other.expiresAt == expiresAt && + other.isElevated == isElevated && + other.password == password && + other.pinCode == pinCode && + other.pinExpiresAt == pinExpiresAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (expiresAt == null ? 0 : expiresAt!.hashCode) + + (isElevated.hashCode) + + (password.hashCode) + + (pinCode.hashCode) + + (pinExpiresAt == null ? 0 : pinExpiresAt!.hashCode); + + @override + String toString() => 'AuthStatusResponseDto[expiresAt=$expiresAt, isElevated=$isElevated, password=$password, pinCode=$pinCode, pinExpiresAt=$pinExpiresAt]'; + + Map toJson() { + final json = {}; + if (this.expiresAt != null) { + json[r'expiresAt'] = this.expiresAt; + } else { + // json[r'expiresAt'] = null; + } + json[r'isElevated'] = this.isElevated; + json[r'password'] = this.password; + json[r'pinCode'] = this.pinCode; + if (this.pinExpiresAt != null) { + json[r'pinExpiresAt'] = this.pinExpiresAt; + } else { + // json[r'pinExpiresAt'] = null; + } + return json; + } + + /// Returns a new [AuthStatusResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AuthStatusResponseDto? fromJson(dynamic value) { + upgradeDto(value, "AuthStatusResponseDto"); + if (value is Map) { + final json = value.cast(); + + return AuthStatusResponseDto( + expiresAt: mapValueOfType(json, r'expiresAt'), + isElevated: mapValueOfType(json, r'isElevated')!, + password: mapValueOfType(json, r'password')!, + pinCode: mapValueOfType(json, r'pinCode')!, + pinExpiresAt: mapValueOfType(json, r'pinExpiresAt'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AuthStatusResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AuthStatusResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AuthStatusResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AuthStatusResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'isElevated', + 'password', + 'pinCode', + }; +} + diff --git a/mobile/openapi/lib/model/file_checksum_response_dto.dart b/mobile/openapi/lib/model/file_checksum_response_dto.dart deleted file mode 100644 index 7b963c8bd5..0000000000 --- a/mobile/openapi/lib/model/file_checksum_response_dto.dart +++ /dev/null @@ -1,107 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - -class FileChecksumResponseDto { - /// Returns a new [FileChecksumResponseDto] instance. - FileChecksumResponseDto({ - required this.checksum, - required this.filename, - }); - - String checksum; - - String filename; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileChecksumResponseDto && - other.checksum == checksum && - other.filename == filename; - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (checksum.hashCode) + - (filename.hashCode); - - @override - String toString() => 'FileChecksumResponseDto[checksum=$checksum, filename=$filename]'; - - Map toJson() { - final json = {}; - json[r'checksum'] = this.checksum; - json[r'filename'] = this.filename; - return json; - } - - /// Returns a new [FileChecksumResponseDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileChecksumResponseDto? fromJson(dynamic value) { - upgradeDto(value, "FileChecksumResponseDto"); - if (value is Map) { - final json = value.cast(); - - return FileChecksumResponseDto( - checksum: mapValueOfType(json, r'checksum')!, - filename: mapValueOfType(json, r'filename')!, - ); - } - return null; - } - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileChecksumResponseDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map mapFromJson(dynamic json) { - final map = {}; - if (json is Map && json.isNotEmpty) { - json = json.cast(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileChecksumResponseDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileChecksumResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast(); - for (final entry in json.entries) { - map[entry.key] = FileChecksumResponseDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = { - 'checksum', - 'filename', - }; -} - diff --git a/mobile/openapi/lib/model/file_report_dto.dart b/mobile/openapi/lib/model/file_report_dto.dart deleted file mode 100644 index 3dc892e5e7..0000000000 --- a/mobile/openapi/lib/model/file_report_dto.dart +++ /dev/null @@ -1,109 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - -class FileReportDto { - /// Returns a new [FileReportDto] instance. - FileReportDto({ - this.extras = const [], - this.orphans = const [], - }); - - List extras; - - List orphans; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileReportDto && - _deepEquality.equals(other.extras, extras) && - _deepEquality.equals(other.orphans, orphans); - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (extras.hashCode) + - (orphans.hashCode); - - @override - String toString() => 'FileReportDto[extras=$extras, orphans=$orphans]'; - - Map toJson() { - final json = {}; - json[r'extras'] = this.extras; - json[r'orphans'] = this.orphans; - return json; - } - - /// Returns a new [FileReportDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileReportDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportDto"); - if (value is Map) { - final json = value.cast(); - - return FileReportDto( - extras: json[r'extras'] is Iterable - ? (json[r'extras'] as Iterable).cast().toList(growable: false) - : const [], - orphans: FileReportItemDto.listFromJson(json[r'orphans']), - ); - } - return null; - } - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileReportDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map mapFromJson(dynamic json) { - final map = {}; - if (json is Map && json.isNotEmpty) { - json = json.cast(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileReportDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileReportDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast(); - for (final entry in json.entries) { - map[entry.key] = FileReportDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = { - 'extras', - 'orphans', - }; -} - diff --git a/mobile/openapi/lib/model/file_report_item_dto.dart b/mobile/openapi/lib/model/file_report_item_dto.dart deleted file mode 100644 index 1ef08c2b48..0000000000 --- a/mobile/openapi/lib/model/file_report_item_dto.dart +++ /dev/null @@ -1,140 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - -class FileReportItemDto { - /// Returns a new [FileReportItemDto] instance. - FileReportItemDto({ - this.checksum, - required this.entityId, - required this.entityType, - required this.pathType, - required this.pathValue, - }); - - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - String? checksum; - - String entityId; - - PathEntityType entityType; - - PathType pathType; - - String pathValue; - - @override - bool operator ==(Object other) => identical(this, other) || other is FileReportItemDto && - other.checksum == checksum && - other.entityId == entityId && - other.entityType == entityType && - other.pathType == pathType && - other.pathValue == pathValue; - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - (checksum == null ? 0 : checksum!.hashCode) + - (entityId.hashCode) + - (entityType.hashCode) + - (pathType.hashCode) + - (pathValue.hashCode); - - @override - String toString() => 'FileReportItemDto[checksum=$checksum, entityId=$entityId, entityType=$entityType, pathType=$pathType, pathValue=$pathValue]'; - - Map toJson() { - final json = {}; - if (this.checksum != null) { - json[r'checksum'] = this.checksum; - } else { - // json[r'checksum'] = null; - } - json[r'entityId'] = this.entityId; - json[r'entityType'] = this.entityType; - json[r'pathType'] = this.pathType; - json[r'pathValue'] = this.pathValue; - return json; - } - - /// Returns a new [FileReportItemDto] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static FileReportItemDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportItemDto"); - if (value is Map) { - final json = value.cast(); - - return FileReportItemDto( - checksum: mapValueOfType(json, r'checksum'), - entityId: mapValueOfType(json, r'entityId')!, - entityType: PathEntityType.fromJson(json[r'entityType'])!, - pathType: PathType.fromJson(json[r'pathType'])!, - pathValue: mapValueOfType(json, r'pathValue')!, - ); - } - return null; - } - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = FileReportItemDto.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map mapFromJson(dynamic json) { - final map = {}; - if (json is Map && json.isNotEmpty) { - json = json.cast(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = FileReportItemDto.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of FileReportItemDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast(); - for (final entry in json.entries) { - map[entry.key] = FileReportItemDto.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = { - 'entityId', - 'entityType', - 'pathType', - 'pathValue', - }; -} - diff --git a/mobile/openapi/lib/model/metadata_search_dto.dart b/mobile/openapi/lib/model/metadata_search_dto.dart index 3fb003d164..7f1184467b 100644 --- a/mobile/openapi/lib/model/metadata_search_dto.dart +++ b/mobile/openapi/lib/model/metadata_search_dto.dart @@ -23,13 +23,11 @@ class MetadataSearchDto { this.deviceId, this.encodedVideoPath, this.id, - this.isArchived, this.isEncoded, this.isFavorite, this.isMotion, this.isNotInAlbum, this.isOffline, - this.isVisible, this.lensModel, this.libraryId, this.make, @@ -52,7 +50,7 @@ class MetadataSearchDto { this.type, this.updatedAfter, this.updatedBefore, - this.withArchived = false, + this.visibility, this.withDeleted, this.withExif, this.withPeople, @@ -127,14 +125,6 @@ class MetadataSearchDto { /// String? id; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isArchived; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -175,14 +165,6 @@ class MetadataSearchDto { /// bool? isOffline; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isVisible; - String? lensModel; String? libraryId; @@ -322,7 +304,13 @@ class MetadataSearchDto { /// DateTime? updatedBefore; - bool withArchived; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetVisibility? visibility; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -368,13 +356,11 @@ class MetadataSearchDto { other.deviceId == deviceId && other.encodedVideoPath == encodedVideoPath && other.id == id && - other.isArchived == isArchived && other.isEncoded == isEncoded && other.isFavorite == isFavorite && other.isMotion == isMotion && other.isNotInAlbum == isNotInAlbum && other.isOffline == isOffline && - other.isVisible == isVisible && other.lensModel == lensModel && other.libraryId == libraryId && other.make == make && @@ -397,7 +383,7 @@ class MetadataSearchDto { other.type == type && other.updatedAfter == updatedAfter && other.updatedBefore == updatedBefore && - other.withArchived == withArchived && + other.visibility == visibility && other.withDeleted == withDeleted && other.withExif == withExif && other.withPeople == withPeople && @@ -416,13 +402,11 @@ class MetadataSearchDto { (deviceId == null ? 0 : deviceId!.hashCode) + (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + (id == null ? 0 : id!.hashCode) + - (isArchived == null ? 0 : isArchived!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + (isMotion == null ? 0 : isMotion!.hashCode) + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + (isOffline == null ? 0 : isOffline!.hashCode) + - (isVisible == null ? 0 : isVisible!.hashCode) + (lensModel == null ? 0 : lensModel!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) + (make == null ? 0 : make!.hashCode) + @@ -445,14 +429,14 @@ class MetadataSearchDto { (type == null ? 0 : type!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) + - (withArchived.hashCode) + + (visibility == null ? 0 : visibility!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) + (withExif == null ? 0 : withExif!.hashCode) + (withPeople == null ? 0 : withPeople!.hashCode) + (withStacked == null ? 0 : withStacked!.hashCode); @override - String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; + String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; Map toJson() { final json = {}; @@ -506,11 +490,6 @@ class MetadataSearchDto { } else { // json[r'id'] = null; } - if (this.isArchived != null) { - json[r'isArchived'] = this.isArchived; - } else { - // json[r'isArchived'] = null; - } if (this.isEncoded != null) { json[r'isEncoded'] = this.isEncoded; } else { @@ -536,11 +515,6 @@ class MetadataSearchDto { } else { // json[r'isOffline'] = null; } - if (this.isVisible != null) { - json[r'isVisible'] = this.isVisible; - } else { - // json[r'isVisible'] = null; - } if (this.lensModel != null) { json[r'lensModel'] = this.lensModel; } else { @@ -639,7 +613,11 @@ class MetadataSearchDto { } else { // json[r'updatedBefore'] = null; } - json[r'withArchived'] = this.withArchived; + if (this.visibility != null) { + json[r'visibility'] = this.visibility; + } else { + // json[r'visibility'] = null; + } if (this.withDeleted != null) { json[r'withDeleted'] = this.withDeleted; } else { @@ -682,13 +660,11 @@ class MetadataSearchDto { deviceId: mapValueOfType(json, r'deviceId'), encodedVideoPath: mapValueOfType(json, r'encodedVideoPath'), id: mapValueOfType(json, r'id'), - isArchived: mapValueOfType(json, r'isArchived'), isEncoded: mapValueOfType(json, r'isEncoded'), isFavorite: mapValueOfType(json, r'isFavorite'), isMotion: mapValueOfType(json, r'isMotion'), isNotInAlbum: mapValueOfType(json, r'isNotInAlbum'), isOffline: mapValueOfType(json, r'isOffline'), - isVisible: mapValueOfType(json, r'isVisible'), lensModel: mapValueOfType(json, r'lensModel'), libraryId: mapValueOfType(json, r'libraryId'), make: mapValueOfType(json, r'make'), @@ -715,7 +691,7 @@ class MetadataSearchDto { type: AssetTypeEnum.fromJson(json[r'type']), updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''), - withArchived: mapValueOfType(json, r'withArchived') ?? false, + visibility: AssetVisibility.fromJson(json[r'visibility']), withDeleted: mapValueOfType(json, r'withDeleted'), withExif: mapValueOfType(json, r'withExif'), withPeople: mapValueOfType(json, r'withPeople'), diff --git a/mobile/openapi/lib/model/notification_create_dto.dart b/mobile/openapi/lib/model/notification_create_dto.dart new file mode 100644 index 0000000000..07985353b2 --- /dev/null +++ b/mobile/openapi/lib/model/notification_create_dto.dart @@ -0,0 +1,180 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class NotificationCreateDto { + /// Returns a new [NotificationCreateDto] instance. + NotificationCreateDto({ + this.data, + this.description, + this.level, + this.readAt, + required this.title, + this.type, + required this.userId, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + Object? data; + + String? description; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + NotificationLevel? level; + + DateTime? readAt; + + String title; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + NotificationType? type; + + String userId; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationCreateDto && + other.data == data && + other.description == description && + other.level == level && + other.readAt == readAt && + other.title == title && + other.type == type && + other.userId == userId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (data == null ? 0 : data!.hashCode) + + (description == null ? 0 : description!.hashCode) + + (level == null ? 0 : level!.hashCode) + + (readAt == null ? 0 : readAt!.hashCode) + + (title.hashCode) + + (type == null ? 0 : type!.hashCode) + + (userId.hashCode); + + @override + String toString() => 'NotificationCreateDto[data=$data, description=$description, level=$level, readAt=$readAt, title=$title, type=$type, userId=$userId]'; + + Map toJson() { + final json = {}; + if (this.data != null) { + json[r'data'] = this.data; + } else { + // json[r'data'] = null; + } + if (this.description != null) { + json[r'description'] = this.description; + } else { + // json[r'description'] = null; + } + if (this.level != null) { + json[r'level'] = this.level; + } else { + // json[r'level'] = null; + } + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + json[r'title'] = this.title; + if (this.type != null) { + json[r'type'] = this.type; + } else { + // json[r'type'] = null; + } + json[r'userId'] = this.userId; + return json; + } + + /// Returns a new [NotificationCreateDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationCreateDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationCreateDto"); + if (value is Map) { + final json = value.cast(); + + return NotificationCreateDto( + data: mapValueOfType(json, r'data'), + description: mapValueOfType(json, r'description'), + level: NotificationLevel.fromJson(json[r'level']), + readAt: mapDateTime(json, r'readAt', r''), + title: mapValueOfType(json, r'title')!, + type: NotificationType.fromJson(json[r'type']), + userId: mapValueOfType(json, r'userId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationCreateDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationCreateDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationCreateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = NotificationCreateDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'title', + 'userId', + }; +} + diff --git a/mobile/openapi/lib/model/file_report_fix_dto.dart b/mobile/openapi/lib/model/notification_delete_all_dto.dart similarity index 50% rename from mobile/openapi/lib/model/file_report_fix_dto.dart rename to mobile/openapi/lib/model/notification_delete_all_dto.dart index d46cdeb4b7..4be1b89e92 100644 --- a/mobile/openapi/lib/model/file_report_fix_dto.dart +++ b/mobile/openapi/lib/model/notification_delete_all_dto.dart @@ -10,52 +10,54 @@ part of openapi.api; -class FileReportFixDto { - /// Returns a new [FileReportFixDto] instance. - FileReportFixDto({ - this.items = const [], +class NotificationDeleteAllDto { + /// Returns a new [NotificationDeleteAllDto] instance. + NotificationDeleteAllDto({ + this.ids = const [], }); - List items; + List ids; @override - bool operator ==(Object other) => identical(this, other) || other is FileReportFixDto && - _deepEquality.equals(other.items, items); + bool operator ==(Object other) => identical(this, other) || other is NotificationDeleteAllDto && + _deepEquality.equals(other.ids, ids); @override int get hashCode => // ignore: unnecessary_parenthesis - (items.hashCode); + (ids.hashCode); @override - String toString() => 'FileReportFixDto[items=$items]'; + String toString() => 'NotificationDeleteAllDto[ids=$ids]'; Map toJson() { final json = {}; - json[r'items'] = this.items; + json[r'ids'] = this.ids; return json; } - /// Returns a new [FileReportFixDto] instance and imports its values from + /// Returns a new [NotificationDeleteAllDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static FileReportFixDto? fromJson(dynamic value) { - upgradeDto(value, "FileReportFixDto"); + static NotificationDeleteAllDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationDeleteAllDto"); if (value is Map) { final json = value.cast(); - return FileReportFixDto( - items: FileReportItemDto.listFromJson(json[r'items']), + return NotificationDeleteAllDto( + ids: json[r'ids'] is Iterable + ? (json[r'ids'] as Iterable).cast().toList(growable: false) + : const [], ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = FileReportFixDto.fromJson(row); + final value = NotificationDeleteAllDto.fromJson(row); if (value != null) { result.add(value); } @@ -64,12 +66,12 @@ class FileReportFixDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = FileReportFixDto.fromJson(entry.value); + final value = NotificationDeleteAllDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -78,14 +80,14 @@ class FileReportFixDto { return map; } - // maps a json object with a list of FileReportFixDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of NotificationDeleteAllDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = FileReportFixDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = NotificationDeleteAllDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -93,7 +95,7 @@ class FileReportFixDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'items', + 'ids', }; } diff --git a/mobile/openapi/lib/model/notification_dto.dart b/mobile/openapi/lib/model/notification_dto.dart new file mode 100644 index 0000000000..4f730b4e50 --- /dev/null +++ b/mobile/openapi/lib/model/notification_dto.dart @@ -0,0 +1,182 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class NotificationDto { + /// Returns a new [NotificationDto] instance. + NotificationDto({ + required this.createdAt, + this.data, + this.description, + required this.id, + required this.level, + this.readAt, + required this.title, + required this.type, + }); + + DateTime createdAt; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + Object? data; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? description; + + String id; + + NotificationLevel level; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + DateTime? readAt; + + String title; + + NotificationType type; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationDto && + other.createdAt == createdAt && + other.data == data && + other.description == description && + other.id == id && + other.level == level && + other.readAt == readAt && + other.title == title && + other.type == type; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (data == null ? 0 : data!.hashCode) + + (description == null ? 0 : description!.hashCode) + + (id.hashCode) + + (level.hashCode) + + (readAt == null ? 0 : readAt!.hashCode) + + (title.hashCode) + + (type.hashCode); + + @override + String toString() => 'NotificationDto[createdAt=$createdAt, data=$data, description=$description, id=$id, level=$level, readAt=$readAt, title=$title, type=$type]'; + + Map toJson() { + final json = {}; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); + if (this.data != null) { + json[r'data'] = this.data; + } else { + // json[r'data'] = null; + } + if (this.description != null) { + json[r'description'] = this.description; + } else { + // json[r'description'] = null; + } + json[r'id'] = this.id; + json[r'level'] = this.level; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + json[r'title'] = this.title; + json[r'type'] = this.type; + return json; + } + + /// Returns a new [NotificationDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationDto"); + if (value is Map) { + final json = value.cast(); + + return NotificationDto( + createdAt: mapDateTime(json, r'createdAt', r'')!, + data: mapValueOfType(json, r'data'), + description: mapValueOfType(json, r'description'), + id: mapValueOfType(json, r'id')!, + level: NotificationLevel.fromJson(json[r'level'])!, + readAt: mapDateTime(json, r'readAt', r''), + title: mapValueOfType(json, r'title')!, + type: NotificationType.fromJson(json[r'type'])!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = NotificationDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'createdAt', + 'id', + 'level', + 'title', + 'type', + }; +} + diff --git a/mobile/openapi/lib/model/notification_level.dart b/mobile/openapi/lib/model/notification_level.dart new file mode 100644 index 0000000000..554863ae4f --- /dev/null +++ b/mobile/openapi/lib/model/notification_level.dart @@ -0,0 +1,91 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class NotificationLevel { + /// Instantiate a new enum with the provided [value]. + const NotificationLevel._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const success = NotificationLevel._(r'success'); + static const error = NotificationLevel._(r'error'); + static const warning = NotificationLevel._(r'warning'); + static const info = NotificationLevel._(r'info'); + + /// List of all possible values in this [enum][NotificationLevel]. + static const values = [ + success, + error, + warning, + info, + ]; + + static NotificationLevel? fromJson(dynamic value) => NotificationLevelTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationLevel.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [NotificationLevel] to String, +/// and [decode] dynamic data back to [NotificationLevel]. +class NotificationLevelTypeTransformer { + factory NotificationLevelTypeTransformer() => _instance ??= const NotificationLevelTypeTransformer._(); + + const NotificationLevelTypeTransformer._(); + + String encode(NotificationLevel data) => data.value; + + /// Decodes a [dynamic value][data] to a NotificationLevel. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + NotificationLevel? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'success': return NotificationLevel.success; + case r'error': return NotificationLevel.error; + case r'warning': return NotificationLevel.warning; + case r'info': return NotificationLevel.info; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [NotificationLevelTypeTransformer] instance. + static NotificationLevelTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/notification_type.dart b/mobile/openapi/lib/model/notification_type.dart new file mode 100644 index 0000000000..436d2d190f --- /dev/null +++ b/mobile/openapi/lib/model/notification_type.dart @@ -0,0 +1,91 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class NotificationType { + /// Instantiate a new enum with the provided [value]. + const NotificationType._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const jobFailed = NotificationType._(r'JobFailed'); + static const backupFailed = NotificationType._(r'BackupFailed'); + static const systemMessage = NotificationType._(r'SystemMessage'); + static const custom = NotificationType._(r'Custom'); + + /// List of all possible values in this [enum][NotificationType]. + static const values = [ + jobFailed, + backupFailed, + systemMessage, + custom, + ]; + + static NotificationType? fromJson(dynamic value) => NotificationTypeTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationType.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [NotificationType] to String, +/// and [decode] dynamic data back to [NotificationType]. +class NotificationTypeTypeTransformer { + factory NotificationTypeTypeTransformer() => _instance ??= const NotificationTypeTypeTransformer._(); + + const NotificationTypeTypeTransformer._(); + + String encode(NotificationType data) => data.value; + + /// Decodes a [dynamic value][data] to a NotificationType. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + NotificationType? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'JobFailed': return NotificationType.jobFailed; + case r'BackupFailed': return NotificationType.backupFailed; + case r'SystemMessage': return NotificationType.systemMessage; + case r'Custom': return NotificationType.custom; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [NotificationTypeTypeTransformer] instance. + static NotificationTypeTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/notification_update_all_dto.dart b/mobile/openapi/lib/model/notification_update_all_dto.dart new file mode 100644 index 0000000000..a6393b275a --- /dev/null +++ b/mobile/openapi/lib/model/notification_update_all_dto.dart @@ -0,0 +1,112 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class NotificationUpdateAllDto { + /// Returns a new [NotificationUpdateAllDto] instance. + NotificationUpdateAllDto({ + this.ids = const [], + this.readAt, + }); + + List ids; + + DateTime? readAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateAllDto && + _deepEquality.equals(other.ids, ids) && + other.readAt == readAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (ids.hashCode) + + (readAt == null ? 0 : readAt!.hashCode); + + @override + String toString() => 'NotificationUpdateAllDto[ids=$ids, readAt=$readAt]'; + + Map toJson() { + final json = {}; + json[r'ids'] = this.ids; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + return json; + } + + /// Returns a new [NotificationUpdateAllDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationUpdateAllDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationUpdateAllDto"); + if (value is Map) { + final json = value.cast(); + + return NotificationUpdateAllDto( + ids: json[r'ids'] is Iterable + ? (json[r'ids'] as Iterable).cast().toList(growable: false) + : const [], + readAt: mapDateTime(json, r'readAt', r''), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationUpdateAllDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationUpdateAllDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationUpdateAllDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = NotificationUpdateAllDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'ids', + }; +} + diff --git a/mobile/openapi/lib/model/notification_update_dto.dart b/mobile/openapi/lib/model/notification_update_dto.dart new file mode 100644 index 0000000000..e76496eb97 --- /dev/null +++ b/mobile/openapi/lib/model/notification_update_dto.dart @@ -0,0 +1,102 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class NotificationUpdateDto { + /// Returns a new [NotificationUpdateDto] instance. + NotificationUpdateDto({ + this.readAt, + }); + + DateTime? readAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is NotificationUpdateDto && + other.readAt == readAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (readAt == null ? 0 : readAt!.hashCode); + + @override + String toString() => 'NotificationUpdateDto[readAt=$readAt]'; + + Map toJson() { + final json = {}; + if (this.readAt != null) { + json[r'readAt'] = this.readAt!.toUtc().toIso8601String(); + } else { + // json[r'readAt'] = null; + } + return json; + } + + /// Returns a new [NotificationUpdateDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static NotificationUpdateDto? fromJson(dynamic value) { + upgradeDto(value, "NotificationUpdateDto"); + if (value is Map) { + final json = value.cast(); + + return NotificationUpdateDto( + readAt: mapDateTime(json, r'readAt', r''), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = NotificationUpdateDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = NotificationUpdateDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of NotificationUpdateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = NotificationUpdateDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/o_auth_callback_dto.dart b/mobile/openapi/lib/model/o_auth_callback_dto.dart index d0b98d5c6f..ea8cac31a0 100644 --- a/mobile/openapi/lib/model/o_auth_callback_dto.dart +++ b/mobile/openapi/lib/model/o_auth_callback_dto.dart @@ -13,25 +13,57 @@ part of openapi.api; class OAuthCallbackDto { /// Returns a new [OAuthCallbackDto] instance. OAuthCallbackDto({ + this.codeVerifier, + this.state, required this.url, }); + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? codeVerifier; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + String url; @override bool operator ==(Object other) => identical(this, other) || other is OAuthCallbackDto && + other.codeVerifier == codeVerifier && + other.state == state && other.url == url; @override int get hashCode => // ignore: unnecessary_parenthesis + (codeVerifier == null ? 0 : codeVerifier!.hashCode) + + (state == null ? 0 : state!.hashCode) + (url.hashCode); @override - String toString() => 'OAuthCallbackDto[url=$url]'; + String toString() => 'OAuthCallbackDto[codeVerifier=$codeVerifier, state=$state, url=$url]'; Map toJson() { final json = {}; + if (this.codeVerifier != null) { + json[r'codeVerifier'] = this.codeVerifier; + } else { + // json[r'codeVerifier'] = null; + } + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } json[r'url'] = this.url; return json; } @@ -45,6 +77,8 @@ class OAuthCallbackDto { final json = value.cast(); return OAuthCallbackDto( + codeVerifier: mapValueOfType(json, r'codeVerifier'), + state: mapValueOfType(json, r'state'), url: mapValueOfType(json, r'url')!, ); } diff --git a/mobile/openapi/lib/model/o_auth_config_dto.dart b/mobile/openapi/lib/model/o_auth_config_dto.dart index 86c79b4e04..bb3e8d448d 100644 --- a/mobile/openapi/lib/model/o_auth_config_dto.dart +++ b/mobile/openapi/lib/model/o_auth_config_dto.dart @@ -13,26 +13,58 @@ part of openapi.api; class OAuthConfigDto { /// Returns a new [OAuthConfigDto] instance. OAuthConfigDto({ + this.codeChallenge, required this.redirectUri, + this.state, }); + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? codeChallenge; + String redirectUri; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? state; + @override bool operator ==(Object other) => identical(this, other) || other is OAuthConfigDto && - other.redirectUri == redirectUri; + other.codeChallenge == codeChallenge && + other.redirectUri == redirectUri && + other.state == state; @override int get hashCode => // ignore: unnecessary_parenthesis - (redirectUri.hashCode); + (codeChallenge == null ? 0 : codeChallenge!.hashCode) + + (redirectUri.hashCode) + + (state == null ? 0 : state!.hashCode); @override - String toString() => 'OAuthConfigDto[redirectUri=$redirectUri]'; + String toString() => 'OAuthConfigDto[codeChallenge=$codeChallenge, redirectUri=$redirectUri, state=$state]'; Map toJson() { final json = {}; + if (this.codeChallenge != null) { + json[r'codeChallenge'] = this.codeChallenge; + } else { + // json[r'codeChallenge'] = null; + } json[r'redirectUri'] = this.redirectUri; + if (this.state != null) { + json[r'state'] = this.state; + } else { + // json[r'state'] = null; + } return json; } @@ -45,7 +77,9 @@ class OAuthConfigDto { final json = value.cast(); return OAuthConfigDto( + codeChallenge: mapValueOfType(json, r'codeChallenge'), redirectUri: mapValueOfType(json, r'redirectUri')!, + state: mapValueOfType(json, r'state'), ); } return null; diff --git a/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart b/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart new file mode 100644 index 0000000000..fc528888b3 --- /dev/null +++ b/mobile/openapi/lib/model/o_auth_token_endpoint_auth_method.dart @@ -0,0 +1,85 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class OAuthTokenEndpointAuthMethod { + /// Instantiate a new enum with the provided [value]. + const OAuthTokenEndpointAuthMethod._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const post = OAuthTokenEndpointAuthMethod._(r'client_secret_post'); + static const basic = OAuthTokenEndpointAuthMethod._(r'client_secret_basic'); + + /// List of all possible values in this [enum][OAuthTokenEndpointAuthMethod]. + static const values = [ + post, + basic, + ]; + + static OAuthTokenEndpointAuthMethod? fromJson(dynamic value) => OAuthTokenEndpointAuthMethodTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = OAuthTokenEndpointAuthMethod.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [OAuthTokenEndpointAuthMethod] to String, +/// and [decode] dynamic data back to [OAuthTokenEndpointAuthMethod]. +class OAuthTokenEndpointAuthMethodTypeTransformer { + factory OAuthTokenEndpointAuthMethodTypeTransformer() => _instance ??= const OAuthTokenEndpointAuthMethodTypeTransformer._(); + + const OAuthTokenEndpointAuthMethodTypeTransformer._(); + + String encode(OAuthTokenEndpointAuthMethod data) => data.value; + + /// Decodes a [dynamic value][data] to a OAuthTokenEndpointAuthMethod. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + OAuthTokenEndpointAuthMethod? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'client_secret_post': return OAuthTokenEndpointAuthMethod.post; + case r'client_secret_basic': return OAuthTokenEndpointAuthMethod.basic; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [OAuthTokenEndpointAuthMethodTypeTransformer] instance. + static OAuthTokenEndpointAuthMethodTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/path_entity_type.dart b/mobile/openapi/lib/model/path_entity_type.dart deleted file mode 100644 index fdcdae4f1b..0000000000 --- a/mobile/openapi/lib/model/path_entity_type.dart +++ /dev/null @@ -1,88 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - - -class PathEntityType { - /// Instantiate a new enum with the provided [value]. - const PathEntityType._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const asset = PathEntityType._(r'asset'); - static const person = PathEntityType._(r'person'); - static const user = PathEntityType._(r'user'); - - /// List of all possible values in this [enum][PathEntityType]. - static const values = [ - asset, - person, - user, - ]; - - static PathEntityType? fromJson(dynamic value) => PathEntityTypeTypeTransformer().decode(value); - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = PathEntityType.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [PathEntityType] to String, -/// and [decode] dynamic data back to [PathEntityType]. -class PathEntityTypeTypeTransformer { - factory PathEntityTypeTypeTransformer() => _instance ??= const PathEntityTypeTypeTransformer._(); - - const PathEntityTypeTypeTransformer._(); - - String encode(PathEntityType data) => data.value; - - /// Decodes a [dynamic value][data] to a PathEntityType. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - PathEntityType? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'asset': return PathEntityType.asset; - case r'person': return PathEntityType.person; - case r'user': return PathEntityType.user; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [PathEntityTypeTypeTransformer] instance. - static PathEntityTypeTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/path_type.dart b/mobile/openapi/lib/model/path_type.dart deleted file mode 100644 index 55453ed1e8..0000000000 --- a/mobile/openapi/lib/model/path_type.dart +++ /dev/null @@ -1,103 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - - -class PathType { - /// Instantiate a new enum with the provided [value]. - const PathType._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const original = PathType._(r'original'); - static const fullsize = PathType._(r'fullsize'); - static const preview = PathType._(r'preview'); - static const thumbnail = PathType._(r'thumbnail'); - static const encodedVideo = PathType._(r'encoded_video'); - static const sidecar = PathType._(r'sidecar'); - static const face = PathType._(r'face'); - static const profile = PathType._(r'profile'); - - /// List of all possible values in this [enum][PathType]. - static const values = [ - original, - fullsize, - preview, - thumbnail, - encodedVideo, - sidecar, - face, - profile, - ]; - - static PathType? fromJson(dynamic value) => PathTypeTypeTransformer().decode(value); - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = PathType.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [PathType] to String, -/// and [decode] dynamic data back to [PathType]. -class PathTypeTypeTransformer { - factory PathTypeTypeTransformer() => _instance ??= const PathTypeTypeTransformer._(); - - const PathTypeTypeTransformer._(); - - String encode(PathType data) => data.value; - - /// Decodes a [dynamic value][data] to a PathType. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - PathType? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'original': return PathType.original; - case r'fullsize': return PathType.fullsize; - case r'preview': return PathType.preview; - case r'thumbnail': return PathType.thumbnail; - case r'encoded_video': return PathType.encodedVideo; - case r'sidecar': return PathType.sidecar; - case r'face': return PathType.face; - case r'profile': return PathType.profile; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [PathTypeTypeTransformer] instance. - static PathTypeTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index 1244a434b6..a85b5002bf 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -66,6 +66,10 @@ class Permission { static const memoryPeriodRead = Permission._(r'memory.read'); static const memoryPeriodUpdate = Permission._(r'memory.update'); static const memoryPeriodDelete = Permission._(r'memory.delete'); + static const notificationPeriodCreate = Permission._(r'notification.create'); + static const notificationPeriodRead = Permission._(r'notification.read'); + static const notificationPeriodUpdate = Permission._(r'notification.update'); + static const notificationPeriodDelete = Permission._(r'notification.delete'); static const partnerPeriodCreate = Permission._(r'partner.create'); static const partnerPeriodRead = Permission._(r'partner.read'); static const partnerPeriodUpdate = Permission._(r'partner.update'); @@ -77,9 +81,11 @@ class Permission { static const personPeriodStatistics = Permission._(r'person.statistics'); static const personPeriodMerge = Permission._(r'person.merge'); static const personPeriodReassign = Permission._(r'person.reassign'); + static const sessionPeriodCreate = Permission._(r'session.create'); static const sessionPeriodRead = Permission._(r'session.read'); static const sessionPeriodUpdate = Permission._(r'session.update'); static const sessionPeriodDelete = Permission._(r'session.delete'); + static const sessionPeriodLock = Permission._(r'session.lock'); static const sharedLinkPeriodCreate = Permission._(r'sharedLink.create'); static const sharedLinkPeriodRead = Permission._(r'sharedLink.read'); static const sharedLinkPeriodUpdate = Permission._(r'sharedLink.update'); @@ -147,6 +153,10 @@ class Permission { memoryPeriodRead, memoryPeriodUpdate, memoryPeriodDelete, + notificationPeriodCreate, + notificationPeriodRead, + notificationPeriodUpdate, + notificationPeriodDelete, partnerPeriodCreate, partnerPeriodRead, partnerPeriodUpdate, @@ -158,9 +168,11 @@ class Permission { personPeriodStatistics, personPeriodMerge, personPeriodReassign, + sessionPeriodCreate, sessionPeriodRead, sessionPeriodUpdate, sessionPeriodDelete, + sessionPeriodLock, sharedLinkPeriodCreate, sharedLinkPeriodRead, sharedLinkPeriodUpdate, @@ -263,6 +275,10 @@ class PermissionTypeTransformer { case r'memory.read': return Permission.memoryPeriodRead; case r'memory.update': return Permission.memoryPeriodUpdate; case r'memory.delete': return Permission.memoryPeriodDelete; + case r'notification.create': return Permission.notificationPeriodCreate; + case r'notification.read': return Permission.notificationPeriodRead; + case r'notification.update': return Permission.notificationPeriodUpdate; + case r'notification.delete': return Permission.notificationPeriodDelete; case r'partner.create': return Permission.partnerPeriodCreate; case r'partner.read': return Permission.partnerPeriodRead; case r'partner.update': return Permission.partnerPeriodUpdate; @@ -274,9 +290,11 @@ class PermissionTypeTransformer { case r'person.statistics': return Permission.personPeriodStatistics; case r'person.merge': return Permission.personPeriodMerge; case r'person.reassign': return Permission.personPeriodReassign; + case r'session.create': return Permission.sessionPeriodCreate; case r'session.read': return Permission.sessionPeriodRead; case r'session.update': return Permission.sessionPeriodUpdate; case r'session.delete': return Permission.sessionPeriodDelete; + case r'session.lock': return Permission.sessionPeriodLock; case r'sharedLink.create': return Permission.sharedLinkPeriodCreate; case r'sharedLink.read': return Permission.sharedLinkPeriodRead; case r'sharedLink.update': return Permission.sharedLinkPeriodUpdate; diff --git a/mobile/openapi/lib/model/pin_code_change_dto.dart b/mobile/openapi/lib/model/pin_code_change_dto.dart new file mode 100644 index 0000000000..2e9967aa6b --- /dev/null +++ b/mobile/openapi/lib/model/pin_code_change_dto.dart @@ -0,0 +1,133 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class PinCodeChangeDto { + /// Returns a new [PinCodeChangeDto] instance. + PinCodeChangeDto({ + required this.newPinCode, + this.password, + this.pinCode, + }); + + String newPinCode; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? password; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? pinCode; + + @override + bool operator ==(Object other) => identical(this, other) || other is PinCodeChangeDto && + other.newPinCode == newPinCode && + other.password == password && + other.pinCode == pinCode; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (newPinCode.hashCode) + + (password == null ? 0 : password!.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode); + + @override + String toString() => 'PinCodeChangeDto[newPinCode=$newPinCode, password=$password, pinCode=$pinCode]'; + + Map toJson() { + final json = {}; + json[r'newPinCode'] = this.newPinCode; + if (this.password != null) { + json[r'password'] = this.password; + } else { + // json[r'password'] = null; + } + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + return json; + } + + /// Returns a new [PinCodeChangeDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PinCodeChangeDto? fromJson(dynamic value) { + upgradeDto(value, "PinCodeChangeDto"); + if (value is Map) { + final json = value.cast(); + + return PinCodeChangeDto( + newPinCode: mapValueOfType(json, r'newPinCode')!, + password: mapValueOfType(json, r'password'), + pinCode: mapValueOfType(json, r'pinCode'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PinCodeChangeDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PinCodeChangeDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PinCodeChangeDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PinCodeChangeDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'newPinCode', + }; +} + diff --git a/mobile/openapi/lib/model/pin_code_reset_dto.dart b/mobile/openapi/lib/model/pin_code_reset_dto.dart new file mode 100644 index 0000000000..3585348675 --- /dev/null +++ b/mobile/openapi/lib/model/pin_code_reset_dto.dart @@ -0,0 +1,125 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class PinCodeResetDto { + /// Returns a new [PinCodeResetDto] instance. + PinCodeResetDto({ + this.password, + this.pinCode, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? password; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? pinCode; + + @override + bool operator ==(Object other) => identical(this, other) || other is PinCodeResetDto && + other.password == password && + other.pinCode == pinCode; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (password == null ? 0 : password!.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode); + + @override + String toString() => 'PinCodeResetDto[password=$password, pinCode=$pinCode]'; + + Map toJson() { + final json = {}; + if (this.password != null) { + json[r'password'] = this.password; + } else { + // json[r'password'] = null; + } + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + return json; + } + + /// Returns a new [PinCodeResetDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PinCodeResetDto? fromJson(dynamic value) { + upgradeDto(value, "PinCodeResetDto"); + if (value is Map) { + final json = value.cast(); + + return PinCodeResetDto( + password: mapValueOfType(json, r'password'), + pinCode: mapValueOfType(json, r'pinCode'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PinCodeResetDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PinCodeResetDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PinCodeResetDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PinCodeResetDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/file_checksum_dto.dart b/mobile/openapi/lib/model/pin_code_setup_dto.dart similarity index 56% rename from mobile/openapi/lib/model/file_checksum_dto.dart rename to mobile/openapi/lib/model/pin_code_setup_dto.dart index 7dc9ccdf2f..09933790de 100644 --- a/mobile/openapi/lib/model/file_checksum_dto.dart +++ b/mobile/openapi/lib/model/pin_code_setup_dto.dart @@ -10,54 +10,52 @@ part of openapi.api; -class FileChecksumDto { - /// Returns a new [FileChecksumDto] instance. - FileChecksumDto({ - this.filenames = const [], +class PinCodeSetupDto { + /// Returns a new [PinCodeSetupDto] instance. + PinCodeSetupDto({ + required this.pinCode, }); - List filenames; + String pinCode; @override - bool operator ==(Object other) => identical(this, other) || other is FileChecksumDto && - _deepEquality.equals(other.filenames, filenames); + bool operator ==(Object other) => identical(this, other) || other is PinCodeSetupDto && + other.pinCode == pinCode; @override int get hashCode => // ignore: unnecessary_parenthesis - (filenames.hashCode); + (pinCode.hashCode); @override - String toString() => 'FileChecksumDto[filenames=$filenames]'; + String toString() => 'PinCodeSetupDto[pinCode=$pinCode]'; Map toJson() { final json = {}; - json[r'filenames'] = this.filenames; + json[r'pinCode'] = this.pinCode; return json; } - /// Returns a new [FileChecksumDto] instance and imports its values from + /// Returns a new [PinCodeSetupDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static FileChecksumDto? fromJson(dynamic value) { - upgradeDto(value, "FileChecksumDto"); + static PinCodeSetupDto? fromJson(dynamic value) { + upgradeDto(value, "PinCodeSetupDto"); if (value is Map) { final json = value.cast(); - return FileChecksumDto( - filenames: json[r'filenames'] is Iterable - ? (json[r'filenames'] as Iterable).cast().toList(growable: false) - : const [], + return PinCodeSetupDto( + pinCode: mapValueOfType(json, r'pinCode')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = FileChecksumDto.fromJson(row); + final value = PinCodeSetupDto.fromJson(row); if (value != null) { result.add(value); } @@ -66,12 +64,12 @@ class FileChecksumDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = FileChecksumDto.fromJson(entry.value); + final value = PinCodeSetupDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -80,14 +78,14 @@ class FileChecksumDto { return map; } - // maps a json object with a list of FileChecksumDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of PinCodeSetupDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = FileChecksumDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = PinCodeSetupDto.listFromJson(entry.value, growable: growable,); } } return map; @@ -95,7 +93,7 @@ class FileChecksumDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'filenames', + 'pinCode', }; } diff --git a/mobile/openapi/lib/model/random_search_dto.dart b/mobile/openapi/lib/model/random_search_dto.dart index 10727ec10d..0284212efc 100644 --- a/mobile/openapi/lib/model/random_search_dto.dart +++ b/mobile/openapi/lib/model/random_search_dto.dart @@ -18,13 +18,11 @@ class RandomSearchDto { this.createdAfter, this.createdBefore, this.deviceId, - this.isArchived, this.isEncoded, this.isFavorite, this.isMotion, this.isNotInAlbum, this.isOffline, - this.isVisible, this.lensModel, this.libraryId, this.make, @@ -41,7 +39,7 @@ class RandomSearchDto { this.type, this.updatedAfter, this.updatedBefore, - this.withArchived = false, + this.visibility, this.withDeleted, this.withExif, this.withPeople, @@ -76,14 +74,6 @@ class RandomSearchDto { /// String? deviceId; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isArchived; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -124,14 +114,6 @@ class RandomSearchDto { /// bool? isOffline; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isVisible; - String? lensModel; String? libraryId; @@ -228,7 +210,13 @@ class RandomSearchDto { /// DateTime? updatedBefore; - bool withArchived; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetVisibility? visibility; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -269,13 +257,11 @@ class RandomSearchDto { other.createdAfter == createdAfter && other.createdBefore == createdBefore && other.deviceId == deviceId && - other.isArchived == isArchived && other.isEncoded == isEncoded && other.isFavorite == isFavorite && other.isMotion == isMotion && other.isNotInAlbum == isNotInAlbum && other.isOffline == isOffline && - other.isVisible == isVisible && other.lensModel == lensModel && other.libraryId == libraryId && other.make == make && @@ -292,7 +278,7 @@ class RandomSearchDto { other.type == type && other.updatedAfter == updatedAfter && other.updatedBefore == updatedBefore && - other.withArchived == withArchived && + other.visibility == visibility && other.withDeleted == withDeleted && other.withExif == withExif && other.withPeople == withPeople && @@ -306,13 +292,11 @@ class RandomSearchDto { (createdAfter == null ? 0 : createdAfter!.hashCode) + (createdBefore == null ? 0 : createdBefore!.hashCode) + (deviceId == null ? 0 : deviceId!.hashCode) + - (isArchived == null ? 0 : isArchived!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + (isMotion == null ? 0 : isMotion!.hashCode) + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + (isOffline == null ? 0 : isOffline!.hashCode) + - (isVisible == null ? 0 : isVisible!.hashCode) + (lensModel == null ? 0 : lensModel!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) + (make == null ? 0 : make!.hashCode) + @@ -329,14 +313,14 @@ class RandomSearchDto { (type == null ? 0 : type!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) + - (withArchived.hashCode) + + (visibility == null ? 0 : visibility!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) + (withExif == null ? 0 : withExif!.hashCode) + (withPeople == null ? 0 : withPeople!.hashCode) + (withStacked == null ? 0 : withStacked!.hashCode); @override - String toString() => 'RandomSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, personIds=$personIds, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; + String toString() => 'RandomSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, personIds=$personIds, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; Map toJson() { final json = {}; @@ -365,11 +349,6 @@ class RandomSearchDto { } else { // json[r'deviceId'] = null; } - if (this.isArchived != null) { - json[r'isArchived'] = this.isArchived; - } else { - // json[r'isArchived'] = null; - } if (this.isEncoded != null) { json[r'isEncoded'] = this.isEncoded; } else { @@ -395,11 +374,6 @@ class RandomSearchDto { } else { // json[r'isOffline'] = null; } - if (this.isVisible != null) { - json[r'isVisible'] = this.isVisible; - } else { - // json[r'isVisible'] = null; - } if (this.lensModel != null) { json[r'lensModel'] = this.lensModel; } else { @@ -472,7 +446,11 @@ class RandomSearchDto { } else { // json[r'updatedBefore'] = null; } - json[r'withArchived'] = this.withArchived; + if (this.visibility != null) { + json[r'visibility'] = this.visibility; + } else { + // json[r'visibility'] = null; + } if (this.withDeleted != null) { json[r'withDeleted'] = this.withDeleted; } else { @@ -510,13 +488,11 @@ class RandomSearchDto { createdAfter: mapDateTime(json, r'createdAfter', r''), createdBefore: mapDateTime(json, r'createdBefore', r''), deviceId: mapValueOfType(json, r'deviceId'), - isArchived: mapValueOfType(json, r'isArchived'), isEncoded: mapValueOfType(json, r'isEncoded'), isFavorite: mapValueOfType(json, r'isFavorite'), isMotion: mapValueOfType(json, r'isMotion'), isNotInAlbum: mapValueOfType(json, r'isNotInAlbum'), isOffline: mapValueOfType(json, r'isOffline'), - isVisible: mapValueOfType(json, r'isVisible'), lensModel: mapValueOfType(json, r'lensModel'), libraryId: mapValueOfType(json, r'libraryId'), make: mapValueOfType(json, r'make'), @@ -537,7 +513,7 @@ class RandomSearchDto { type: AssetTypeEnum.fromJson(json[r'type']), updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''), - withArchived: mapValueOfType(json, r'withArchived') ?? false, + visibility: AssetVisibility.fromJson(json[r'visibility']), withDeleted: mapValueOfType(json, r'withDeleted'), withExif: mapValueOfType(json, r'withExif'), withPeople: mapValueOfType(json, r'withPeople'), diff --git a/mobile/openapi/lib/model/session_create_dto.dart b/mobile/openapi/lib/model/session_create_dto.dart new file mode 100644 index 0000000000..aacf1150a5 --- /dev/null +++ b/mobile/openapi/lib/model/session_create_dto.dart @@ -0,0 +1,145 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SessionCreateDto { + /// Returns a new [SessionCreateDto] instance. + SessionCreateDto({ + this.deviceOS, + this.deviceType, + this.duration, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? deviceOS; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? deviceType; + + /// session duration, in seconds + /// + /// Minimum value: 1 + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? duration; + + @override + bool operator ==(Object other) => identical(this, other) || other is SessionCreateDto && + other.deviceOS == deviceOS && + other.deviceType == deviceType && + other.duration == duration; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (deviceOS == null ? 0 : deviceOS!.hashCode) + + (deviceType == null ? 0 : deviceType!.hashCode) + + (duration == null ? 0 : duration!.hashCode); + + @override + String toString() => 'SessionCreateDto[deviceOS=$deviceOS, deviceType=$deviceType, duration=$duration]'; + + Map toJson() { + final json = {}; + if (this.deviceOS != null) { + json[r'deviceOS'] = this.deviceOS; + } else { + // json[r'deviceOS'] = null; + } + if (this.deviceType != null) { + json[r'deviceType'] = this.deviceType; + } else { + // json[r'deviceType'] = null; + } + if (this.duration != null) { + json[r'duration'] = this.duration; + } else { + // json[r'duration'] = null; + } + return json; + } + + /// Returns a new [SessionCreateDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SessionCreateDto? fromJson(dynamic value) { + upgradeDto(value, "SessionCreateDto"); + if (value is Map) { + final json = value.cast(); + + return SessionCreateDto( + deviceOS: mapValueOfType(json, r'deviceOS'), + deviceType: mapValueOfType(json, r'deviceType'), + duration: num.parse('${json[r'duration']}'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SessionCreateDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SessionCreateDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SessionCreateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SessionCreateDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/session_create_response_dto.dart b/mobile/openapi/lib/model/session_create_response_dto.dart new file mode 100644 index 0000000000..ab1c4ca2d8 --- /dev/null +++ b/mobile/openapi/lib/model/session_create_response_dto.dart @@ -0,0 +1,164 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SessionCreateResponseDto { + /// Returns a new [SessionCreateResponseDto] instance. + SessionCreateResponseDto({ + required this.createdAt, + required this.current, + required this.deviceOS, + required this.deviceType, + this.expiresAt, + required this.id, + required this.token, + required this.updatedAt, + }); + + String createdAt; + + bool current; + + String deviceOS; + + String deviceType; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? expiresAt; + + String id; + + String token; + + String updatedAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is SessionCreateResponseDto && + other.createdAt == createdAt && + other.current == current && + other.deviceOS == deviceOS && + other.deviceType == deviceType && + other.expiresAt == expiresAt && + other.id == id && + other.token == token && + other.updatedAt == updatedAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (current.hashCode) + + (deviceOS.hashCode) + + (deviceType.hashCode) + + (expiresAt == null ? 0 : expiresAt!.hashCode) + + (id.hashCode) + + (token.hashCode) + + (updatedAt.hashCode); + + @override + String toString() => 'SessionCreateResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, token=$token, updatedAt=$updatedAt]'; + + Map toJson() { + final json = {}; + json[r'createdAt'] = this.createdAt; + json[r'current'] = this.current; + json[r'deviceOS'] = this.deviceOS; + json[r'deviceType'] = this.deviceType; + if (this.expiresAt != null) { + json[r'expiresAt'] = this.expiresAt; + } else { + // json[r'expiresAt'] = null; + } + json[r'id'] = this.id; + json[r'token'] = this.token; + json[r'updatedAt'] = this.updatedAt; + return json; + } + + /// Returns a new [SessionCreateResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SessionCreateResponseDto? fromJson(dynamic value) { + upgradeDto(value, "SessionCreateResponseDto"); + if (value is Map) { + final json = value.cast(); + + return SessionCreateResponseDto( + createdAt: mapValueOfType(json, r'createdAt')!, + current: mapValueOfType(json, r'current')!, + deviceOS: mapValueOfType(json, r'deviceOS')!, + deviceType: mapValueOfType(json, r'deviceType')!, + expiresAt: mapValueOfType(json, r'expiresAt'), + id: mapValueOfType(json, r'id')!, + token: mapValueOfType(json, r'token')!, + updatedAt: mapValueOfType(json, r'updatedAt')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SessionCreateResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SessionCreateResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SessionCreateResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SessionCreateResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'createdAt', + 'current', + 'deviceOS', + 'deviceType', + 'id', + 'token', + 'updatedAt', + }; +} + diff --git a/mobile/openapi/lib/model/session_response_dto.dart b/mobile/openapi/lib/model/session_response_dto.dart index 92e2dc6067..cf9eb08a78 100644 --- a/mobile/openapi/lib/model/session_response_dto.dart +++ b/mobile/openapi/lib/model/session_response_dto.dart @@ -17,6 +17,7 @@ class SessionResponseDto { required this.current, required this.deviceOS, required this.deviceType, + this.expiresAt, required this.id, required this.updatedAt, }); @@ -29,6 +30,14 @@ class SessionResponseDto { String deviceType; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? expiresAt; + String id; String updatedAt; @@ -39,6 +48,7 @@ class SessionResponseDto { other.current == current && other.deviceOS == deviceOS && other.deviceType == deviceType && + other.expiresAt == expiresAt && other.id == id && other.updatedAt == updatedAt; @@ -49,11 +59,12 @@ class SessionResponseDto { (current.hashCode) + (deviceOS.hashCode) + (deviceType.hashCode) + + (expiresAt == null ? 0 : expiresAt!.hashCode) + (id.hashCode) + (updatedAt.hashCode); @override - String toString() => 'SessionResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, id=$id, updatedAt=$updatedAt]'; + String toString() => 'SessionResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -61,6 +72,11 @@ class SessionResponseDto { json[r'current'] = this.current; json[r'deviceOS'] = this.deviceOS; json[r'deviceType'] = this.deviceType; + if (this.expiresAt != null) { + json[r'expiresAt'] = this.expiresAt; + } else { + // json[r'expiresAt'] = null; + } json[r'id'] = this.id; json[r'updatedAt'] = this.updatedAt; return json; @@ -79,6 +95,7 @@ class SessionResponseDto { current: mapValueOfType(json, r'current')!, deviceOS: mapValueOfType(json, r'deviceOS')!, deviceType: mapValueOfType(json, r'deviceType')!, + expiresAt: mapValueOfType(json, r'expiresAt'), id: mapValueOfType(json, r'id')!, updatedAt: mapValueOfType(json, r'updatedAt')!, ); diff --git a/mobile/openapi/lib/model/session_unlock_dto.dart b/mobile/openapi/lib/model/session_unlock_dto.dart new file mode 100644 index 0000000000..4cfeb14385 --- /dev/null +++ b/mobile/openapi/lib/model/session_unlock_dto.dart @@ -0,0 +1,125 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SessionUnlockDto { + /// Returns a new [SessionUnlockDto] instance. + SessionUnlockDto({ + this.password, + this.pinCode, + }); + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? password; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? pinCode; + + @override + bool operator ==(Object other) => identical(this, other) || other is SessionUnlockDto && + other.password == password && + other.pinCode == pinCode; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (password == null ? 0 : password!.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode); + + @override + String toString() => 'SessionUnlockDto[password=$password, pinCode=$pinCode]'; + + Map toJson() { + final json = {}; + if (this.password != null) { + json[r'password'] = this.password; + } else { + // json[r'password'] = null; + } + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + return json; + } + + /// Returns a new [SessionUnlockDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SessionUnlockDto? fromJson(dynamic value) { + upgradeDto(value, "SessionUnlockDto"); + if (value is Map) { + final json = value.cast(); + + return SessionUnlockDto( + password: mapValueOfType(json, r'password'), + pinCode: mapValueOfType(json, r'pinCode'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SessionUnlockDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SessionUnlockDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SessionUnlockDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SessionUnlockDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + }; +} + diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart index 47c800ff09..a915d97b31 100644 --- a/mobile/openapi/lib/model/smart_search_dto.dart +++ b/mobile/openapi/lib/model/smart_search_dto.dart @@ -18,13 +18,11 @@ class SmartSearchDto { this.createdAfter, this.createdBefore, this.deviceId, - this.isArchived, this.isEncoded, this.isFavorite, this.isMotion, this.isNotInAlbum, this.isOffline, - this.isVisible, this.language, this.lensModel, this.libraryId, @@ -44,7 +42,7 @@ class SmartSearchDto { this.type, this.updatedAfter, this.updatedBefore, - this.withArchived = false, + this.visibility, this.withDeleted, this.withExif, }); @@ -77,14 +75,6 @@ class SmartSearchDto { /// String? deviceId; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isArchived; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -125,14 +115,6 @@ class SmartSearchDto { /// bool? isOffline; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isVisible; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -248,7 +230,13 @@ class SmartSearchDto { /// DateTime? updatedBefore; - bool withArchived; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetVisibility? visibility; /// /// Please note: This property should have been non-nullable! Since the specification file @@ -273,13 +261,11 @@ class SmartSearchDto { other.createdAfter == createdAfter && other.createdBefore == createdBefore && other.deviceId == deviceId && - other.isArchived == isArchived && other.isEncoded == isEncoded && other.isFavorite == isFavorite && other.isMotion == isMotion && other.isNotInAlbum == isNotInAlbum && other.isOffline == isOffline && - other.isVisible == isVisible && other.language == language && other.lensModel == lensModel && other.libraryId == libraryId && @@ -299,7 +285,7 @@ class SmartSearchDto { other.type == type && other.updatedAfter == updatedAfter && other.updatedBefore == updatedBefore && - other.withArchived == withArchived && + other.visibility == visibility && other.withDeleted == withDeleted && other.withExif == withExif; @@ -311,13 +297,11 @@ class SmartSearchDto { (createdAfter == null ? 0 : createdAfter!.hashCode) + (createdBefore == null ? 0 : createdBefore!.hashCode) + (deviceId == null ? 0 : deviceId!.hashCode) + - (isArchived == null ? 0 : isArchived!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + (isMotion == null ? 0 : isMotion!.hashCode) + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + (isOffline == null ? 0 : isOffline!.hashCode) + - (isVisible == null ? 0 : isVisible!.hashCode) + (language == null ? 0 : language!.hashCode) + (lensModel == null ? 0 : lensModel!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) + @@ -337,12 +321,12 @@ class SmartSearchDto { (type == null ? 0 : type!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) + - (withArchived.hashCode) + + (visibility == null ? 0 : visibility!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) + (withExif == null ? 0 : withExif!.hashCode); @override - String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif]'; + String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]'; Map toJson() { final json = {}; @@ -371,11 +355,6 @@ class SmartSearchDto { } else { // json[r'deviceId'] = null; } - if (this.isArchived != null) { - json[r'isArchived'] = this.isArchived; - } else { - // json[r'isArchived'] = null; - } if (this.isEncoded != null) { json[r'isEncoded'] = this.isEncoded; } else { @@ -401,11 +380,6 @@ class SmartSearchDto { } else { // json[r'isOffline'] = null; } - if (this.isVisible != null) { - json[r'isVisible'] = this.isVisible; - } else { - // json[r'isVisible'] = null; - } if (this.language != null) { json[r'language'] = this.language; } else { @@ -489,7 +463,11 @@ class SmartSearchDto { } else { // json[r'updatedBefore'] = null; } - json[r'withArchived'] = this.withArchived; + if (this.visibility != null) { + json[r'visibility'] = this.visibility; + } else { + // json[r'visibility'] = null; + } if (this.withDeleted != null) { json[r'withDeleted'] = this.withDeleted; } else { @@ -517,13 +495,11 @@ class SmartSearchDto { createdAfter: mapDateTime(json, r'createdAfter', r''), createdBefore: mapDateTime(json, r'createdBefore', r''), deviceId: mapValueOfType(json, r'deviceId'), - isArchived: mapValueOfType(json, r'isArchived'), isEncoded: mapValueOfType(json, r'isEncoded'), isFavorite: mapValueOfType(json, r'isFavorite'), isMotion: mapValueOfType(json, r'isMotion'), isNotInAlbum: mapValueOfType(json, r'isNotInAlbum'), isOffline: mapValueOfType(json, r'isOffline'), - isVisible: mapValueOfType(json, r'isVisible'), language: mapValueOfType(json, r'language'), lensModel: mapValueOfType(json, r'lensModel'), libraryId: mapValueOfType(json, r'libraryId'), @@ -547,7 +523,7 @@ class SmartSearchDto { type: AssetTypeEnum.fromJson(json[r'type']), updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''), - withArchived: mapValueOfType(json, r'withArchived') ?? false, + visibility: AssetVisibility.fromJson(json[r'visibility']), withDeleted: mapValueOfType(json, r'withDeleted'), withExif: mapValueOfType(json, r'withExif'), ); diff --git a/mobile/openapi/lib/model/avatar_response.dart b/mobile/openapi/lib/model/sync_album_delete_v1.dart similarity index 54% rename from mobile/openapi/lib/model/avatar_response.dart rename to mobile/openapi/lib/model/sync_album_delete_v1.dart index 8ce0287565..ae5ba3da5d 100644 --- a/mobile/openapi/lib/model/avatar_response.dart +++ b/mobile/openapi/lib/model/sync_album_delete_v1.dart @@ -10,52 +10,52 @@ part of openapi.api; -class AvatarResponse { - /// Returns a new [AvatarResponse] instance. - AvatarResponse({ - required this.color, +class SyncAlbumDeleteV1 { + /// Returns a new [SyncAlbumDeleteV1] instance. + SyncAlbumDeleteV1({ + required this.albumId, }); - UserAvatarColor color; + String albumId; @override - bool operator ==(Object other) => identical(this, other) || other is AvatarResponse && - other.color == color; + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumDeleteV1 && + other.albumId == albumId; @override int get hashCode => // ignore: unnecessary_parenthesis - (color.hashCode); + (albumId.hashCode); @override - String toString() => 'AvatarResponse[color=$color]'; + String toString() => 'SyncAlbumDeleteV1[albumId=$albumId]'; Map toJson() { final json = {}; - json[r'color'] = this.color; + json[r'albumId'] = this.albumId; return json; } - /// Returns a new [AvatarResponse] instance and imports its values from + /// Returns a new [SyncAlbumDeleteV1] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static AvatarResponse? fromJson(dynamic value) { - upgradeDto(value, "AvatarResponse"); + static SyncAlbumDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumDeleteV1"); if (value is Map) { final json = value.cast(); - return AvatarResponse( - color: UserAvatarColor.fromJson(json[r'color'])!, + return SyncAlbumDeleteV1( + albumId: mapValueOfType(json, r'albumId')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = AvatarResponse.fromJson(row); + final value = SyncAlbumDeleteV1.fromJson(row); if (value != null) { result.add(value); } @@ -64,12 +64,12 @@ class AvatarResponse { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = AvatarResponse.fromJson(entry.value); + final value = SyncAlbumDeleteV1.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -78,14 +78,14 @@ class AvatarResponse { return map; } - // maps a json object with a list of AvatarResponse-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of SyncAlbumDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = AvatarResponse.listFromJson(entry.value, growable: growable,); + map[entry.key] = SyncAlbumDeleteV1.listFromJson(entry.value, growable: growable,); } } return map; @@ -93,7 +93,7 @@ class AvatarResponse { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'color', + 'albumId', }; } diff --git a/mobile/openapi/lib/model/memory_lane_response_dto.dart b/mobile/openapi/lib/model/sync_album_user_delete_v1.dart similarity index 53% rename from mobile/openapi/lib/model/memory_lane_response_dto.dart rename to mobile/openapi/lib/model/sync_album_user_delete_v1.dart index 27248d05c1..f2b0fbee26 100644 --- a/mobile/openapi/lib/model/memory_lane_response_dto.dart +++ b/mobile/openapi/lib/model/sync_album_user_delete_v1.dart @@ -10,59 +10,59 @@ part of openapi.api; -class MemoryLaneResponseDto { - /// Returns a new [MemoryLaneResponseDto] instance. - MemoryLaneResponseDto({ - this.assets = const [], - required this.yearsAgo, +class SyncAlbumUserDeleteV1 { + /// Returns a new [SyncAlbumUserDeleteV1] instance. + SyncAlbumUserDeleteV1({ + required this.albumId, + required this.userId, }); - List assets; + String albumId; - int yearsAgo; + String userId; @override - bool operator ==(Object other) => identical(this, other) || other is MemoryLaneResponseDto && - _deepEquality.equals(other.assets, assets) && - other.yearsAgo == yearsAgo; + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumUserDeleteV1 && + other.albumId == albumId && + other.userId == userId; @override int get hashCode => // ignore: unnecessary_parenthesis - (assets.hashCode) + - (yearsAgo.hashCode); + (albumId.hashCode) + + (userId.hashCode); @override - String toString() => 'MemoryLaneResponseDto[assets=$assets, yearsAgo=$yearsAgo]'; + String toString() => 'SyncAlbumUserDeleteV1[albumId=$albumId, userId=$userId]'; Map toJson() { final json = {}; - json[r'assets'] = this.assets; - json[r'yearsAgo'] = this.yearsAgo; + json[r'albumId'] = this.albumId; + json[r'userId'] = this.userId; return json; } - /// Returns a new [MemoryLaneResponseDto] instance and imports its values from + /// Returns a new [SyncAlbumUserDeleteV1] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static MemoryLaneResponseDto? fromJson(dynamic value) { - upgradeDto(value, "MemoryLaneResponseDto"); + static SyncAlbumUserDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumUserDeleteV1"); if (value is Map) { final json = value.cast(); - return MemoryLaneResponseDto( - assets: AssetResponseDto.listFromJson(json[r'assets']), - yearsAgo: mapValueOfType(json, r'yearsAgo')!, + return SyncAlbumUserDeleteV1( + albumId: mapValueOfType(json, r'albumId')!, + userId: mapValueOfType(json, r'userId')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = MemoryLaneResponseDto.fromJson(row); + final value = SyncAlbumUserDeleteV1.fromJson(row); if (value != null) { result.add(value); } @@ -71,12 +71,12 @@ class MemoryLaneResponseDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = MemoryLaneResponseDto.fromJson(entry.value); + final value = SyncAlbumUserDeleteV1.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -85,14 +85,14 @@ class MemoryLaneResponseDto { return map; } - // maps a json object with a list of MemoryLaneResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of SyncAlbumUserDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = MemoryLaneResponseDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = SyncAlbumUserDeleteV1.listFromJson(entry.value, growable: growable,); } } return map; @@ -100,8 +100,8 @@ class MemoryLaneResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'assets', - 'yearsAgo', + 'albumId', + 'userId', }; } diff --git a/mobile/openapi/lib/model/sync_album_user_v1.dart b/mobile/openapi/lib/model/sync_album_user_v1.dart new file mode 100644 index 0000000000..c2b8ed7f48 --- /dev/null +++ b/mobile/openapi/lib/model/sync_album_user_v1.dart @@ -0,0 +1,189 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAlbumUserV1 { + /// Returns a new [SyncAlbumUserV1] instance. + SyncAlbumUserV1({ + required this.albumId, + required this.role, + required this.userId, + }); + + String albumId; + + SyncAlbumUserV1RoleEnum role; + + String userId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumUserV1 && + other.albumId == albumId && + other.role == role && + other.userId == userId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumId.hashCode) + + (role.hashCode) + + (userId.hashCode); + + @override + String toString() => 'SyncAlbumUserV1[albumId=$albumId, role=$role, userId=$userId]'; + + Map toJson() { + final json = {}; + json[r'albumId'] = this.albumId; + json[r'role'] = this.role; + json[r'userId'] = this.userId; + return json; + } + + /// Returns a new [SyncAlbumUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAlbumUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAlbumUserV1( + albumId: mapValueOfType(json, r'albumId')!, + role: SyncAlbumUserV1RoleEnum.fromJson(json[r'role'])!, + userId: mapValueOfType(json, r'userId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAlbumUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAlbumUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAlbumUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAlbumUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumId', + 'role', + 'userId', + }; +} + + +class SyncAlbumUserV1RoleEnum { + /// Instantiate a new enum with the provided [value]. + const SyncAlbumUserV1RoleEnum._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const editor = SyncAlbumUserV1RoleEnum._(r'editor'); + static const viewer = SyncAlbumUserV1RoleEnum._(r'viewer'); + + /// List of all possible values in this [enum][SyncAlbumUserV1RoleEnum]. + static const values = [ + editor, + viewer, + ]; + + static SyncAlbumUserV1RoleEnum? fromJson(dynamic value) => SyncAlbumUserV1RoleEnumTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAlbumUserV1RoleEnum.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [SyncAlbumUserV1RoleEnum] to String, +/// and [decode] dynamic data back to [SyncAlbumUserV1RoleEnum]. +class SyncAlbumUserV1RoleEnumTypeTransformer { + factory SyncAlbumUserV1RoleEnumTypeTransformer() => _instance ??= const SyncAlbumUserV1RoleEnumTypeTransformer._(); + + const SyncAlbumUserV1RoleEnumTypeTransformer._(); + + String encode(SyncAlbumUserV1RoleEnum data) => data.value; + + /// Decodes a [dynamic value][data] to a SyncAlbumUserV1RoleEnum. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + SyncAlbumUserV1RoleEnum? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'editor': return SyncAlbumUserV1RoleEnum.editor; + case r'viewer': return SyncAlbumUserV1RoleEnum.viewer; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [SyncAlbumUserV1RoleEnumTypeTransformer] instance. + static SyncAlbumUserV1RoleEnumTypeTransformer? _instance; +} + + diff --git a/mobile/openapi/lib/model/sync_album_v1.dart b/mobile/openapi/lib/model/sync_album_v1.dart new file mode 100644 index 0000000000..8ac8246d46 --- /dev/null +++ b/mobile/openapi/lib/model/sync_album_v1.dart @@ -0,0 +1,167 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAlbumV1 { + /// Returns a new [SyncAlbumV1] instance. + SyncAlbumV1({ + required this.createdAt, + required this.description, + required this.id, + required this.isActivityEnabled, + required this.name, + required this.order, + required this.ownerId, + required this.thumbnailAssetId, + required this.updatedAt, + }); + + DateTime createdAt; + + String description; + + String id; + + bool isActivityEnabled; + + String name; + + AssetOrder order; + + String ownerId; + + String? thumbnailAssetId; + + DateTime updatedAt; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAlbumV1 && + other.createdAt == createdAt && + other.description == description && + other.id == id && + other.isActivityEnabled == isActivityEnabled && + other.name == name && + other.order == order && + other.ownerId == ownerId && + other.thumbnailAssetId == thumbnailAssetId && + other.updatedAt == updatedAt; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (createdAt.hashCode) + + (description.hashCode) + + (id.hashCode) + + (isActivityEnabled.hashCode) + + (name.hashCode) + + (order.hashCode) + + (ownerId.hashCode) + + (thumbnailAssetId == null ? 0 : thumbnailAssetId!.hashCode) + + (updatedAt.hashCode); + + @override + String toString() => 'SyncAlbumV1[createdAt=$createdAt, description=$description, id=$id, isActivityEnabled=$isActivityEnabled, name=$name, order=$order, ownerId=$ownerId, thumbnailAssetId=$thumbnailAssetId, updatedAt=$updatedAt]'; + + Map toJson() { + final json = {}; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); + json[r'description'] = this.description; + json[r'id'] = this.id; + json[r'isActivityEnabled'] = this.isActivityEnabled; + json[r'name'] = this.name; + json[r'order'] = this.order; + json[r'ownerId'] = this.ownerId; + if (this.thumbnailAssetId != null) { + json[r'thumbnailAssetId'] = this.thumbnailAssetId; + } else { + // json[r'thumbnailAssetId'] = null; + } + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + return json; + } + + /// Returns a new [SyncAlbumV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAlbumV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAlbumV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAlbumV1( + createdAt: mapDateTime(json, r'createdAt', r'')!, + description: mapValueOfType(json, r'description')!, + id: mapValueOfType(json, r'id')!, + isActivityEnabled: mapValueOfType(json, r'isActivityEnabled')!, + name: mapValueOfType(json, r'name')!, + order: AssetOrder.fromJson(json[r'order'])!, + ownerId: mapValueOfType(json, r'ownerId')!, + thumbnailAssetId: mapValueOfType(json, r'thumbnailAssetId'), + updatedAt: mapDateTime(json, r'updatedAt', r'')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAlbumV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAlbumV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAlbumV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAlbumV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'createdAt', + 'description', + 'id', + 'isActivityEnabled', + 'name', + 'order', + 'ownerId', + 'thumbnailAssetId', + 'updatedAt', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 6f9d7d7eaf..f5d59b6ae9 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -19,11 +19,11 @@ class SyncAssetV1 { required this.fileModifiedAt, required this.id, required this.isFavorite, - required this.isVisible, required this.localDateTime, required this.ownerId, required this.thumbhash, required this.type, + required this.visibility, }); String checksum; @@ -38,8 +38,6 @@ class SyncAssetV1 { bool isFavorite; - bool isVisible; - DateTime? localDateTime; String ownerId; @@ -48,6 +46,8 @@ class SyncAssetV1 { SyncAssetV1TypeEnum type; + SyncAssetV1VisibilityEnum visibility; + @override bool operator ==(Object other) => identical(this, other) || other is SyncAssetV1 && other.checksum == checksum && @@ -56,11 +56,11 @@ class SyncAssetV1 { other.fileModifiedAt == fileModifiedAt && other.id == id && other.isFavorite == isFavorite && - other.isVisible == isVisible && other.localDateTime == localDateTime && other.ownerId == ownerId && other.thumbhash == thumbhash && - other.type == type; + other.type == type && + other.visibility == visibility; @override int get hashCode => @@ -71,14 +71,14 @@ class SyncAssetV1 { (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + (isFavorite.hashCode) + - (isVisible.hashCode) + (localDateTime == null ? 0 : localDateTime!.hashCode) + (ownerId.hashCode) + (thumbhash == null ? 0 : thumbhash!.hashCode) + - (type.hashCode); + (type.hashCode) + + (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, isVisible=$isVisible, localDateTime=$localDateTime, ownerId=$ownerId, thumbhash=$thumbhash, type=$type]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, localDateTime=$localDateTime, ownerId=$ownerId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -100,7 +100,6 @@ class SyncAssetV1 { } json[r'id'] = this.id; json[r'isFavorite'] = this.isFavorite; - json[r'isVisible'] = this.isVisible; if (this.localDateTime != null) { json[r'localDateTime'] = this.localDateTime!.toUtc().toIso8601String(); } else { @@ -113,6 +112,7 @@ class SyncAssetV1 { // json[r'thumbhash'] = null; } json[r'type'] = this.type; + json[r'visibility'] = this.visibility; return json; } @@ -131,11 +131,11 @@ class SyncAssetV1 { fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, isFavorite: mapValueOfType(json, r'isFavorite')!, - isVisible: mapValueOfType(json, r'isVisible')!, localDateTime: mapDateTime(json, r'localDateTime', r''), ownerId: mapValueOfType(json, r'ownerId')!, thumbhash: mapValueOfType(json, r'thumbhash'), type: SyncAssetV1TypeEnum.fromJson(json[r'type'])!, + visibility: SyncAssetV1VisibilityEnum.fromJson(json[r'visibility'])!, ); } return null; @@ -189,11 +189,11 @@ class SyncAssetV1 { 'fileModifiedAt', 'id', 'isFavorite', - 'isVisible', 'localDateTime', 'ownerId', 'thumbhash', 'type', + 'visibility', }; } @@ -277,3 +277,83 @@ class SyncAssetV1TypeEnumTypeTransformer { } + +class SyncAssetV1VisibilityEnum { + /// Instantiate a new enum with the provided [value]. + const SyncAssetV1VisibilityEnum._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const archive = SyncAssetV1VisibilityEnum._(r'archive'); + static const timeline = SyncAssetV1VisibilityEnum._(r'timeline'); + static const hidden = SyncAssetV1VisibilityEnum._(r'hidden'); + static const locked = SyncAssetV1VisibilityEnum._(r'locked'); + + /// List of all possible values in this [enum][SyncAssetV1VisibilityEnum]. + static const values = [ + archive, + timeline, + hidden, + locked, + ]; + + static SyncAssetV1VisibilityEnum? fromJson(dynamic value) => SyncAssetV1VisibilityEnumTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetV1VisibilityEnum.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [SyncAssetV1VisibilityEnum] to String, +/// and [decode] dynamic data back to [SyncAssetV1VisibilityEnum]. +class SyncAssetV1VisibilityEnumTypeTransformer { + factory SyncAssetV1VisibilityEnumTypeTransformer() => _instance ??= const SyncAssetV1VisibilityEnumTypeTransformer._(); + + const SyncAssetV1VisibilityEnumTypeTransformer._(); + + String encode(SyncAssetV1VisibilityEnum data) => data.value; + + /// Decodes a [dynamic value][data] to a SyncAssetV1VisibilityEnum. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + SyncAssetV1VisibilityEnum? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'archive': return SyncAssetV1VisibilityEnum.archive; + case r'timeline': return SyncAssetV1VisibilityEnum.timeline; + case r'hidden': return SyncAssetV1VisibilityEnum.hidden; + case r'locked': return SyncAssetV1VisibilityEnum.locked; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [SyncAssetV1VisibilityEnumTypeTransformer] instance. + static SyncAssetV1VisibilityEnumTypeTransformer? _instance; +} + + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 5e52a10e7a..600371545a 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -33,6 +33,10 @@ class SyncEntityType { static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1'); static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1'); static const partnerAssetExifV1 = SyncEntityType._(r'PartnerAssetExifV1'); + static const albumV1 = SyncEntityType._(r'AlbumV1'); + static const albumDeleteV1 = SyncEntityType._(r'AlbumDeleteV1'); + static const albumUserV1 = SyncEntityType._(r'AlbumUserV1'); + static const albumUserDeleteV1 = SyncEntityType._(r'AlbumUserDeleteV1'); /// List of all possible values in this [enum][SyncEntityType]. static const values = [ @@ -46,6 +50,10 @@ class SyncEntityType { partnerAssetV1, partnerAssetDeleteV1, partnerAssetExifV1, + albumV1, + albumDeleteV1, + albumUserV1, + albumUserDeleteV1, ]; static SyncEntityType? fromJson(dynamic value) => SyncEntityTypeTypeTransformer().decode(value); @@ -94,6 +102,10 @@ class SyncEntityTypeTypeTransformer { case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1; case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1; case r'PartnerAssetExifV1': return SyncEntityType.partnerAssetExifV1; + case r'AlbumV1': return SyncEntityType.albumV1; + case r'AlbumDeleteV1': return SyncEntityType.albumDeleteV1; + case r'AlbumUserV1': return SyncEntityType.albumUserV1; + case r'AlbumUserDeleteV1': return SyncEntityType.albumUserDeleteV1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 08f977ad57..c149c329de 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -29,6 +29,8 @@ class SyncRequestType { static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); static const partnerAssetsV1 = SyncRequestType._(r'PartnerAssetsV1'); static const partnerAssetExifsV1 = SyncRequestType._(r'PartnerAssetExifsV1'); + static const albumsV1 = SyncRequestType._(r'AlbumsV1'); + static const albumUsersV1 = SyncRequestType._(r'AlbumUsersV1'); /// List of all possible values in this [enum][SyncRequestType]. static const values = [ @@ -38,6 +40,8 @@ class SyncRequestType { assetExifsV1, partnerAssetsV1, partnerAssetExifsV1, + albumsV1, + albumUsersV1, ]; static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value); @@ -82,6 +86,8 @@ class SyncRequestTypeTypeTransformer { case r'AssetExifsV1': return SyncRequestType.assetExifsV1; case r'PartnerAssetsV1': return SyncRequestType.partnerAssetsV1; case r'PartnerAssetExifsV1': return SyncRequestType.partnerAssetExifsV1; + case r'AlbumsV1': return SyncRequestType.albumsV1; + case r'AlbumUsersV1': return SyncRequestType.albumUsersV1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/system_config_o_auth_dto.dart b/mobile/openapi/lib/model/system_config_o_auth_dto.dart index 9125bb7bba..24384a47b1 100644 --- a/mobile/openapi/lib/model/system_config_o_auth_dto.dart +++ b/mobile/openapi/lib/model/system_config_o_auth_dto.dart @@ -28,6 +28,8 @@ class SystemConfigOAuthDto { required this.signingAlgorithm, required this.storageLabelClaim, required this.storageQuotaClaim, + required this.timeout, + required this.tokenEndpointAuthMethod, }); bool autoLaunch; @@ -61,6 +63,11 @@ class SystemConfigOAuthDto { String storageQuotaClaim; + /// Minimum value: 1 + int timeout; + + OAuthTokenEndpointAuthMethod tokenEndpointAuthMethod; + @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigOAuthDto && other.autoLaunch == autoLaunch && @@ -77,7 +84,9 @@ class SystemConfigOAuthDto { other.scope == scope && other.signingAlgorithm == signingAlgorithm && other.storageLabelClaim == storageLabelClaim && - other.storageQuotaClaim == storageQuotaClaim; + other.storageQuotaClaim == storageQuotaClaim && + other.timeout == timeout && + other.tokenEndpointAuthMethod == tokenEndpointAuthMethod; @override int get hashCode => @@ -96,10 +105,12 @@ class SystemConfigOAuthDto { (scope.hashCode) + (signingAlgorithm.hashCode) + (storageLabelClaim.hashCode) + - (storageQuotaClaim.hashCode); + (storageQuotaClaim.hashCode) + + (timeout.hashCode) + + (tokenEndpointAuthMethod.hashCode); @override - String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim]'; + String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]'; Map toJson() { final json = {}; @@ -118,6 +129,8 @@ class SystemConfigOAuthDto { json[r'signingAlgorithm'] = this.signingAlgorithm; json[r'storageLabelClaim'] = this.storageLabelClaim; json[r'storageQuotaClaim'] = this.storageQuotaClaim; + json[r'timeout'] = this.timeout; + json[r'tokenEndpointAuthMethod'] = this.tokenEndpointAuthMethod; return json; } @@ -145,6 +158,8 @@ class SystemConfigOAuthDto { signingAlgorithm: mapValueOfType(json, r'signingAlgorithm')!, storageLabelClaim: mapValueOfType(json, r'storageLabelClaim')!, storageQuotaClaim: mapValueOfType(json, r'storageQuotaClaim')!, + timeout: mapValueOfType(json, r'timeout')!, + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.fromJson(json[r'tokenEndpointAuthMethod'])!, ); } return null; @@ -207,6 +222,8 @@ class SystemConfigOAuthDto { 'signingAlgorithm', 'storageLabelClaim', 'storageQuotaClaim', + 'timeout', + 'tokenEndpointAuthMethod', }; } diff --git a/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart new file mode 100644 index 0000000000..3f1406c019 --- /dev/null +++ b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart @@ -0,0 +1,241 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TimeBucketAssetResponseDto { + /// Returns a new [TimeBucketAssetResponseDto] instance. + TimeBucketAssetResponseDto({ + this.city = const [], + this.country = const [], + this.duration = const [], + this.id = const [], + this.isFavorite = const [], + this.isImage = const [], + this.isTrashed = const [], + this.livePhotoVideoId = const [], + this.localDateTime = const [], + this.ownerId = const [], + this.projectionType = const [], + this.ratio = const [], + this.stack = const [], + this.thumbhash = const [], + this.visibility = const [], + }); + + List city; + + List country; + + List duration; + + List id; + + List isFavorite; + + List isImage; + + List isTrashed; + + List livePhotoVideoId; + + List localDateTime; + + List ownerId; + + List projectionType; + + List ratio; + + /// (stack ID, stack asset count) tuple + List?> stack; + + List thumbhash; + + List visibility; + + @override + bool operator ==(Object other) => identical(this, other) || other is TimeBucketAssetResponseDto && + _deepEquality.equals(other.city, city) && + _deepEquality.equals(other.country, country) && + _deepEquality.equals(other.duration, duration) && + _deepEquality.equals(other.id, id) && + _deepEquality.equals(other.isFavorite, isFavorite) && + _deepEquality.equals(other.isImage, isImage) && + _deepEquality.equals(other.isTrashed, isTrashed) && + _deepEquality.equals(other.livePhotoVideoId, livePhotoVideoId) && + _deepEquality.equals(other.localDateTime, localDateTime) && + _deepEquality.equals(other.ownerId, ownerId) && + _deepEquality.equals(other.projectionType, projectionType) && + _deepEquality.equals(other.ratio, ratio) && + _deepEquality.equals(other.stack, stack) && + _deepEquality.equals(other.thumbhash, thumbhash) && + _deepEquality.equals(other.visibility, visibility); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (city.hashCode) + + (country.hashCode) + + (duration.hashCode) + + (id.hashCode) + + (isFavorite.hashCode) + + (isImage.hashCode) + + (isTrashed.hashCode) + + (livePhotoVideoId.hashCode) + + (localDateTime.hashCode) + + (ownerId.hashCode) + + (projectionType.hashCode) + + (ratio.hashCode) + + (stack.hashCode) + + (thumbhash.hashCode) + + (visibility.hashCode); + + @override + String toString() => 'TimeBucketAssetResponseDto[city=$city, country=$country, duration=$duration, id=$id, isFavorite=$isFavorite, isImage=$isImage, isTrashed=$isTrashed, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, ownerId=$ownerId, projectionType=$projectionType, ratio=$ratio, stack=$stack, thumbhash=$thumbhash, visibility=$visibility]'; + + Map toJson() { + final json = {}; + json[r'city'] = this.city; + json[r'country'] = this.country; + json[r'duration'] = this.duration; + json[r'id'] = this.id; + json[r'isFavorite'] = this.isFavorite; + json[r'isImage'] = this.isImage; + json[r'isTrashed'] = this.isTrashed; + json[r'livePhotoVideoId'] = this.livePhotoVideoId; + json[r'localDateTime'] = this.localDateTime; + json[r'ownerId'] = this.ownerId; + json[r'projectionType'] = this.projectionType; + json[r'ratio'] = this.ratio; + json[r'stack'] = this.stack; + json[r'thumbhash'] = this.thumbhash; + json[r'visibility'] = this.visibility; + return json; + } + + /// Returns a new [TimeBucketAssetResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TimeBucketAssetResponseDto? fromJson(dynamic value) { + upgradeDto(value, "TimeBucketAssetResponseDto"); + if (value is Map) { + final json = value.cast(); + + return TimeBucketAssetResponseDto( + city: json[r'city'] is Iterable + ? (json[r'city'] as Iterable).cast().toList(growable: false) + : const [], + country: json[r'country'] is Iterable + ? (json[r'country'] as Iterable).cast().toList(growable: false) + : const [], + duration: json[r'duration'] is Iterable + ? (json[r'duration'] as Iterable).cast().toList(growable: false) + : const [], + id: json[r'id'] is Iterable + ? (json[r'id'] as Iterable).cast().toList(growable: false) + : const [], + isFavorite: json[r'isFavorite'] is Iterable + ? (json[r'isFavorite'] as Iterable).cast().toList(growable: false) + : const [], + isImage: json[r'isImage'] is Iterable + ? (json[r'isImage'] as Iterable).cast().toList(growable: false) + : const [], + isTrashed: json[r'isTrashed'] is Iterable + ? (json[r'isTrashed'] as Iterable).cast().toList(growable: false) + : const [], + livePhotoVideoId: json[r'livePhotoVideoId'] is Iterable + ? (json[r'livePhotoVideoId'] as Iterable).cast().toList(growable: false) + : const [], + localDateTime: json[r'localDateTime'] is Iterable + ? (json[r'localDateTime'] as Iterable).cast().toList(growable: false) + : const [], + ownerId: json[r'ownerId'] is Iterable + ? (json[r'ownerId'] as Iterable).cast().toList(growable: false) + : const [], + projectionType: json[r'projectionType'] is Iterable + ? (json[r'projectionType'] as Iterable).cast().toList(growable: false) + : const [], + ratio: json[r'ratio'] is Iterable + ? (json[r'ratio'] as Iterable).cast().toList(growable: false) + : const [], + stack: json[r'stack'] is List + ? (json[r'stack'] as List).map((e) => + e == null ? null : (e as List).cast() + ).toList() + : const [], + thumbhash: json[r'thumbhash'] is Iterable + ? (json[r'thumbhash'] as Iterable).cast().toList(growable: false) + : const [], + visibility: AssetVisibility.listFromJson(json[r'visibility']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TimeBucketAssetResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TimeBucketAssetResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TimeBucketAssetResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TimeBucketAssetResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'city', + 'country', + 'duration', + 'id', + 'isFavorite', + 'isImage', + 'isTrashed', + 'livePhotoVideoId', + 'localDateTime', + 'ownerId', + 'projectionType', + 'ratio', + 'thumbhash', + 'visibility', + }; +} + diff --git a/mobile/openapi/lib/model/time_bucket_size.dart b/mobile/openapi/lib/model/time_bucket_size.dart deleted file mode 100644 index e843b43f43..0000000000 --- a/mobile/openapi/lib/model/time_bucket_size.dart +++ /dev/null @@ -1,85 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - - -class TimeBucketSize { - /// Instantiate a new enum with the provided [value]. - const TimeBucketSize._(this.value); - - /// The underlying value of this enum member. - final String value; - - @override - String toString() => value; - - String toJson() => value; - - static const DAY = TimeBucketSize._(r'DAY'); - static const MONTH = TimeBucketSize._(r'MONTH'); - - /// List of all possible values in this [enum][TimeBucketSize]. - static const values = [ - DAY, - MONTH, - ]; - - static TimeBucketSize? fromJson(dynamic value) => TimeBucketSizeTypeTransformer().decode(value); - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = TimeBucketSize.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } -} - -/// Transformation class that can [encode] an instance of [TimeBucketSize] to String, -/// and [decode] dynamic data back to [TimeBucketSize]. -class TimeBucketSizeTypeTransformer { - factory TimeBucketSizeTypeTransformer() => _instance ??= const TimeBucketSizeTypeTransformer._(); - - const TimeBucketSizeTypeTransformer._(); - - String encode(TimeBucketSize data) => data.value; - - /// Decodes a [dynamic value][data] to a TimeBucketSize. - /// - /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, - /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] - /// cannot be decoded successfully, then an [UnimplementedError] is thrown. - /// - /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, - /// and users are still using an old app with the old code. - TimeBucketSize? decode(dynamic data, {bool allowNull = true}) { - if (data != null) { - switch (data) { - case r'DAY': return TimeBucketSize.DAY; - case r'MONTH': return TimeBucketSize.MONTH; - default: - if (!allowNull) { - throw ArgumentError('Unknown enum value to decode: $data'); - } - } - } - return null; - } - - /// Singleton [TimeBucketSizeTypeTransformer] instance. - static TimeBucketSizeTypeTransformer? _instance; -} - diff --git a/mobile/openapi/lib/model/time_bucket_response_dto.dart b/mobile/openapi/lib/model/time_buckets_response_dto.dart similarity index 62% rename from mobile/openapi/lib/model/time_bucket_response_dto.dart rename to mobile/openapi/lib/model/time_buckets_response_dto.dart index 56044b27a8..8c9f8dab61 100644 --- a/mobile/openapi/lib/model/time_bucket_response_dto.dart +++ b/mobile/openapi/lib/model/time_buckets_response_dto.dart @@ -10,9 +10,9 @@ part of openapi.api; -class TimeBucketResponseDto { - /// Returns a new [TimeBucketResponseDto] instance. - TimeBucketResponseDto({ +class TimeBucketsResponseDto { + /// Returns a new [TimeBucketsResponseDto] instance. + TimeBucketsResponseDto({ required this.count, required this.timeBucket, }); @@ -22,7 +22,7 @@ class TimeBucketResponseDto { String timeBucket; @override - bool operator ==(Object other) => identical(this, other) || other is TimeBucketResponseDto && + bool operator ==(Object other) => identical(this, other) || other is TimeBucketsResponseDto && other.count == count && other.timeBucket == timeBucket; @@ -33,7 +33,7 @@ class TimeBucketResponseDto { (timeBucket.hashCode); @override - String toString() => 'TimeBucketResponseDto[count=$count, timeBucket=$timeBucket]'; + String toString() => 'TimeBucketsResponseDto[count=$count, timeBucket=$timeBucket]'; Map toJson() { final json = {}; @@ -42,15 +42,15 @@ class TimeBucketResponseDto { return json; } - /// Returns a new [TimeBucketResponseDto] instance and imports its values from + /// Returns a new [TimeBucketsResponseDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static TimeBucketResponseDto? fromJson(dynamic value) { - upgradeDto(value, "TimeBucketResponseDto"); + static TimeBucketsResponseDto? fromJson(dynamic value) { + upgradeDto(value, "TimeBucketsResponseDto"); if (value is Map) { final json = value.cast(); - return TimeBucketResponseDto( + return TimeBucketsResponseDto( count: mapValueOfType(json, r'count')!, timeBucket: mapValueOfType(json, r'timeBucket')!, ); @@ -58,11 +58,11 @@ class TimeBucketResponseDto { return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = TimeBucketResponseDto.fromJson(row); + final value = TimeBucketsResponseDto.fromJson(row); if (value != null) { result.add(value); } @@ -71,12 +71,12 @@ class TimeBucketResponseDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = TimeBucketResponseDto.fromJson(entry.value); + final value = TimeBucketsResponseDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -85,14 +85,14 @@ class TimeBucketResponseDto { return map; } - // maps a json object with a list of TimeBucketResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of TimeBucketsResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = TimeBucketResponseDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = TimeBucketsResponseDto.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/update_asset_dto.dart b/mobile/openapi/lib/model/update_asset_dto.dart index c6ae6d8e07..7b364f1387 100644 --- a/mobile/openapi/lib/model/update_asset_dto.dart +++ b/mobile/openapi/lib/model/update_asset_dto.dart @@ -15,12 +15,12 @@ class UpdateAssetDto { UpdateAssetDto({ this.dateTimeOriginal, this.description, - this.isArchived, this.isFavorite, this.latitude, this.livePhotoVideoId, this.longitude, this.rating, + this.visibility, }); /// @@ -39,14 +39,6 @@ class UpdateAssetDto { /// String? description; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isArchived; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -83,31 +75,39 @@ class UpdateAssetDto { /// num? rating; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + AssetVisibility? visibility; + @override bool operator ==(Object other) => identical(this, other) || other is UpdateAssetDto && other.dateTimeOriginal == dateTimeOriginal && other.description == description && - other.isArchived == isArchived && other.isFavorite == isFavorite && other.latitude == latitude && other.livePhotoVideoId == livePhotoVideoId && other.longitude == longitude && - other.rating == rating; + other.rating == rating && + other.visibility == visibility; @override int get hashCode => // ignore: unnecessary_parenthesis (dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) + (description == null ? 0 : description!.hashCode) + - (isArchived == null ? 0 : isArchived!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) + (latitude == null ? 0 : latitude!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (longitude == null ? 0 : longitude!.hashCode) + - (rating == null ? 0 : rating!.hashCode); + (rating == null ? 0 : rating!.hashCode) + + (visibility == null ? 0 : visibility!.hashCode); @override - String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, livePhotoVideoId=$livePhotoVideoId, longitude=$longitude, rating=$rating]'; + String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isFavorite=$isFavorite, latitude=$latitude, livePhotoVideoId=$livePhotoVideoId, longitude=$longitude, rating=$rating, visibility=$visibility]'; Map toJson() { final json = {}; @@ -121,11 +121,6 @@ class UpdateAssetDto { } else { // json[r'description'] = null; } - if (this.isArchived != null) { - json[r'isArchived'] = this.isArchived; - } else { - // json[r'isArchived'] = null; - } if (this.isFavorite != null) { json[r'isFavorite'] = this.isFavorite; } else { @@ -151,6 +146,11 @@ class UpdateAssetDto { } else { // json[r'rating'] = null; } + if (this.visibility != null) { + json[r'visibility'] = this.visibility; + } else { + // json[r'visibility'] = null; + } return json; } @@ -165,12 +165,12 @@ class UpdateAssetDto { return UpdateAssetDto( dateTimeOriginal: mapValueOfType(json, r'dateTimeOriginal'), description: mapValueOfType(json, r'description'), - isArchived: mapValueOfType(json, r'isArchived'), isFavorite: mapValueOfType(json, r'isFavorite'), latitude: num.parse('${json[r'latitude']}'), livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'), longitude: num.parse('${json[r'longitude']}'), rating: num.parse('${json[r'rating']}'), + visibility: AssetVisibility.fromJson(json[r'visibility']), ); } return null; diff --git a/mobile/openapi/lib/model/user_admin_create_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart index 4bd1266426..1477c82ca1 100644 --- a/mobile/openapi/lib/model/user_admin_create_dto.dart +++ b/mobile/openapi/lib/model/user_admin_create_dto.dart @@ -13,6 +13,7 @@ part of openapi.api; class UserAdminCreateDto { /// Returns a new [UserAdminCreateDto] instance. UserAdminCreateDto({ + this.avatarColor, required this.email, required this.name, this.notify, @@ -22,6 +23,8 @@ class UserAdminCreateDto { this.storageLabel, }); + UserAvatarColor? avatarColor; + String email; String name; @@ -51,6 +54,7 @@ class UserAdminCreateDto { @override bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.notify == notify && @@ -62,6 +66,7 @@ class UserAdminCreateDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email.hashCode) + (name.hashCode) + (notify == null ? 0 : notify!.hashCode) + @@ -71,10 +76,15 @@ class UserAdminCreateDto { (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminCreateDto[email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminCreateDto[avatarColor=$avatarColor, email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } json[r'email'] = this.email; json[r'name'] = this.name; if (this.notify != null) { @@ -110,6 +120,7 @@ class UserAdminCreateDto { final json = value.cast(); return UserAdminCreateDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email')!, name: mapValueOfType(json, r'name')!, notify: mapValueOfType(json, r'notify'), diff --git a/mobile/openapi/lib/model/user_admin_update_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart index f0478c9b4c..ee5c006840 100644 --- a/mobile/openapi/lib/model/user_admin_update_dto.dart +++ b/mobile/openapi/lib/model/user_admin_update_dto.dart @@ -13,14 +13,18 @@ part of openapi.api; class UserAdminUpdateDto { /// Returns a new [UserAdminUpdateDto] instance. UserAdminUpdateDto({ + this.avatarColor, this.email, this.name, this.password, + this.pinCode, this.quotaSizeInBytes, this.shouldChangePassword, this.storageLabel, }); + UserAvatarColor? avatarColor; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -45,6 +49,8 @@ class UserAdminUpdateDto { /// String? password; + String? pinCode; + /// Minimum value: 0 int? quotaSizeInBytes; @@ -60,9 +66,11 @@ class UserAdminUpdateDto { @override bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.password == password && + other.pinCode == pinCode && other.quotaSizeInBytes == quotaSizeInBytes && other.shouldChangePassword == shouldChangePassword && other.storageLabel == storageLabel; @@ -70,18 +78,25 @@ class UserAdminUpdateDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + (name == null ? 0 : name!.hashCode) + (password == null ? 0 : password!.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + (shouldChangePassword == null ? 0 : shouldChangePassword!.hashCode) + (storageLabel == null ? 0 : storageLabel!.hashCode); @override - String toString() => 'UserAdminUpdateDto[email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; + String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password, pinCode=$pinCode, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.email != null) { json[r'email'] = this.email; } else { @@ -97,6 +112,11 @@ class UserAdminUpdateDto { } else { // json[r'password'] = null; } + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } if (this.quotaSizeInBytes != null) { json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; } else { @@ -124,9 +144,11 @@ class UserAdminUpdateDto { final json = value.cast(); return UserAdminUpdateDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email'), name: mapValueOfType(json, r'name'), password: mapValueOfType(json, r'password'), + pinCode: mapValueOfType(json, r'pinCode'), quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), shouldChangePassword: mapValueOfType(json, r'shouldChangePassword'), storageLabel: mapValueOfType(json, r'storageLabel'), diff --git a/mobile/openapi/lib/model/user_preferences_response_dto.dart b/mobile/openapi/lib/model/user_preferences_response_dto.dart index b244284eb0..215e691cb1 100644 --- a/mobile/openapi/lib/model/user_preferences_response_dto.dart +++ b/mobile/openapi/lib/model/user_preferences_response_dto.dart @@ -13,7 +13,6 @@ part of openapi.api; class UserPreferencesResponseDto { /// Returns a new [UserPreferencesResponseDto] instance. UserPreferencesResponseDto({ - required this.avatar, required this.download, required this.emailNotifications, required this.folders, @@ -25,8 +24,6 @@ class UserPreferencesResponseDto { required this.tags, }); - AvatarResponse avatar; - DownloadResponse download; EmailNotificationsResponse emailNotifications; @@ -47,7 +44,6 @@ class UserPreferencesResponseDto { @override bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto && - other.avatar == avatar && other.download == download && other.emailNotifications == emailNotifications && other.folders == folders && @@ -61,7 +57,6 @@ class UserPreferencesResponseDto { @override int get hashCode => // ignore: unnecessary_parenthesis - (avatar.hashCode) + (download.hashCode) + (emailNotifications.hashCode) + (folders.hashCode) + @@ -73,11 +68,10 @@ class UserPreferencesResponseDto { (tags.hashCode); @override - String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]'; + String toString() => 'UserPreferencesResponseDto[download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, sharedLinks=$sharedLinks, tags=$tags]'; Map toJson() { final json = {}; - json[r'avatar'] = this.avatar; json[r'download'] = this.download; json[r'emailNotifications'] = this.emailNotifications; json[r'folders'] = this.folders; @@ -99,7 +93,6 @@ class UserPreferencesResponseDto { final json = value.cast(); return UserPreferencesResponseDto( - avatar: AvatarResponse.fromJson(json[r'avatar'])!, download: DownloadResponse.fromJson(json[r'download'])!, emailNotifications: EmailNotificationsResponse.fromJson(json[r'emailNotifications'])!, folders: FoldersResponse.fromJson(json[r'folders'])!, @@ -156,7 +149,6 @@ class UserPreferencesResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'avatar', 'download', 'emailNotifications', 'folders', diff --git a/mobile/openapi/lib/model/user_update_me_dto.dart b/mobile/openapi/lib/model/user_update_me_dto.dart index 8f3f4df37a..779e07ffa6 100644 --- a/mobile/openapi/lib/model/user_update_me_dto.dart +++ b/mobile/openapi/lib/model/user_update_me_dto.dart @@ -13,11 +13,14 @@ part of openapi.api; class UserUpdateMeDto { /// Returns a new [UserUpdateMeDto] instance. UserUpdateMeDto({ + this.avatarColor, this.email, this.name, this.password, }); + UserAvatarColor? avatarColor; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -44,6 +47,7 @@ class UserUpdateMeDto { @override bool operator ==(Object other) => identical(this, other) || other is UserUpdateMeDto && + other.avatarColor == avatarColor && other.email == email && other.name == name && other.password == password; @@ -51,15 +55,21 @@ class UserUpdateMeDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (email == null ? 0 : email!.hashCode) + (name == null ? 0 : name!.hashCode) + (password == null ? 0 : password!.hashCode); @override - String toString() => 'UserUpdateMeDto[email=$email, name=$name, password=$password]'; + String toString() => 'UserUpdateMeDto[avatarColor=$avatarColor, email=$email, name=$name, password=$password]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.email != null) { json[r'email'] = this.email; } else { @@ -87,6 +97,7 @@ class UserUpdateMeDto { final json = value.cast(); return UserUpdateMeDto( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), email: mapValueOfType(json, r'email'), name: mapValueOfType(json, r'name'), password: mapValueOfType(json, r'password'), diff --git a/mobile/openapi/lib/model/version_check_state_response_dto.dart b/mobile/openapi/lib/model/version_check_state_response_dto.dart new file mode 100644 index 0000000000..d3f9a6cd95 --- /dev/null +++ b/mobile/openapi/lib/model/version_check_state_response_dto.dart @@ -0,0 +1,115 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class VersionCheckStateResponseDto { + /// Returns a new [VersionCheckStateResponseDto] instance. + VersionCheckStateResponseDto({ + required this.checkedAt, + required this.releaseVersion, + }); + + String? checkedAt; + + String? releaseVersion; + + @override + bool operator ==(Object other) => identical(this, other) || other is VersionCheckStateResponseDto && + other.checkedAt == checkedAt && + other.releaseVersion == releaseVersion; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (checkedAt == null ? 0 : checkedAt!.hashCode) + + (releaseVersion == null ? 0 : releaseVersion!.hashCode); + + @override + String toString() => 'VersionCheckStateResponseDto[checkedAt=$checkedAt, releaseVersion=$releaseVersion]'; + + Map toJson() { + final json = {}; + if (this.checkedAt != null) { + json[r'checkedAt'] = this.checkedAt; + } else { + // json[r'checkedAt'] = null; + } + if (this.releaseVersion != null) { + json[r'releaseVersion'] = this.releaseVersion; + } else { + // json[r'releaseVersion'] = null; + } + return json; + } + + /// Returns a new [VersionCheckStateResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static VersionCheckStateResponseDto? fromJson(dynamic value) { + upgradeDto(value, "VersionCheckStateResponseDto"); + if (value is Map) { + final json = value.cast(); + + return VersionCheckStateResponseDto( + checkedAt: mapValueOfType(json, r'checkedAt'), + releaseVersion: mapValueOfType(json, r'releaseVersion'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = VersionCheckStateResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = VersionCheckStateResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of VersionCheckStateResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = VersionCheckStateResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'checkedAt', + 'releaseVersion', + }; +} + diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 3731832296..3df4e4e8a9 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -303,7 +303,7 @@ packages: source: hosted version: "0.3.4+2" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" @@ -621,6 +621,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_svg: dependency: "direct main" description: @@ -696,18 +744,18 @@ packages: dependency: "direct main" description: name: geolocator - sha256: "6cb9fb6e5928b58b9a84bdf85012d757fd07aab8215c5205337021c4999bad27" + sha256: e7ebfa04ce451daf39b5499108c973189a71a919aa53c1204effda1c5b93b822 url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "14.0.0" geolocator_android: dependency: transitive description: name: geolocator_android - sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47" + sha256: "114072db5d1dce0ec0b36af2697f55c133bc89a2c8dd513e137c0afe59696ed4" url: "https://pub.dev" source: hosted - version: "4.6.1" + version: "5.0.1+1" geolocator_apple: dependency: transitive description: @@ -728,10 +776,10 @@ packages: dependency: transitive description: name: geolocator_web - sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" + sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.1.3" geolocator_windows: dependency: transitive description: @@ -976,6 +1024,46 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.1" + local_auth: + dependency: "direct main" + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "63ad7ca6396290626dc0cb34725a939e4cfe965d80d36112f08d49cf13a8136e" + url: "https://pub.dev" + source: hosted + version: "1.0.49" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" + url: "https://pub.dev" + source: hosted + version: "1.4.3" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" logging: dependency: "direct main" description: @@ -1264,6 +1352,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + url: "https://pub.dev" + source: hosted + version: "5.0.1" platform: dependency: transitive description: @@ -1741,6 +1837,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.2" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" url_launcher: dependency: "direct main" description: @@ -1806,7 +1910,7 @@ packages: source: hosted version: "3.1.4" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff @@ -1933,6 +2037,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.3" + worker_manager: + dependency: "direct main" + description: + name: worker_manager + sha256: "086ed63e9b36266e851404ca90fd44e37c0f4c9bbf819e5f8d7c87f9741c0591" + url: "https://pub.dev" + source: hosted + version: "7.2.3" xdg_directories: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 03c39810f6..6dd81b7fc1 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.131.3+193 +version: 1.134.0+200 environment: sdk: '>=3.3.0 <4.0.0' @@ -22,6 +22,7 @@ dependencies: collection: ^1.18.0 connectivity_plus: ^6.1.3 crop_image: ^1.0.16 + crypto: ^3.0.6 device_info_plus: ^11.3.3 dynamic_color: ^1.7.0 easy_image_viewer: ^1.5.1 @@ -35,7 +36,7 @@ dependencies: flutter_udid: ^3.0.0 flutter_web_auth_2: ^5.0.0-alpha.0 fluttertoast: ^8.2.12 - geolocator: ^11.0.0 + geolocator: ^14.0.0 hooks_riverpod: ^2.6.1 http: ^1.3.0 image_picker: ^1.1.2 @@ -60,7 +61,12 @@ dependencies: thumbhash: 0.1.0+1 timezone: ^0.9.4 url_launcher: ^6.3.1 + uuid: ^4.5.1 wakelock_plus: ^1.2.10 + worker_manager: ^7.2.3 + local_auth: ^2.3.0 + pinput: ^5.0.1 + flutter_secure_storage: ^9.2.4 native_video_player: git: diff --git a/mobile/test/api.mocks.dart b/mobile/test/api.mocks.dart index d502ea0675..b0a4e9b8fd 100644 --- a/mobile/test/api.mocks.dart +++ b/mobile/test/api.mocks.dart @@ -2,3 +2,5 @@ import 'package:mocktail/mocktail.dart'; import 'package:openapi/api.dart'; class MockAssetsApi extends Mock implements AssetsApi {} + +class MockSyncApi extends Mock implements SyncApi {} diff --git a/mobile/test/domain/service.mock.dart b/mobile/test/domain/service.mock.dart index 53a173fc28..97a3f30294 100644 --- a/mobile/test/domain/service.mock.dart +++ b/mobile/test/domain/service.mock.dart @@ -1,7 +1,10 @@ import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; +import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:mocktail/mocktail.dart'; class MockStoreService extends Mock implements StoreService {} class MockUserService extends Mock implements UserService {} + +class MockBackgroundSyncManager extends Mock implements BackgroundSyncManager {} diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart new file mode 100644 index 0000000000..b78a44342b --- /dev/null +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -0,0 +1,222 @@ +// ignore_for_file: avoid-declaring-call-method, avoid-unnecessary-futures + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; +import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; +import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:immich_mobile/domain/services/sync_stream.service.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../fixtures/sync_stream.stub.dart'; +import '../../infrastructure/repository.mock.dart'; + +class _AbortCallbackWrapper { + const _AbortCallbackWrapper(); + + bool call() => false; +} + +class _MockAbortCallbackWrapper extends Mock implements _AbortCallbackWrapper {} + +class _CancellationWrapper { + const _CancellationWrapper(); + + bool call() => false; +} + +class _MockCancellationWrapper extends Mock implements _CancellationWrapper {} + +void main() { + late SyncStreamService sut; + late ISyncStreamRepository mockSyncStreamRepo; + late ISyncApiRepository mockSyncApiRepo; + late Function(List, Function()) handleEventsCallback; + late _MockAbortCallbackWrapper mockAbortCallbackWrapper; + + successHandler(Invocation _) async => true; + + setUp(() { + mockSyncStreamRepo = MockSyncStreamRepository(); + mockSyncApiRepo = MockSyncApiRepository(); + mockAbortCallbackWrapper = _MockAbortCallbackWrapper(); + + when(() => mockAbortCallbackWrapper()).thenReturn(false); + + when(() => mockSyncApiRepo.streamChanges(any())) + .thenAnswer((invocation) async { + // ignore: avoid-unsafe-collection-methods + handleEventsCallback = invocation.positionalArguments.first; + }); + + when(() => mockSyncApiRepo.ack(any())).thenAnswer((_) async => {}); + + when(() => mockSyncStreamRepo.updateUsersV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteUsersV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePartnerV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePartnerV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetsV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsExifV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePartnerAssetsV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePartnerAssetsV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePartnerAssetsExifV1(any())) + .thenAnswer(successHandler); + + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + ); + }); + + Future simulateEvents(List events) async { + await sut.sync(); + await handleEventsCallback(events, mockAbortCallbackWrapper.call); + } + + group("SyncStreamService - _handleEvents", () { + test( + "processes events and acks successfully when handlers succeed", + () async { + final events = [ + SyncStreamStub.userDeleteV1, + SyncStreamStub.userV1Admin, + SyncStreamStub.userV1User, + SyncStreamStub.partnerDeleteV1, + SyncStreamStub.partnerV1, + ]; + + await simulateEvents(events); + + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deletePartnerV1(any()), + () => mockSyncApiRepo.ack(["4"]), + () => mockSyncStreamRepo.updatePartnerV1(any()), + () => mockSyncApiRepo.ack(["3"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }, + ); + + test("processes final batch correctly", () async { + final events = [ + SyncStreamStub.userDeleteV1, + SyncStreamStub.userV1Admin, + ]; + + await simulateEvents(events); + + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["1"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("does not process or ack when event list is empty", () async { + await simulateEvents([]); + + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.deleteUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.updatePartnerV1(any())); + verifyNever(() => mockSyncStreamRepo.deletePartnerV1(any())); + verifyNever(() => mockAbortCallbackWrapper()); + verifyNever(() => mockSyncApiRepo.ack(any())); + }); + + test("aborts and stops processing if cancelled during iteration", () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); + + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); + await sut.sync(); + + final events = [ + SyncStreamStub.userDeleteV1, + SyncStreamStub.userV1Admin, + SyncStreamStub.partnerDeleteV1, + ]; + + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { + when(() => cancellationChecker()).thenReturn(true); + }); + + await handleEventsCallback(events, mockAbortCallbackWrapper.call); + + verify(() => mockSyncStreamRepo.deleteUsersV1(any())).called(1); + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.deletePartnerV1(any())); + + verify(() => mockAbortCallbackWrapper()).called(1); + + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }); + + test( + "aborts and stops processing if cancelled before processing batch", + () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); + + final processingCompleter = Completer(); + bool handler1Started = false; + when(() => mockSyncStreamRepo.deleteUsersV1(any())) + .thenAnswer((_) async { + handler1Started = true; + return processingCompleter.future; + }); + + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); + + await sut.sync(); + + final events = [ + SyncStreamStub.userDeleteV1, + SyncStreamStub.userV1Admin, + SyncStreamStub.partnerDeleteV1, + ]; + + final processingFuture = + handleEventsCallback(events, mockAbortCallbackWrapper.call); + await pumpEventQueue(); + + expect(handler1Started, isTrue); + + // Signal cancellation while handler 1 is waiting + when(() => cancellationChecker()).thenReturn(true); + await pumpEventQueue(); + + processingCompleter.complete(); + await processingFuture; + + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }, + ); + }); +} diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart new file mode 100644 index 0000000000..ba97f1434a --- /dev/null +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -0,0 +1,45 @@ +import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:openapi/api.dart'; + +abstract final class SyncStreamStub { + static final userV1Admin = SyncEvent( + type: SyncEntityType.userV1, + data: SyncUserV1( + deletedAt: DateTime(2020), + email: "admin@admin", + id: "1", + name: "Admin", + ), + ack: "1", + ); + static final userV1User = SyncEvent( + type: SyncEntityType.userV1, + data: SyncUserV1( + deletedAt: DateTime(2021), + email: "user@user", + id: "5", + name: "User", + ), + ack: "5", + ); + static final userDeleteV1 = SyncEvent( + type: SyncEntityType.userDeleteV1, + data: SyncUserDeleteV1(userId: "2"), + ack: "2", + ); + + static final partnerV1 = SyncEvent( + type: SyncEntityType.partnerV1, + data: SyncPartnerV1( + inTimeline: true, + sharedById: "1", + sharedWithId: "2", + ), + ack: "3", + ); + static final partnerDeleteV1 = SyncEvent( + type: SyncEntityType.partnerDeleteV1, + data: SyncPartnerDeleteV1(sharedById: "3", sharedWithId: "4"), + ack: "4", + ); +} diff --git a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart new file mode 100644 index 0000000000..55b03a8116 --- /dev/null +++ b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart @@ -0,0 +1,299 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:openapi/api.dart'; + +import '../../api.mocks.dart'; +import '../../service.mocks.dart'; + +class MockHttpClient extends Mock implements http.Client {} + +class MockApiClient extends Mock implements ApiClient {} + +class MockStreamedResponse extends Mock implements http.StreamedResponse {} + +class FakeBaseRequest extends Fake implements http.BaseRequest {} + +String _createJsonLine(String type, Map data, String ack) { + return '${jsonEncode({'type': type, 'data': data, 'ack': ack})}\n'; +} + +void main() { + late SyncApiRepository sut; + late MockApiService mockApiService; + late MockApiClient mockApiClient; + late MockSyncApi mockSyncApi; + late MockHttpClient mockHttpClient; + late MockStreamedResponse mockStreamedResponse; + late StreamController> responseStreamController; + late int testBatchSize = 3; + + setUp(() { + mockApiService = MockApiService(); + mockApiClient = MockApiClient(); + mockSyncApi = MockSyncApi(); + mockHttpClient = MockHttpClient(); + mockStreamedResponse = MockStreamedResponse(); + responseStreamController = + StreamController>.broadcast(sync: true); + + registerFallbackValue(FakeBaseRequest()); + + when(() => mockApiService.apiClient).thenReturn(mockApiClient); + when(() => mockApiService.syncApi).thenReturn(mockSyncApi); + when(() => mockApiClient.basePath).thenReturn('http://demo.immich.app/api'); + when(() => mockApiService.applyToParams(any(), any())) + .thenAnswer((_) async => {}); + + // Mock HTTP client behavior + when(() => mockHttpClient.send(any())) + .thenAnswer((_) async => mockStreamedResponse); + when(() => mockStreamedResponse.statusCode).thenReturn(200); + when(() => mockStreamedResponse.stream) + .thenAnswer((_) => http.ByteStream(responseStreamController.stream)); + when(() => mockHttpClient.close()).thenAnswer((_) => {}); + + sut = SyncApiRepository(mockApiService); + }); + + tearDown(() async { + if (!responseStreamController.isClosed) { + await responseStreamController.close(); + } + }); + + Future streamChanges( + Function(List, Function() abort) onDataCallback, + ) { + return sut.streamChanges( + onDataCallback, + batchSize: testBatchSize, + httpClient: mockHttpClient, + ); + } + + test('streamChanges stops processing stream when abort is called', () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; + List receivedEventsBatch1 = []; + + onDataCallback(List events, Function() abort) { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); + } + } + + final streamChangesFuture = streamChanges(onDataCallback); + + await pumpEventQueue(); + + for (int i = 0; i < testBatchSize; i++) { + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user$i").toJson(), + 'ack$i', + ), + ), + ); + } + + for (int i = testBatchSize; i < testBatchSize * 2; i++) { + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user$i").toJson(), + 'ack$i', + ), + ), + ); + } + + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + expect(receivedEventsBatch1.length, testBatchSize); + verify(() => mockHttpClient.close()).called(1); + }); + + test( + 'streamChanges does not process remaining lines in finally block if aborted', + () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; + + onDataCallback(List events, Function() abort) { + onDataCallCount++; + if (onDataCallCount == 1) { + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); + } + } + + final streamChangesFuture = streamChanges(onDataCallback); + + await pumpEventQueue(); + + for (int i = 0; i < testBatchSize; i++) { + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user$i").toJson(), + 'ack$i', + ), + ), + ); + } + + // emit a single event to skip batching and trigger finally + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user100").toJson(), + 'ack100', + ), + ), + ); + + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + verify(() => mockHttpClient.close()).called(1); + }, + ); + + test( + 'streamChanges processes remaining lines in finally block if not aborted', + () async { + int onDataCallCount = 0; + List receivedEventsBatch1 = []; + List receivedEventsBatch2 = []; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + } else if (onDataCallCount == 2) { + receivedEventsBatch2 = events; + } else { + fail("onData called more than expected"); + } + } + + final streamChangesFuture = streamChanges(onDataCallback); + + await pumpEventQueue(); + + // Batch 1 + for (int i = 0; i < testBatchSize; i++) { + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user$i").toJson(), + 'ack$i', + ), + ), + ); + } + + // Partial Batch 2 + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user100").toJson(), + 'ack100', + ), + ), + ); + + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 2); + expect(receivedEventsBatch1.length, testBatchSize); + expect(receivedEventsBatch2.length, 1); + verify(() => mockHttpClient.close()).called(1); + }, + ); + + test('streamChanges handles stream error gracefully', () async { + final streamError = Exception("Network Error"); + int onDataCallCount = 0; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + } + + final streamChangesFuture = streamChanges(onDataCallback); + + await pumpEventQueue(); + + responseStreamController.add( + utf8.encode( + _createJsonLine( + SyncEntityType.userDeleteV1.toString(), + SyncUserDeleteV1(userId: "user1").toJson(), + 'ack1', + ), + ), + ); + + responseStreamController.addError(streamError); + await expectLater(streamChangesFuture, throwsA(streamError)); + + expect(onDataCallCount, 0); + verify(() => mockHttpClient.close()).called(1); + }); + + test('streamChanges throws ApiException on non-200 status code', () async { + when(() => mockStreamedResponse.statusCode).thenReturn(401); + final errorBodyController = StreamController>(sync: true); + when(() => mockStreamedResponse.stream) + .thenAnswer((_) => http.ByteStream(errorBodyController.stream)); + + int onDataCallCount = 0; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + } + + final future = streamChanges(onDataCallback); + + errorBodyController.add(utf8.encode('{"error":"Unauthorized"}')); + await errorBodyController.close(); + + await expectLater( + future, + throwsA( + isA() + .having((e) => e.code, 'code', 401) + .having((e) => e.message, 'message', contains('Unauthorized')), + ), + ); + + expect(onDataCallCount, 0); + verify(() => mockHttpClient.close()).called(1); + }); +} diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index 192858adff..c4a5680f71 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -1,6 +1,8 @@ import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart'; import 'package:immich_mobile/domain/interfaces/log.interface.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart'; +import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; +import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/interfaces/user_api.interface.dart'; import 'package:mocktail/mocktail.dart'; @@ -14,5 +16,9 @@ class MockUserRepository extends Mock implements IUserRepository {} class MockDeviceAssetRepository extends Mock implements IDeviceAssetRepository {} +class MockSyncStreamRepository extends Mock implements ISyncStreamRepository {} + // API Repos class MockUserApiRepository extends Mock implements IUserApiRepository {} + +class MockSyncApiRepository extends Mock implements ISyncApiRepository {} diff --git a/mobile/test/service.mocks.dart b/mobile/test/service.mocks.dart index e1b8df40a3..87a8c01cf0 100644 --- a/mobile/test/service.mocks.dart +++ b/mobile/test/service.mocks.dart @@ -29,4 +29,3 @@ class MockSearchApi extends Mock implements SearchApi {} class MockAppSettingService extends Mock implements AppSettingsService {} class MockBackgroundService extends Mock implements BackgroundService {} - diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index e4f011d940..4ada98a6c9 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -8,6 +8,7 @@ import 'package:isar/isar.dart'; import 'package:mocktail/mocktail.dart'; import 'package:openapi/api.dart'; +import '../domain/service.mock.dart'; import '../repository.mocks.dart'; import '../service.mocks.dart'; import '../test_utils.dart'; @@ -18,6 +19,7 @@ void main() { late MockAuthRepository authRepository; late MockApiService apiService; late MockNetworkService networkService; + late MockBackgroundSyncManager backgroundSyncManager; late Isar db; setUp(() async { @@ -25,12 +27,14 @@ void main() { authRepository = MockAuthRepository(); apiService = MockApiService(); networkService = MockNetworkService(); + backgroundSyncManager = MockBackgroundSyncManager(); sut = AuthService( authApiRepository, authRepository, apiService, networkService, + backgroundSyncManager, ); registerFallbackValue(Uri()); @@ -116,24 +120,28 @@ void main() { group('logout', () { test('Should logout user', () async { when(() => authApiRepository.logout()).thenAnswer((_) async => {}); + when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); when(() => authRepository.clearLocalData()) .thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); + verify(() => backgroundSyncManager.cancel()).called(1); verify(() => authRepository.clearLocalData()).called(1); }); test('Should clear local data even on server error', () async { when(() => authApiRepository.logout()) .thenThrow(Exception('Server error')); + when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); when(() => authRepository.clearLocalData()) .thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); + verify(() => backgroundSyncManager.cancel()).called(1); verify(() => authRepository.clearLocalData()).called(1); }); }); diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index e2badc6dff..d6f1333489 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -OPENAPI_GENERATOR_VERSION=v7.8.0 +OPENAPI_GENERATOR_VERSION=v7.12.0 # usage: ./bin/generate-open-api.sh @@ -8,6 +8,7 @@ function dart { cd ./templates/mobile/serialization/native wget -O native_class.mustache https://raw.githubusercontent.com/OpenAPITools/openapi-generator/$OPENAPI_GENERATOR_VERSION/modules/openapi-generator/src/main/resources/dart2/serialization/native/native_class.mustache patch --no-backup-if-mismatch -u native_class.mustache {{/isArray}}{{^isArray}}{{{datatypeWithEnum}}}{{/isArray}}{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isNullable}} {{{name}}}; {{/vars}} @override diff --git a/open-api/templates/mobile/serialization/native/native_class_nullable_items_in_arrays.patch b/open-api/templates/mobile/serialization/native/native_class_nullable_items_in_arrays.patch new file mode 100644 index 0000000000..a59e300913 --- /dev/null +++ b/open-api/templates/mobile/serialization/native/native_class_nullable_items_in_arrays.patch @@ -0,0 +1,13 @@ +diff --git a/open-api/templates/mobile/serialization/native/native_class.mustache b/open-api/templates/mobile/serialization/native/native_class.mustache +index 9a7b1439b..9f40d5b0b 100644 +--- a/open-api/templates/mobile/serialization/native/native_class.mustache ++++ b/open-api/templates/mobile/serialization/native/native_class.mustache +@@ -32,7 +32,7 @@ class {{{classname}}} { + {{/required}} + {{/isNullable}} + {{/isEnum}} +- {{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isNullable}} {{{name}}}; ++ {{#isArray}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}<{{{items.dataType}}}{{#items.isNullable}}?{{/items.isNullable}}>{{/isArray}}{{^isArray}}{{{datatypeWithEnum}}}{{/isArray}}{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isNullable}} {{{name}}}; + + {{/vars}} + @override diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 761a228de0..e524b5e27e 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,18 +1,18 @@ { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 29fe50dcd9..cf73d261ff 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "typescript": "^5.3.3" }, "repository": { @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" } } diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index f82f5bc9a7..0ce6f417b1 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.131.3 + * 1.134.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -39,6 +39,48 @@ export type ActivityCreateDto = { export type ActivityStatisticsResponseDto = { comments: number; }; +export type NotificationCreateDto = { + data?: object; + description?: string | null; + level?: NotificationLevel; + readAt?: string | null; + title: string; + "type"?: NotificationType; + userId: string; +}; +export type NotificationDto = { + createdAt: string; + data?: object; + description?: string; + id: string; + level: NotificationLevel; + readAt?: string; + title: string; + "type": NotificationType; +}; +export type TemplateDto = { + template: string; +}; +export type TemplateResponseDto = { + html: string; + name: string; +}; +export type SystemConfigSmtpTransportDto = { + host: string; + ignoreCert: boolean; + password: string; + port: number; + username: string; +}; +export type SystemConfigSmtpDto = { + enabled: boolean; + "from": string; + replyTo: string; + transport: SystemConfigSmtpTransportDto; +}; +export type TestEmailResponseDto = { + messageId: string; +}; export type UserLicense = { activatedAt: string; activationKey: string; @@ -64,6 +106,7 @@ export type UserAdminResponseDto = { updatedAt: string; }; export type UserAdminCreateDto = { + avatarColor?: (UserAvatarColor) | null; email: string; name: string; notify?: boolean; @@ -76,16 +119,15 @@ export type UserAdminDeleteDto = { force?: boolean; }; export type UserAdminUpdateDto = { + avatarColor?: (UserAvatarColor) | null; email?: string; name?: string; password?: string; + pinCode?: string | null; quotaSizeInBytes?: number | null; shouldChangePassword?: boolean; storageLabel?: string | null; }; -export type AvatarResponse = { - color: UserAvatarColor; -}; export type DownloadResponse = { archiveSize: number; includeEmbeddedVideos: boolean; @@ -122,7 +164,6 @@ export type TagsResponse = { sidebarWeb: boolean; }; export type UserPreferencesResponseDto = { - avatar: AvatarResponse; download: DownloadResponse; emailNotifications: EmailNotificationsResponse; folders: FoldersResponse; @@ -183,6 +224,11 @@ export type UserPreferencesUpdateDto = { sharedLinks?: SharedLinksUpdate; tags?: TagsUpdate; }; +export type AssetStatsResponseDto = { + images: number; + total: number; + videos: number; +}; export type AlbumUserResponseDto = { role: AlbumUserRole; user: UserResponseDto; @@ -283,6 +329,7 @@ export type AssetResponseDto = { "type": AssetTypeEnum; unassignedFaces?: AssetFaceWithoutPersonResponseDto[]; updatedAt: string; + visibility: AssetVisibility; }; export type AlbumResponseDto = { albumName: string; @@ -361,6 +408,7 @@ export type ApiKeyCreateResponseDto = { }; export type ApiKeyUpdateDto = { name: string; + permissions: Permission[]; }; export type AssetBulkDeleteDto = { force?: boolean; @@ -373,11 +421,10 @@ export type AssetMediaCreateDto = { duration?: string; fileCreatedAt: string; fileModifiedAt: string; - isArchived?: boolean; isFavorite?: boolean; - isVisible?: boolean; livePhotoVideoId?: string; sidecarData?: Blob; + visibility?: AssetVisibility; }; export type AssetMediaResponseDto = { id: string; @@ -385,13 +432,14 @@ export type AssetMediaResponseDto = { }; export type AssetBulkUpdateDto = { dateTimeOriginal?: string; + description?: string; duplicateId?: string | null; ids: string[]; - isArchived?: boolean; isFavorite?: boolean; latitude?: number; longitude?: number; rating?: number; + visibility?: AssetVisibility; }; export type AssetBulkUploadCheckItem = { /** base64 or hex encoded sha1 hash */ @@ -422,24 +470,15 @@ export type AssetJobsDto = { assetIds: string[]; name: AssetJobName; }; -export type MemoryLaneResponseDto = { - assets: AssetResponseDto[]; - yearsAgo: number; -}; -export type AssetStatsResponseDto = { - images: number; - total: number; - videos: number; -}; export type UpdateAssetDto = { dateTimeOriginal?: string; description?: string; - isArchived?: boolean; isFavorite?: boolean; latitude?: number; livePhotoVideoId?: string | null; longitude?: number; rating?: number; + visibility?: AssetVisibility; }; export type AssetMediaReplaceDto = { assetData: Blob; @@ -475,6 +514,29 @@ export type LogoutResponseDto = { redirectUri: string; successful: boolean; }; +export type PinCodeResetDto = { + password?: string; + pinCode?: string; +}; +export type PinCodeSetupDto = { + pinCode: string; +}; +export type PinCodeChangeDto = { + newPinCode: string; + password?: string; + pinCode?: string; +}; +export type SessionUnlockDto = { + password?: string; + pinCode?: string; +}; +export type AuthStatusResponseDto = { + expiresAt?: string; + isElevated: boolean; + password: boolean; + pinCode: boolean; + pinExpiresAt?: string; +}; export type ValidateAccessTokenResponseDto = { authStatus: boolean; }; @@ -663,36 +725,27 @@ export type MemoryUpdateDto = { memoryAt?: string; seenAt?: string; }; -export type TemplateDto = { - template: string; +export type NotificationDeleteAllDto = { + ids: string[]; }; -export type TemplateResponseDto = { - html: string; - name: string; +export type NotificationUpdateAllDto = { + ids: string[]; + readAt?: string | null; }; -export type SystemConfigSmtpTransportDto = { - host: string; - ignoreCert: boolean; - password: string; - port: number; - username: string; -}; -export type SystemConfigSmtpDto = { - enabled: boolean; - "from": string; - replyTo: string; - transport: SystemConfigSmtpTransportDto; -}; -export type TestEmailResponseDto = { - messageId: string; +export type NotificationUpdateDto = { + readAt?: string | null; }; export type OAuthConfigDto = { + codeChallenge?: string; redirectUri: string; + state?: string; }; export type OAuthAuthorizeResponseDto = { url: string; }; export type OAuthCallbackDto = { + codeVerifier?: string; + state?: string; url: string; }; export type PartnerResponseDto = { @@ -769,27 +822,6 @@ export type AssetFaceUpdateDto = { export type PersonStatisticsResponseDto = { assets: number; }; -export type FileReportItemDto = { - checksum?: string; - entityId: string; - entityType: PathEntityType; - pathType: PathType; - pathValue: string; -}; -export type FileReportDto = { - extras: string[]; - orphans: FileReportItemDto[]; -}; -export type FileChecksumDto = { - filenames: string[]; -}; -export type FileChecksumResponseDto = { - checksum: string; - filename: string; -}; -export type FileReportFixDto = { - items: FileReportItemDto[]; -}; export type SearchExploreItem = { data: AssetResponseDto; value: string; @@ -809,13 +841,11 @@ export type MetadataSearchDto = { deviceId?: string; encodedVideoPath?: string; id?: string; - isArchived?: boolean; isEncoded?: boolean; isFavorite?: boolean; isMotion?: boolean; isNotInAlbum?: boolean; isOffline?: boolean; - isVisible?: boolean; lensModel?: string | null; libraryId?: string | null; make?: string; @@ -838,7 +868,7 @@ export type MetadataSearchDto = { "type"?: AssetTypeEnum; updatedAfter?: string; updatedBefore?: string; - withArchived?: boolean; + visibility?: AssetVisibility; withDeleted?: boolean; withExif?: boolean; withPeople?: boolean; @@ -882,13 +912,11 @@ export type RandomSearchDto = { createdAfter?: string; createdBefore?: string; deviceId?: string; - isArchived?: boolean; isEncoded?: boolean; isFavorite?: boolean; isMotion?: boolean; isNotInAlbum?: boolean; isOffline?: boolean; - isVisible?: boolean; lensModel?: string | null; libraryId?: string | null; make?: string; @@ -905,7 +933,7 @@ export type RandomSearchDto = { "type"?: AssetTypeEnum; updatedAfter?: string; updatedBefore?: string; - withArchived?: boolean; + visibility?: AssetVisibility; withDeleted?: boolean; withExif?: boolean; withPeople?: boolean; @@ -917,13 +945,11 @@ export type SmartSearchDto = { createdAfter?: string; createdBefore?: string; deviceId?: string; - isArchived?: boolean; isEncoded?: boolean; isFavorite?: boolean; isMotion?: boolean; isNotInAlbum?: boolean; isOffline?: boolean; - isVisible?: boolean; language?: string; lensModel?: string | null; libraryId?: string | null; @@ -943,7 +969,7 @@ export type SmartSearchDto = { "type"?: AssetTypeEnum; updatedAfter?: string; updatedBefore?: string; - withArchived?: boolean; + visibility?: AssetVisibility; withDeleted?: boolean; withExif?: boolean; }; @@ -1051,6 +1077,10 @@ export type ServerVersionResponseDto = { minor: number; patch: number; }; +export type VersionCheckStateResponseDto = { + checkedAt: string | null; + releaseVersion: string | null; +}; export type ServerVersionHistoryResponseDto = { createdAt: string; id: string; @@ -1061,9 +1091,26 @@ export type SessionResponseDto = { current: boolean; deviceOS: string; deviceType: string; + expiresAt?: string; id: string; updatedAt: string; }; +export type SessionCreateDto = { + deviceOS?: string; + deviceType?: string; + /** session duration, in seconds */ + duration?: number; +}; +export type SessionCreateResponseDto = { + createdAt: string; + current: boolean; + deviceOS: string; + deviceType: string; + expiresAt?: string; + id: string; + token: string; + updatedAt: string; +}; export type SharedLinkResponseDto = { album?: AlbumResponseDto; allowDownload: boolean; @@ -1284,6 +1331,8 @@ export type SystemConfigOAuthDto = { signingAlgorithm: string; storageLabelClaim: string; storageQuotaClaim: string; + timeout: number; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; }; export type SystemConfigPasswordLoginDto = { enabled: boolean; @@ -1376,7 +1425,25 @@ export type TagBulkAssetsResponseDto = { export type TagUpdateDto = { color?: string | null; }; -export type TimeBucketResponseDto = { +export type TimeBucketAssetResponseDto = { + city: (string | null)[]; + country: (string | null)[]; + duration: (string | null)[]; + id: string[]; + isFavorite: boolean[]; + isImage: boolean[]; + isTrashed: boolean[]; + livePhotoVideoId: (string | null)[]; + localDateTime: string[]; + ownerId: string[]; + projectionType: (string | null)[]; + ratio: number[]; + /** (stack ID, stack asset count) tuple */ + stack?: (string[] | null)[]; + thumbhash: (string | null)[]; + visibility: AssetVisibility[]; +}; +export type TimeBucketsResponseDto = { count: number; timeBucket: string; }; @@ -1384,6 +1451,7 @@ export type TrashResponseDto = { count: number; }; export type UserUpdateMeDto = { + avatarColor?: (UserAvatarColor) | null; email?: string; name?: string; password?: string; @@ -1450,13 +1518,52 @@ export function deleteActivity({ id }: { method: "DELETE" })); } -export function searchUsersAdmin({ withDeleted }: { +export function createNotification({ notificationCreateDto }: { + notificationCreateDto: NotificationCreateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 201; + data: NotificationDto; + }>("/admin/notifications", oazapfts.json({ + ...opts, + method: "POST", + body: notificationCreateDto + }))); +} +export function getNotificationTemplateAdmin({ name, templateDto }: { + name: string; + templateDto: TemplateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TemplateResponseDto; + }>(`/admin/notifications/templates/${encodeURIComponent(name)}`, oazapfts.json({ + ...opts, + method: "POST", + body: templateDto + }))); +} +export function sendTestEmailAdmin({ systemConfigSmtpDto }: { + systemConfigSmtpDto: SystemConfigSmtpDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: TestEmailResponseDto; + }>("/admin/notifications/test-email", oazapfts.json({ + ...opts, + method: "POST", + body: systemConfigSmtpDto + }))); +} +export function searchUsersAdmin({ id, withDeleted }: { + id?: string; withDeleted?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: UserAdminResponseDto[]; }>(`/admin/users${QS.query(QS.explode({ + id, withDeleted }))}`, { ...opts @@ -1544,6 +1651,23 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility }: { + id: string; + isFavorite?: boolean; + isTrashed?: boolean; + visibility?: AssetVisibility; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetStatsResponseDto; + }>(`/admin/users/${encodeURIComponent(id)}/statistics${QS.query(QS.explode({ + isFavorite, + isTrashed, + visibility + }))}`, { + ...opts + })); +} export function getAllAlbums({ assetId, shared }: { assetId?: string; shared?: boolean; @@ -1816,20 +1940,6 @@ export function runAssetJobs({ assetJobsDto }: { body: assetJobsDto }))); } -export function getMemoryLane({ day, month }: { - day: number; - month: number; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: MemoryLaneResponseDto[]; - }>(`/assets/memory-lane${QS.query(QS.explode({ - day, - month - }))}`, { - ...opts - })); -} /** * This property was deprecated in v1.116.0 */ @@ -1845,18 +1955,18 @@ export function getRandom({ count }: { ...opts })); } -export function getAssetStatistics({ isArchived, isFavorite, isTrashed }: { - isArchived?: boolean; +export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { isFavorite?: boolean; isTrashed?: boolean; + visibility?: AssetVisibility; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetStatsResponseDto; }>(`/assets/statistics${QS.query(QS.explode({ - isArchived, isFavorite, - isTrashed + isTrashed, + visibility }))}`, { ...opts })); @@ -1992,6 +2102,56 @@ export function logout(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +export function resetPinCode({ pinCodeResetDto }: { + pinCodeResetDto: PinCodeResetDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/auth/pin-code", oazapfts.json({ + ...opts, + method: "DELETE", + body: pinCodeResetDto + }))); +} +export function setupPinCode({ pinCodeSetupDto }: { + pinCodeSetupDto: PinCodeSetupDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/auth/pin-code", oazapfts.json({ + ...opts, + method: "POST", + body: pinCodeSetupDto + }))); +} +export function changePinCode({ pinCodeChangeDto }: { + pinCodeChangeDto: PinCodeChangeDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/auth/pin-code", oazapfts.json({ + ...opts, + method: "PUT", + body: pinCodeChangeDto + }))); +} +export function lockAuthSession(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/auth/session/lock", { + ...opts, + method: "POST" + })); +} +export function unlockAuthSession({ sessionUnlockDto }: { + sessionUnlockDto: SessionUnlockDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/auth/session/unlock", oazapfts.json({ + ...opts, + method: "POST", + body: sessionUnlockDto + }))); +} +export function getAuthStatus(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AuthStatusResponseDto; + }>("/auth/status", { + ...opts + })); +} export function validateAccessToken(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2318,29 +2478,71 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getNotificationTemplate({ name, templateDto }: { - name: string; - templateDto: TemplateDto; +export function deleteNotifications({ notificationDeleteAllDto }: { + notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: TemplateResponseDto; - }>(`/notifications/templates/${encodeURIComponent(name)}`, oazapfts.json({ + return oazapfts.ok(oazapfts.fetchText("/notifications", oazapfts.json({ ...opts, - method: "POST", - body: templateDto + method: "DELETE", + body: notificationDeleteAllDto }))); } -export function sendTestEmail({ systemConfigSmtpDto }: { - systemConfigSmtpDto: SystemConfigSmtpDto; +export function getNotifications({ id, level, $type, unread }: { + id?: string; + level?: NotificationLevel; + $type?: NotificationType; + unread?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: TestEmailResponseDto; - }>("/notifications/test-email", oazapfts.json({ + data: NotificationDto[]; + }>(`/notifications${QS.query(QS.explode({ + id, + level, + "type": $type, + unread + }))}`, { + ...opts + })); +} +export function updateNotifications({ notificationUpdateAllDto }: { + notificationUpdateAllDto: NotificationUpdateAllDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/notifications", oazapfts.json({ ...opts, - method: "POST", - body: systemConfigSmtpDto + method: "PUT", + body: notificationUpdateAllDto + }))); +} +export function deleteNotification({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/notifications/${encodeURIComponent(id)}`, { + ...opts, + method: "DELETE" + })); +} +export function getNotification({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: NotificationDto; + }>(`/notifications/${encodeURIComponent(id)}`, { + ...opts + })); +} +export function updateNotification({ id, notificationUpdateDto }: { + id: string; + notificationUpdateDto: NotificationUpdateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: NotificationDto; + }>(`/notifications/${encodeURIComponent(id)}`, oazapfts.json({ + ...opts, + method: "PUT", + body: notificationUpdateDto }))); } export function startOAuth({ oAuthConfigDto }: { @@ -2550,35 +2752,6 @@ export function getPersonThumbnail({ id }: { ...opts })); } -export function getAuditFiles(opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 200; - data: FileReportDto; - }>("/reports", { - ...opts - })); -} -export function getFileChecksums({ fileChecksumDto }: { - fileChecksumDto: FileChecksumDto; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; - data: FileChecksumResponseDto[]; - }>("/reports/checksum", oazapfts.json({ - ...opts, - method: "POST", - body: fileChecksumDto - }))); -} -export function fixAuditFiles({ fileReportFixDto }: { - fileReportFixDto: FileReportFixDto; -}, opts?: Oazapfts.RequestOpts) { - return oazapfts.ok(oazapfts.fetchText("/reports/fix", oazapfts.json({ - ...opts, - method: "POST", - body: fileReportFixDto - }))); -} export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2779,6 +2952,14 @@ export function getServerVersion(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function getVersionCheck(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: VersionCheckStateResponseDto; + }>("/server/version-check", { + ...opts + })); +} export function getVersionHistory(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2801,6 +2982,18 @@ export function getSessions(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function createSession({ sessionCreateDto }: { + sessionCreateDto: SessionCreateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 201; + data: SessionCreateResponseDto; + }>("/sessions", oazapfts.json({ + ...opts, + method: "POST", + body: sessionCreateDto + }))); +} export function deleteSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2809,6 +3002,14 @@ export function deleteSession({ id }: { method: "DELETE" })); } +export function lockSession({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/sessions/${encodeURIComponent(id)}/lock`, { + ...opts, + method: "POST" + })); +} export function getAllSharedLinks({ albumId }: { albumId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3096,6 +3297,14 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: VersionCheckStateResponseDto; + }>("/system-metadata/version-check-state", { + ...opts + })); +} export function getAllTags(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3197,70 +3406,70 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, timeBucket, userId, withPartners, withStacked }: { +export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, page, pageSize, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; - isArchived?: boolean; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; + page?: number; + pageSize?: number; personId?: string; - size: TimeBucketSize; tagId?: string; timeBucket: string; userId?: string; + visibility?: AssetVisibility; withPartners?: boolean; withStacked?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: AssetResponseDto[]; + data: TimeBucketAssetResponseDto; }>(`/timeline/bucket${QS.query(QS.explode({ albumId, - isArchived, isFavorite, isTrashed, key, order, + page, + pageSize, personId, - size, tagId, timeBucket, userId, + visibility, withPartners, withStacked }))}`, { ...opts })); } -export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, userId, withPartners, withStacked }: { +export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; - isArchived?: boolean; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; - size: TimeBucketSize; tagId?: string; userId?: string; + visibility?: AssetVisibility; withPartners?: boolean; withStacked?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: TimeBucketResponseDto[]; + data: TimeBucketsResponseDto[]; }>(`/timeline/buckets${QS.query(QS.explode({ albumId, - isArchived, isFavorite, isTrashed, key, order, personId, - size, tagId, userId, + visibility, withPartners, withStacked }))}`, { @@ -3449,11 +3658,29 @@ export enum UserAvatarColor { Gray = "gray", Amber = "amber" } +export enum NotificationLevel { + Success = "success", + Error = "error", + Warning = "warning", + Info = "info" +} +export enum NotificationType { + JobFailed = "JobFailed", + BackupFailed = "BackupFailed", + SystemMessage = "SystemMessage", + Custom = "Custom" +} export enum UserStatus { Active = "active", Removing = "removing", Deleted = "deleted" } +export enum AssetVisibility { + Archive = "archive", + Timeline = "timeline", + Hidden = "hidden", + Locked = "locked" +} export enum AlbumUserRole { Editor = "editor", Viewer = "viewer" @@ -3523,6 +3750,10 @@ export enum Permission { MemoryRead = "memory.read", MemoryUpdate = "memory.update", MemoryDelete = "memory.delete", + NotificationCreate = "notification.create", + NotificationRead = "notification.read", + NotificationUpdate = "notification.update", + NotificationDelete = "notification.delete", PartnerCreate = "partner.create", PartnerRead = "partner.read", PartnerUpdate = "partner.update", @@ -3534,9 +3765,11 @@ export enum Permission { PersonStatistics = "person.statistics", PersonMerge = "person.merge", PersonReassign = "person.reassign", + SessionCreate = "session.create", SessionRead = "session.read", SessionUpdate = "session.update", SessionDelete = "session.delete", + SessionLock = "session.lock", SharedLinkCreate = "sharedLink.create", SharedLinkRead = "sharedLink.read", SharedLinkUpdate = "sharedLink.update", @@ -3622,21 +3855,6 @@ export enum PartnerDirection { SharedBy = "shared-by", SharedWith = "shared-with" } -export enum PathEntityType { - Asset = "asset", - Person = "person", - User = "user" -} -export enum PathType { - Original = "original", - Fullsize = "fullsize", - Preview = "preview", - Thumbnail = "thumbnail", - EncodedVideo = "encoded_video", - Sidecar = "sidecar", - Face = "face", - Profile = "profile" -} export enum SearchSuggestionType { Country = "country", State = "state", @@ -3663,7 +3881,11 @@ export enum SyncEntityType { AssetExifV1 = "AssetExifV1", PartnerAssetV1 = "PartnerAssetV1", PartnerAssetDeleteV1 = "PartnerAssetDeleteV1", - PartnerAssetExifV1 = "PartnerAssetExifV1" + PartnerAssetExifV1 = "PartnerAssetExifV1", + AlbumV1 = "AlbumV1", + AlbumDeleteV1 = "AlbumDeleteV1", + AlbumUserV1 = "AlbumUserV1", + AlbumUserDeleteV1 = "AlbumUserDeleteV1" } export enum SyncRequestType { UsersV1 = "UsersV1", @@ -3671,7 +3893,9 @@ export enum SyncRequestType { AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", PartnerAssetsV1 = "PartnerAssetsV1", - PartnerAssetExifsV1 = "PartnerAssetExifsV1" + PartnerAssetExifsV1 = "PartnerAssetExifsV1", + AlbumsV1 = "AlbumsV1", + AlbumUsersV1 = "AlbumUsersV1" } export enum TranscodeHWAccel { Nvenc = "nvenc", @@ -3732,7 +3956,7 @@ export enum LogLevel { Error = "error", Fatal = "fatal" } -export enum TimeBucketSize { - Day = "DAY", - Month = "MONTH" +export enum OAuthTokenEndpointAuthMethod { + ClientSecretPost = "client_secret_post", + ClientSecretBasic = "client_secret_basic" } diff --git a/server/.nvmrc b/server/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/server/Dockerfile b/server/Dockerfile index 84037031fd..fcf16ea139 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,19 +1,19 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202504081114@sha256:250ab051cb0bdefdaf7d4069f3de9eada4c0288360ba1143a0e607a202b305b1 AS dev +FROM ghcr.io/immich-app/base-server-dev:202505131114@sha256:cf4507bbbf307e9b6d8ee9418993321f2b85867da8ce14d0a20ccaf9574cb995 AS dev RUN apt-get install --no-install-recommends -yqq tini WORKDIR /usr/src/app COPY server/package.json server/package-lock.json ./ COPY server/patches ./patches RUN npm ci && \ - # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need - # they're marked as optional dependencies, so we need to copy them manually after pruning - rm -rf node_modules/@img/sharp-libvips* && \ - rm -rf node_modules/@img/sharp-linuxmusl-x64 + # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need + # they're marked as optional dependencies, so we need to copy them manually after pruning + rm -rf node_modules/@img/sharp-libvips* && \ + rm -rf node_modules/@img/sharp-linuxmusl-x64 ENV PATH="${PATH}:/usr/src/app/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all ENTRYPOINT ["tini", "--", "/bin/sh"] @@ -26,7 +26,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS web +FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ @@ -43,12 +43,12 @@ RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:202504081114@sha256:8353bcbdb4e6579300adfa0d8b5892abefa42ebfc99740050cbfb38ab83a0605 +FROM ghcr.io/immich-app/base-server-prod:202505061115@sha256:9971d3a089787f0bd01f4682141d3665bcf5efb3e101a88e394ffd25bee4eedb WORKDIR /usr/src/app ENV NODE_ENV=production \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all COPY --from=prod /usr/src/app/node_modules ./node_modules COPY --from=prod /usr/src/app/dist ./dist COPY --from=prod /usr/src/app/bin ./bin diff --git a/server/bin/immich-healthcheck b/server/bin/immich-healthcheck index 81528157e4..e6bdd28050 100755 --- a/server/bin/immich-healthcheck +++ b/server/bin/immich-healthcheck @@ -1,8 +1,14 @@ #!/usr/bin/env bash +log_container_verbose() { + if [[ $IMMICH_LOG_LEVEL == verbose ]]; then + echo "$1" > /proc/1/fd/2 + fi +} + if [[ ( $IMMICH_WORKERS_INCLUDE != '' && $IMMICH_WORKERS_INCLUDE != *api* ) || $IMMICH_WORKERS_EXCLUDE == *api* ]]; then - echo "API worker excluded, skipping"; - exit 0; + echo "API worker excluded, skipping" + exit 0 fi IMMICH_HOST="${IMMICH_HOST:-localhost}" @@ -12,11 +18,13 @@ result=$(curl -fsS -m 2 http://"$IMMICH_HOST":"$IMMICH_PORT"/api/server/ping) result_exit=$? if [ $result_exit != 0 ]; then - echo "Fail: exit code is $result_exit"; - exit 1; + echo "Fail: exit code is $result_exit" + log_container_verbose "Healthcheck failed: exit code $result_exit" + exit 1 fi -if [ "$result" != "{\"res\":\"pong\"}" ]; then - echo "Fail: didn't reply with pong"; - exit 1; +if [ "$result" != '{"res":"pong"}' ]; then + echo "Fail: didn't reply with pong" + log_container_verbose "Healthcheck failed: didn't reply with pong" + exit 1 fi diff --git a/server/package-lock.json b/server/package-lock.json index 8045976b3c..47ee93cae8 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.131.3", + "version": "1.134.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.131.3", + "version": "1.134.0", "hasInstallScript": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -19,19 +19,20 @@ "@nestjs/schedule": "^5.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.57.0", + "@opentelemetry/auto-instrumentations-node": "^0.59.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.200.0", - "@opentelemetry/sdk-node": "^0.200.0", - "@react-email/components": "^0.0.36", + "@opentelemetry/exporter-prometheus": "^0.201.0", + "@opentelemetry/sdk-node": "^0.201.0", + "@react-email/components": "^0.0.41", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^5.1.1", - "bullmq": "^4.8.0", + "bullmq": "^5.51.0", "chokidar": "^3.5.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", "exiftool-vendored": "^28.3.1", @@ -52,7 +53,7 @@ "nestjs-kysely": "^1.1.0", "nestjs-otel": "^6.0.0", "nodemailer": "^6.9.13", - "openid-client": "^5.4.3", + "openid-client": "^6.3.3", "pg": "^8.11.3", "picomatch": "^4.0.2", "react": "^19.0.0", @@ -63,7 +64,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.33.0", + "sharp": "^0.34.2", "sirv": "^3.0.0", "tailwindcss-preset-email": "^1.3.2", "thumbhash": "^0.1.1", @@ -83,6 +84,7 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", @@ -90,9 +92,9 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@types/nodemailer": "^6.4.14", - "@types/picomatch": "^3.0.0", + "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", "@types/react": "^19.0.0", "@types/sanitize-html": "^2.13.0", @@ -105,8 +107,10 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", + "jsdom": "^26.1.0", "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.0", + "node-addon-api": "^8.3.1", + "node-gyp": "^11.2.0", "patch-package": "^8.0.0", "pngjs": "^7.0.0", "prettier": "^3.0.2", @@ -171,14 +175,14 @@ } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.6.tgz", - "integrity": "sha512-OCLVk1YbTWfaZwpKPnd+9A34eMAZIRjntdugGvfw21ok9dUA8gICGDhfYATSfnU8/AbVQMTPK5sgG0xhUEm3UA==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.8.tgz", + "integrity": "sha512-RFnlyu4Ld8I4xvu/eqrhjbQ6kQTr27w79omMiTbQcQZvP3E6oUyZdBjobyih4Np+1VVQrbdEeNz76daP2iUDig==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", + "@angular-devkit/core": "19.2.8", + "@angular-devkit/schematics": "19.2.8", "@inquirer/prompts": "7.3.2", "ansi-colors": "4.1.3", "symbol-observable": "4.0.0", @@ -194,9 +198,9 @@ } }, "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", + "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -221,6 +225,25 @@ } } }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/schematics": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.8.tgz", + "integrity": "sha512-QsmFuYdAyeCyg9WF/AJBhFXDUfCwmDFTEbsv5t5KPSP6slhk0GoLNZApniiFytU2siRlSxVNpve2uATyYuAYkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.8", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", @@ -435,6 +458,20 @@ "node": ">= 8" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.4.tgz", + "integrity": "sha512-SeuBV4rnjpFNjI8HSgKUwteuFdkHwkboq31HWzznuqgySQir+jSTczoWVVL4jvOjKjuH80fMDG0Fvg1Sb+OJsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -584,18 +621,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -624,12 +661,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz", + "integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -680,13 +717,13 @@ } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -720,272 +757,131 @@ "node": ">=0.1.90" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz", + "integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz", + "integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.2.tgz", - "integrity": "sha512-+b+3BJl18a0LKeHvy5eLOwPkiaz10C2MUUYKQ25itZS50TlP5FuDh2Q5EiFlB++vAuCS6HnrihqVlbdcRYyp9w==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", @@ -1002,139 +898,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -1199,9 +966,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1249,13 +1016,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -1269,32 +1039,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -1431,9 +1188,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", + "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", "cpu": [ "arm64" ], @@ -1449,13 +1206,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "@img/sharp-libvips-darwin-arm64": "1.1.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", "cpu": [ "x64" ], @@ -1471,13 +1228,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "@img/sharp-libvips-darwin-x64": "1.1.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", "cpu": [ "arm64" ], @@ -1491,9 +1248,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", "cpu": [ "x64" ], @@ -1507,9 +1264,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", "cpu": [ "arm" ], @@ -1523,9 +1280,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", "cpu": [ "arm64" ], @@ -1538,10 +1295,26 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", "cpu": [ "s390x" ], @@ -1555,9 +1328,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", "cpu": [ "x64" ], @@ -1571,9 +1344,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", "cpu": [ "arm64" ], @@ -1587,9 +1360,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", "cpu": [ "x64" ], @@ -1603,9 +1376,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", "cpu": [ "arm" ], @@ -1621,13 +1394,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "@img/sharp-libvips-linux-arm": "1.1.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", "cpu": [ "arm64" ], @@ -1643,13 +1416,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "@img/sharp-libvips-linux-arm64": "1.1.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", "cpu": [ "s390x" ], @@ -1665,13 +1438,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "@img/sharp-libvips-linux-s390x": "1.1.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", "cpu": [ "x64" ], @@ -1687,13 +1460,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "@img/sharp-libvips-linux-x64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", "cpu": [ "arm64" ], @@ -1709,13 +1482,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", "cpu": [ "x64" ], @@ -1731,20 +1504,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@emnapi/runtime": "^1.4.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1753,10 +1526,29 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", "cpu": [ "ia32" ], @@ -1773,9 +1565,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", "cpu": [ "x64" ], @@ -2184,6 +1976,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -2335,58 +2140,6 @@ "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", "license": "MIT" }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", @@ -2400,19 +2153,6 @@ "linux" ] }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@nestjs/bull-shared": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.2.tgz", @@ -2442,15 +2182,15 @@ } }, "node_modules/@nestjs/cli": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.6.tgz", - "integrity": "sha512-Xco8pTdWHCpTXPTYMkUGAE+C7JXvAv38oVUaQeL81o7UOAi39w8p456r+IjONN/7ekjzakWnqepDzuTtH5Xk5w==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.7.tgz", + "integrity": "sha512-svrP8j1R0/lQVJ8ZI3BlDtuZxmkvVJokUJSB04sr6uibunk2wHeVDDVLZvYBUorCdGU/RHJl1IufhqUBM91vAQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", - "@angular-devkit/schematics-cli": "19.2.6", + "@angular-devkit/core": "19.2.8", + "@angular-devkit/schematics": "19.2.8", + "@angular-devkit/schematics-cli": "19.2.8", "@inquirer/prompts": "7.4.1", "@nestjs/schematics": "^11.0.1", "ansis": "3.17.0", @@ -2464,8 +2204,8 @@ "tree-kill": "1.2.2", "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.7.3", - "webpack": "5.98.0", + "typescript": "5.8.3", + "webpack": "5.99.6", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2475,7 +2215,7 @@ "node": ">= 20.11" }, "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0", + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", "@swc/core": "^1.3.62" }, "peerDependenciesMeta": { @@ -2488,9 +2228,9 @@ } }, "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", + "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2515,6 +2255,25 @@ } } }, + "node_modules/@nestjs/cli/node_modules/@angular-devkit/schematics": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.8.tgz", + "integrity": "sha512-QsmFuYdAyeCyg9WF/AJBhFXDUfCwmDFTEbsv5t5KPSP6slhk0GoLNZApniiFytU2siRlSxVNpve2uATyYuAYkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.8", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, "node_modules/@nestjs/cli/node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -2589,27 +2348,15 @@ "node": ">= 8" } }, - "node_modules/@nestjs/cli/node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@nestjs/common": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.17.tgz", - "integrity": "sha512-FwKylI/hVxaNvzBJdWMMG1LH0cLKz4Oh4jKOHet2JUVMM9j6CuodRbrSnL++KL6PJY/b2E6AY58UDPLNeCqJWw==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.1.tgz", + "integrity": "sha512-crzp+1qeZ5EGL0nFTPy9NrVMAaUWewV5AwtQyv6SQ9yQPXwRl9W9hm1pt0nAtUu5QbYMbSuo7lYcF81EjM+nCA==", "license": "MIT", "dependencies": { + "file-type": "20.5.0", "iterare": "1.2.1", + "load-esm": "1.0.2", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -2618,9 +2365,8 @@ "url": "https://opencollective.com/nest" }, "peerDependencies": { - "class-transformer": "*", - "class-validator": "*", - "file-type": "^20.4.1", + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, @@ -2630,16 +2376,13 @@ }, "class-validator": { "optional": true - }, - "file-type": { - "optional": true } } }, "node_modules/@nestjs/core": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.0.17.tgz", - "integrity": "sha512-ImK6qNxtegKqK7EJLGTBpP5Ild/DTpcduEtAOS+WLLjZOMjK1k214G9roXvlrNQwlVt9ALAY2jcqnsasdEd7Ow==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.1.tgz", + "integrity": "sha512-UFoUAgLKFT+RwHTANJdr0dF7p0qS9QjkaUPjg8aafnjM/qxxxrUVDB49nVvyMlk+Hr1+vvcNaOHbWWQBxoZcHA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2711,9 +2454,9 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.0.17.tgz", - "integrity": "sha512-et6Ydd6dR0FlcE/WR/9VRnQoTqEpDdzBgGK+aWadA0dFJ65wlN+snJRg/9JGP4ngj90S6xwe0VKD/BbfUGj9cw==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.1.tgz", + "integrity": "sha512-IUxk380qnUtz0PCRQ5i+o9UHlGMrFzGPIJxDwyt3JZZwx2AngOlcEcm5e+7YeJQEr2QYX2QyC4tUQg0zde+D7A==", "license": "MIT", "dependencies": { "cors": "2.8.5", @@ -2732,9 +2475,9 @@ } }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.0.17.tgz", - "integrity": "sha512-l9b8VNb7N7rB9IUwKeln2bMQDltsR9mpenzHOaYYqDkz5BtuQSiyT8NpLR2vWhxDjppxMY3DkW8fQAvXh54pMg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.1.tgz", + "integrity": "sha512-Bsc8ouysUFasWiO8RKEvppqYM5LNkHfbyIJQTy3V6+PUdYhblkvmOq8QtjuHpv6DiBI4siUcxACx/90/CdXLkQ==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -2887,9 +2630,9 @@ } }, "node_modules/@nestjs/swagger": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.1.3.tgz", - "integrity": "sha512-vhbW/Xu05Diti/EwYQp3Ea7Hj2M++wiakCcxqUUDA2n7NvCZC8LKsrcGynw6/x/lugdXyklYS+s2FhdAfeAikg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.0.tgz", + "integrity": "sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg==", "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.15.1", @@ -2920,9 +2663,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.17.tgz", - "integrity": "sha512-ryEx6fCYZFCsjEBZo8jOVikQluEHMESocVqHdXWOkkG7UqMPMHimf9gT2qij0GpNnYeDAGw+i7FhSJN3Cajoug==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.1.tgz", + "integrity": "sha512-stzm8YrLDGAijHYQw+8Z9dD6lGdvahL0hIjGVZ/0KBxLZht0/rvRjgV31UK+DUqXaF7yhJTw9ryrPaITxI1J6A==", "dev": true, "license": "MIT", "dependencies": { @@ -2948,9 +2691,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.0.17", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.0.17.tgz", - "integrity": "sha512-2LSjxA/lUKs5hv/g5lPk555CoRNTCt/XywHFteKMSrxo09Cq3yfOQOAPwEWG929EnqAjAAsQaDVbfUHUFisFCg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.1.tgz", + "integrity": "sha512-gxwQoGx5bW5IvparzrX1UOGXz87eqY0fK5Y6yb14z6tSSubQTciNjCDm5osDEkRyRCG6ZB0F+eXF6dRUjwTlBQ==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -3104,6 +2847,19 @@ "node": ">= 10" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3139,6 +2895,60 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", + "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/fs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@nuxt/opencollective": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", @@ -3165,9 +2975,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.200.0.tgz", - "integrity": "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.201.1.tgz", + "integrity": "sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -3177,58 +2987,60 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.57.1.tgz", - "integrity": "sha512-yy+K3vYybqJ6Z4XZCXYYxEC1DtEpPrnJdwxkhI0sTtVlrVnzx49iRLqpMmdvQ4b09+PrvXSN9t0jODMCGNrs8w==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.59.0.tgz", + "integrity": "sha512-kqoEBQss8fGGGRND0ycXZrwCXa/ePFop6W+YvZF5PikA9EsH0J/F2W6zvjetKjtdjyl6AUDW8I7gslZPXLLz3Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", - "@opentelemetry/instrumentation-amqplib": "^0.47.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.51.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.51.0", - "@opentelemetry/instrumentation-bunyan": "^0.46.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.46.0", - "@opentelemetry/instrumentation-connect": "^0.44.0", - "@opentelemetry/instrumentation-cucumber": "^0.15.0", - "@opentelemetry/instrumentation-dataloader": "^0.17.0", - "@opentelemetry/instrumentation-dns": "^0.44.0", - "@opentelemetry/instrumentation-express": "^0.48.0", - "@opentelemetry/instrumentation-fastify": "^0.45.0", - "@opentelemetry/instrumentation-fs": "^0.20.0", - "@opentelemetry/instrumentation-generic-pool": "^0.44.0", - "@opentelemetry/instrumentation-graphql": "^0.48.0", - "@opentelemetry/instrumentation-grpc": "^0.200.0", - "@opentelemetry/instrumentation-hapi": "^0.46.0", - "@opentelemetry/instrumentation-http": "^0.200.0", - "@opentelemetry/instrumentation-ioredis": "^0.48.0", - "@opentelemetry/instrumentation-kafkajs": "^0.9.0", - "@opentelemetry/instrumentation-knex": "^0.45.0", - "@opentelemetry/instrumentation-koa": "^0.48.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.45.0", - "@opentelemetry/instrumentation-memcached": "^0.44.0", - "@opentelemetry/instrumentation-mongodb": "^0.53.0", - "@opentelemetry/instrumentation-mongoose": "^0.47.0", - "@opentelemetry/instrumentation-mysql": "^0.46.0", - "@opentelemetry/instrumentation-mysql2": "^0.46.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.46.0", - "@opentelemetry/instrumentation-net": "^0.44.0", - "@opentelemetry/instrumentation-pg": "^0.52.0", - "@opentelemetry/instrumentation-pino": "^0.47.0", - "@opentelemetry/instrumentation-redis": "^0.47.0", - "@opentelemetry/instrumentation-redis-4": "^0.47.0", - "@opentelemetry/instrumentation-restify": "^0.46.0", - "@opentelemetry/instrumentation-router": "^0.45.0", - "@opentelemetry/instrumentation-socket.io": "^0.47.0", - "@opentelemetry/instrumentation-tedious": "^0.19.0", - "@opentelemetry/instrumentation-undici": "^0.11.0", - "@opentelemetry/instrumentation-winston": "^0.45.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.0", - "@opentelemetry/resource-detector-aws": "^2.0.0", - "@opentelemetry/resource-detector-azure": "^0.7.0", - "@opentelemetry/resource-detector-container": "^0.7.0", - "@opentelemetry/resource-detector-gcp": "^0.34.0", + "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/instrumentation-amqplib": "^0.48.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.52.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.53.0", + "@opentelemetry/instrumentation-bunyan": "^0.47.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.47.0", + "@opentelemetry/instrumentation-connect": "^0.45.0", + "@opentelemetry/instrumentation-cucumber": "^0.16.0", + "@opentelemetry/instrumentation-dataloader": "^0.18.0", + "@opentelemetry/instrumentation-dns": "^0.45.0", + "@opentelemetry/instrumentation-express": "^0.50.0", + "@opentelemetry/instrumentation-fastify": "^0.46.0", + "@opentelemetry/instrumentation-fs": "^0.21.0", + "@opentelemetry/instrumentation-generic-pool": "^0.45.0", + "@opentelemetry/instrumentation-graphql": "^0.49.0", + "@opentelemetry/instrumentation-grpc": "^0.201.0", + "@opentelemetry/instrumentation-hapi": "^0.47.0", + "@opentelemetry/instrumentation-http": "^0.201.0", + "@opentelemetry/instrumentation-ioredis": "^0.49.0", + "@opentelemetry/instrumentation-kafkajs": "^0.10.0", + "@opentelemetry/instrumentation-knex": "^0.46.0", + "@opentelemetry/instrumentation-koa": "^0.49.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.46.0", + "@opentelemetry/instrumentation-memcached": "^0.45.0", + "@opentelemetry/instrumentation-mongodb": "^0.54.0", + "@opentelemetry/instrumentation-mongoose": "^0.48.0", + "@opentelemetry/instrumentation-mysql": "^0.47.0", + "@opentelemetry/instrumentation-mysql2": "^0.47.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.47.0", + "@opentelemetry/instrumentation-net": "^0.45.0", + "@opentelemetry/instrumentation-oracledb": "^0.27.0", + "@opentelemetry/instrumentation-pg": "^0.53.0", + "@opentelemetry/instrumentation-pino": "^0.48.0", + "@opentelemetry/instrumentation-redis": "^0.48.0", + "@opentelemetry/instrumentation-redis-4": "^0.48.0", + "@opentelemetry/instrumentation-restify": "^0.47.0", + "@opentelemetry/instrumentation-router": "^0.46.0", + "@opentelemetry/instrumentation-runtime-node": "^0.15.0", + "@opentelemetry/instrumentation-socket.io": "^0.48.0", + "@opentelemetry/instrumentation-tedious": "^0.20.0", + "@opentelemetry/instrumentation-undici": "^0.12.0", + "@opentelemetry/instrumentation-winston": "^0.46.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.1", + "@opentelemetry/resource-detector-aws": "^2.1.0", + "@opentelemetry/resource-detector-azure": "^0.8.0", + "@opentelemetry/resource-detector-container": "^0.7.1", + "@opentelemetry/resource-detector-gcp": "^0.35.0", "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-node": "^0.200.0" + "@opentelemetry/sdk-node": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3239,9 +3051,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.0.tgz", - "integrity": "sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", + "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3251,9 +3063,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.0.tgz", - "integrity": "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3266,17 +3078,17 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.200.0.tgz", - "integrity": "sha512-+3MDfa5YQPGM3WXxW9kqGD85Q7s9wlEMVNhXXG7tYFLnIeaseUt9YtCeFhEDFzfEktacdFpOtXmJuNW8cHbU5A==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.201.1.tgz", + "integrity": "sha512-ACV2Az9BHRcAaPMYBnYMwKHNn2JwkzzsT3cdeG6+Tokm47fFfpf2xk3sq3QvX0Gk+TXW7q6d+OfBuYfWoAud2g==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/sdk-logs": "0.200.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/sdk-logs": "0.201.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3286,16 +3098,16 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.200.0.tgz", - "integrity": "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.201.1.tgz", + "integrity": "sha512-flYr1tr/wlUxsVc2ZYt/seNLgp3uagyUg9MtjiHYyaMQcN4XuEuI4UjUFwXAGQjd2khmXeie5YnTmO8gzyzemw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/sdk-logs": "0.200.0" + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/sdk-logs": "0.201.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3305,18 +3117,18 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.200.0.tgz", - "integrity": "sha512-GmahpUU/55hxfH4TP77ChOfftADsCq/nuri73I/AVLe2s4NIglvTsaACkFVZAVmnXXyPS00Fk3x27WS3yO07zA==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.201.1.tgz", + "integrity": "sha512-ZVkutDoQYLAkWmpbmd9XKZ9NeBQS6GPxLl/NZ/uDMq+tFnmZu1p0cvZ43x5+TpFoGkjPR6QYHCxkcZBwI9M8ag==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-logs": "0.200.0", - "@opentelemetry/sdk-trace-base": "2.0.0" + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3326,19 +3138,19 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.200.0.tgz", - "integrity": "sha512-uHawPRvKIrhqH09GloTuYeq2BjyieYHIpiklOvxm9zhrCL2eRsnI/6g9v2BZTVtGp8tEgIa7rCQ6Ltxw6NBgew==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.201.1.tgz", + "integrity": "sha512-ywo4TpQNOLi07K7P3CaymzS8XlDGfTFmMQ4oSPsZv38/gAf3/wPVh2uL5qYAFqrVokNCmkcaeCwX3QSy0g9b/A==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-metrics": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3348,16 +3160,16 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.200.0.tgz", - "integrity": "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.201.1.tgz", + "integrity": "sha512-LMRVg2yTev28L51RLLUK3gY0avMa1RVBq7IkYNtXDBxJRcd0TGGq/0rqfk7Y4UIM9NCJhDIUFHeGg8NpSgSWcw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-metrics": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3367,17 +3179,17 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.200.0.tgz", - "integrity": "sha512-E+uPj0yyvz81U9pvLZp3oHtFrEzNSqKGVkIViTQY1rH3TOobeJPSpLnTVXACnCwkPR5XeTvPnK3pZ2Kni8AFMg==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.201.1.tgz", + "integrity": "sha512-9ie2jcaUQZdIoe6B02r0rF4Gz+JsZ9mev/2pYou1N0woOUkFM8xwO6BAlORnrFVslqF/XO5WG3q5FsTbuC5iiw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-metrics": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3387,14 +3199,14 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.200.0.tgz", - "integrity": "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.201.1.tgz", + "integrity": "sha512-J6/4KgljApWda/2YBMHHZg6vaZ6H8BjFInO8YQW+N0al1LjGAAq3pFRCEHpU6GI7ZlkphCxKy6MUjXOZVM8KWQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-metrics": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3404,18 +3216,18 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.200.0.tgz", - "integrity": "sha512-hmeZrUkFl1YMsgukSuHCFPYeF9df0hHoKeHUthRKFCxiURs+GwF1VuabuHmBMZnjTbsuvNjOB+JSs37Csem/5Q==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.201.1.tgz", + "integrity": "sha512-0ZM5CBoZbufXckxi/SWwP5B++CjPWS6N1i+K7f+GhRxYWVGt/yh4eiV3jklZKWw/DUyMkUvUOo0GW1RxoiLoZQ==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3425,16 +3237,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.200.0.tgz", - "integrity": "sha512-Goi//m/7ZHeUedxTGVmEzH19NgqJY+Bzr6zXo1Rni1+hwqaksEyJ44gdlEMREu6dzX1DlAaH/qSykSVzdrdafA==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.201.1.tgz", + "integrity": "sha512-Nw3pIqATC/9LfSGrMiQeeMQ7/z7W2D0wKPxtXwAcr7P64JW7KSH4YSX7Ji8Ti3MmB79NQg6imdagfegJDB0rng==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3444,16 +3256,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.200.0.tgz", - "integrity": "sha512-V9TDSD3PjK1OREw2iT9TUTzNYEVWJk4Nhodzhp9eiz4onDMYmPy3LaGbPv81yIR6dUb/hNp/SIhpiCHwFUq2Vg==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.201.1.tgz", + "integrity": "sha512-wMxdDDyW+lmmenYGBp0evCoKzajXqIw6SSaZtaF/uqKR9/POhC/9vudnc+kf8W49hYFyIEutPrc1hA0exe3UwQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3463,14 +3275,14 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.0.tgz", - "integrity": "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.1.tgz", + "integrity": "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3496,12 +3308,12 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.200.0.tgz", - "integrity": "sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.201.1.tgz", + "integrity": "sha512-6EOSoT2zcyBM3VryAzn35ytjRrOMeaWZyzQ/PHVfxoXp5rMf7UUgVToqxOhQffKOHtC7Dma4bHt+DuwIBBZyZA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/api-logs": "0.201.1", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", @@ -3515,13 +3327,13 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.47.0.tgz", - "integrity": "sha512-bQboBxolOVDcD4l5QAwqKYpJVKQ8BW82+8tiD5uheu0hDuYgdmDziSAByc8yKS7xpkJw4AYocVP7JwSpQ1hgjg==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.48.0.tgz", + "integrity": "sha512-zXcClQX3sttvBih1CjdPbvve/If1lCHPFK41fDpJE5NYjK38dwTMOUEV0+/ulfq4iU4oEV+ReCA+ZaXAm/uYdw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3532,12 +3344,12 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.51.0.tgz", - "integrity": "sha512-yPtnDum6vykhxA1xZ2kKc3DGmrLdbRAkJG0HiQUcOas47j716wmtqsLCctHyXgO0NpmS/BCzbUnOxxPG6kln7A==", + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.52.0.tgz", + "integrity": "sha512-xGVhBxxO7OuOl72XNwt1MOgaA6d3pSKI2Y5r3OfGNkx602KzW1t2vBHzJf8s4DAJYdMd5/RJLRi1z87CBu7yyg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "8.10.147" }, @@ -3549,15 +3361,15 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.51.0.tgz", - "integrity": "sha512-NfmdJqrgJyAPGzPJk2bNl8vBn2kbDIHyTmKVNWhcQWh0VCA5aspi75Gsp5tHmLqk26VAtVtUEDZwK3nApFEtzw==", + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.53.0.tgz", + "integrity": "sha512-CXB2cu0qnp5lHtNZRpvz0oOZrIKiWfHOiNVGWln9KY0m9sBheEqc58x3Ptpi5lMyso67heVCGDAc9+KbLAZwTQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", - "@opentelemetry/propagation-utils": "^0.31.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/propagation-utils": "^0.31.1", + "@opentelemetry/semantic-conventions": "^1.31.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3567,13 +3379,13 @@ } }, "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.46.0.tgz", - "integrity": "sha512-7ERXBAMIVi1rtFG5odsLTLVy6IJZnLLB74fFlPstV7/ZZG04UZ8YFOYVS14jXArcPohY8HFYLbm56dIFCXYI9w==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.47.0.tgz", + "integrity": "sha512-Sux5us8fkBLO/z+H8P2fSu+fRIm1xTeUHlwtM/E4CNZS9W/sAYrc8djZVa2JrwNXj/tE6U5vRJVObGekIkULow==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.200.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/api-logs": "^0.201.0", + "@opentelemetry/instrumentation": "^0.201.0", "@types/bunyan": "1.8.11" }, "engines": { @@ -3584,12 +3396,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.46.0.tgz", - "integrity": "sha512-ItT2C32afignjHQosleI/iBjzlHhF+F7tJIK9ty47/CceVNlA9oK39ss9f7o9jmnKvQfhNWffvkXdjc0afwnSQ==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.47.0.tgz", + "integrity": "sha512-MMn/Y2ErClGe7fmzTfR3iJcbEIspAn9hxbnj8oH7bVpPHcWbPphYICkNfLqah4tKVd+zazhs1agCiHL8y/e12g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3600,13 +3412,13 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.44.0.tgz", - "integrity": "sha512-eChFPViU/nkHsCYSp2PCnHnxt/ZmI/N5reHcwmjXbKhEj6TRNJcjLpI+OQksP8lLu0CS9DlDosHEhknCsxLdjQ==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.45.0.tgz", + "integrity": "sha512-OHdp71gsRnm0lVD7SEtYSJFfvq4r6QN/5lgRK+Vrife1DHy+Insm66JJZN2Frt1waIzmDNn3VLCCafTnItfVcA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, @@ -3618,12 +3430,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.15.0.tgz", - "integrity": "sha512-MOHDzttn5TSBqt4j3/XjBhYNH0iLQP7oX2pumIzXP7dJFTcUtaq6PVakKPtIaqBTTabOKqCJhrF240XGwWefPQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.16.0.tgz", + "integrity": "sha512-bLKOQFgKimQkD8th+y0zMD9vNBjq79BWmPd7QqOGV2atQFbb2QJnorp/Y6poTVQNiITv0GE2mmmcqbjF+Y+JQA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3634,12 +3446,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.17.0.tgz", - "integrity": "sha512-JqovxOo7a65+3A/W+eiqUv7DrDsSvsY0NemHJ4uyVrzD4bpDYofVRdnz/ehYcNerlxVIKU+HcybDmiaoj41DPw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.18.0.tgz", + "integrity": "sha512-egPb8OcGZP6GUU/dbB8NnVgnSIqlM0nHS8KkADq51rVaMkzBcevtinYDFYTQu9tuQ6GEwaSdiQxiQORpYaVeQw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3649,12 +3461,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.44.0.tgz", - "integrity": "sha512-+tAFXkFPldOpIba2akqKQ1ukqHET1pZ4pqhrr5x0p+RJ+1a1pPmTt1vCyvSSr634WOY8qMSmzZps++16yxnMbA==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.45.0.tgz", + "integrity": "sha512-gE02Jj97aaYUdZIvp2RwWPy3DLN86k15YvPRzkMaPWZKVwsKrHcA+xVX8k3rh9o0g64PC/U2f+LXiJr14PyVLg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3664,13 +3476,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.48.0.tgz", - "integrity": "sha512-x9L6YD7AfE+7hysSv8k0d0sFmq3Vo3zoa/5eeJBYkGWHnD92CvekKouPyqUt71oX0htmZRdIawrhrwrAi2sonQ==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.50.0.tgz", + "integrity": "sha512-0VF7HM8hTe0B5oXqCfBljMYFeQ3WKKqs0kCTRT02/Pjnmj5bOmR62r2dstjxbxnGKoeFRUHD/QAown9gyf659A==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3681,13 +3493,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.45.0.tgz", - "integrity": "sha512-m94anTFZ6jpvK0G5fXIiq1sB0gCgY2rAL7Cg7svuOh9Roya2RIQz2E5KfCsO1kWCmnHNeTo7wIofoGN7WLPvsA==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.46.0.tgz", + "integrity": "sha512-tib8SH5RCqhYRw9Qcpep9tP6ABxyXFDljdRy2aKpklHaFAyDELr3EpEAkGdkMZtO5Y3/QhUsmzYZp1np9jkjUg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3698,13 +3510,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.20.0.tgz", - "integrity": "sha512-30l45ovjwHb16ImCGVjKCvw5U7X1zKuYY26ii5S+goV8BZ4a/TCpBf2kQxteQjWD05Gl3fzPMZI5aScfPI6Rjw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.21.0.tgz", + "integrity": "sha512-p2Fn78KSSbSSIJOOTn9FbxEzNRIIsYn9KTemKhABuunVqHixIqQ3hUjChbR+RbjPNZQthDC/0GHDeihRoyLdLQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3714,12 +3526,12 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.44.0.tgz", - "integrity": "sha512-bY7locZDqmQLEtY2fIJbSnAbHilxfhflaEQHjevFGkaiXc9UMtOvITOy5JKHhYQISpgrvY2WGXKG7YlVyI7uMg==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.45.0.tgz", + "integrity": "sha512-+fk7tnpzkkBAQzEtyJA0zRv7aBDhr05zczyBn//iJdmDG+ZfQFuIKK4dXNnv9FUZpedW0wcHlPqbP5FIGhAsLQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3729,12 +3541,12 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.48.0.tgz", - "integrity": "sha512-w1sbf9F9bQTpIWGnKWhH1A+9N9rKxS4eM+AzczgMWp272ZM9lQv4zLTrH5NRST2ltY3nmZ72wkfFrSR0rECi0g==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.49.0.tgz", + "integrity": "sha512-FZaOS/BmE5npzk95X3Iqfo80a6wEJlkAtk7wLUJG/VZaB8RbBjJow4g0YdtvK8GNGEQW02KiQ+VtzdPGRemlwg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3744,12 +3556,12 @@ } }, "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.200.0.tgz", - "integrity": "sha512-iaPHlO1qb1WlGUq0oTx0rJND/BtBeTAtyEfflu2VwKDe8XZeia7UEOfiSQxnGqVSTwW5F0P1S5UzqeDJotreWQ==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.201.1.tgz", + "integrity": "sha512-OIkXkVnilh8E6YKz/PiQtWeERqbcbjtVppMc7A2h39eaoaKnckXxom3YXhX+/PMhfmjbUnqw6k/KvmUr9zig1Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "0.200.0", + "@opentelemetry/instrumentation": "0.201.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3760,13 +3572,13 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.46.0.tgz", - "integrity": "sha512-573y+ZxywEcq+3+Z3KqcbV45lrVwUKvQiP9OhABVFNX8wHbtM6DPRBmYfqiUkSbIBcOEihm5qH6Gs73Xq0RBEA==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.47.0.tgz", + "integrity": "sha512-0BCiQl2+oAuhSzbZrgpZgRvg7PclTfb7GxuBqWmWj9XkRk6cKla18S0pBqRCtl+qluRIaZ7tyXKmdtlsXj0QIw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3777,13 +3589,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.200.0.tgz", - "integrity": "sha512-9tqGbCJikhYU68y3k9mi6yWsMyMeCcwoQuHvIXan5VvvPPQ5WIZaV6Mxu/MCVe4swRNoFs8Th+qyj0TZV5ELvw==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.201.1.tgz", + "integrity": "sha512-xhkL/eOntScSLS8C2/LHKZ9Z9MEyGB9Yil7lF3JV0+YBeLXHQUIw2xPD7T0qw0DnqlrN8c/gi8hb5BEXZcyHRg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/instrumentation": "0.200.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/instrumentation": "0.201.1", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -3795,12 +3607,12 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.48.0.tgz", - "integrity": "sha512-kQhdrn/CAfJIObqbyyGtagWNxPvglJ9FwnWmsfXKodaGskJv/nyvdC9yIcgwzjbkG1pokVUROrvJ0mizqm29Tg==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.49.0.tgz", + "integrity": "sha512-CcbA9ylntqK7/lo7NUD/I+Uj6xcIiFFk1O2RnY23MugJunqZIFufvYkdh1mdG2bvBKdIVvA2nkVVt1Igw0uw1A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -3812,12 +3624,12 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.9.0.tgz", - "integrity": "sha512-Uxt/LTSmrzTYtnPpPn/L2W7+tjn38+v8tSnJ7hvaE3/aRXmZA5e72n+pHv0mlCI0pVNTihiQCUE62XYWPZ4jjA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.10.0.tgz", + "integrity": "sha512-0roBjhMaW5li1gXVqrBRjzeLPWUiym8TPQi3iXqMA3GizPzilE4hwhIVI7GxtMHAdS15TgkUce6WVYVOBFrrbg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -3828,12 +3640,12 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.45.0.tgz", - "integrity": "sha512-2kkyTDUzK/3G3jxTc+NqHSdgi1Mjw2irZ98T/cSyNdlbsnDOMSTHjbm0AxJCV4QYQ4cKW7a8W/BBgxDGlu+mXQ==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.46.0.tgz", + "integrity": "sha512-+AxDwDdLJB467mEPOQKHod/1NDzX8msUAOEiViMkM7xAJoUsHTrP6EKlbjrCKkK+X2Eqh2pTO0ibeLkhG96oNA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3844,13 +3656,13 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.48.0.tgz", - "integrity": "sha512-LV63v3pxFpjKC0IJO+y5nsGdcH+9Y8Wnn0fhu673XZ5auxqJk2t4nIHuSmls08oRKaX+5q1e+h70XmP/45NJsw==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.49.0.tgz", + "integrity": "sha512-LO2pdZ5SF2LzWZLwrPTja/sQN8Kl4Wu5QvWSFJJLLGpeVKQWC4n41qjPUAAu668w43s42xqfs9bC4hWmQe7o8g==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3861,12 +3673,12 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.45.0.tgz", - "integrity": "sha512-W2MNx7hPtvSIgEFxFrqdBykdfN0UrShCbJxvMU9fwgqbOdxIrcubPt0i1vmy3Ap6QwSi+HmsRNQD2w3ucbLG3A==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.46.0.tgz", + "integrity": "sha512-k8wdehAJYuSYWKiIDXrXSd7+33M4qOUEhrE3ymNFOHxVjwtUWpSh6JYSFe+5pqGilhl4CqUgxCkaQ9kPy3rAOQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3876,12 +3688,12 @@ } }, "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.44.0.tgz", - "integrity": "sha512-1zABdJlF9Tk0yUv2ELpF6Mk2kw81k+bnB3Sw+D/ssRDcGGCnCNbz+fKJE8dwAPkDP+OcTmiKm6ySREbcyRFzCg==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.45.0.tgz", + "integrity": "sha512-9NjbvCBM7p+wh/sHfSGDvrtinFYqIr6qunL9nN3e86eIQh3WyE9YdnlFGRbBR+MOzTCwSzrKAvY+J0fQe91VHA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/memcached": "^2.2.6" }, @@ -3893,12 +3705,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.53.0.tgz", - "integrity": "sha512-zS2gQJQuG7RZw5yaNG/TnxsOtv1fFkn3ypuDrVLJtJLZtcOr4GYn31jbIA8od+QW/ChZLVcH364iDs+z/xS9wA==", + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.54.0.tgz", + "integrity": "sha512-xTECmvFNfavpNz7btxmmvkCZKdHphQSSf0J4tSw4OOT0CSTythB/IWo41mYBd6GIutkmeA12dkKPd8zAU7zzyA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3909,13 +3721,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.47.0.tgz", - "integrity": "sha512-zg4ixMNmuACda75eOFa1m5h794zC9wp397stX0LAZvOylSb6dWT52P6ElkVQMV42C/27liEdQWxpabsamB+XPQ==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.48.0.tgz", + "integrity": "sha512-kvopwp/kb1wN8jd0HhIBx/ZxbSmwqhN7LLvl9a7fXYACYlewUtCnVJLG80kwuG+rexRZlxeDfjoacFRDQSf9XA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3926,12 +3738,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.46.0.tgz", - "integrity": "sha512-Z1NDAv07suIukgL7kxk9cAQX1t/smRMLNOU+q5Aqnhnf/0FIF/N4cX2wg+25IWy0m2PoaPbAVYCKB0aOt5vzAw==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.47.0.tgz", + "integrity": "sha512-QWJNDNW0JyHj3cGtQOeNBcrDeOY35yX/JnDg8jEvxzmoEABHyj0EqI8fHPdOQmdctTjKTjzbqwtuAzLYIfkdAA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mysql": "2.15.26" }, @@ -3943,12 +3755,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.46.0.tgz", - "integrity": "sha512-JsmIA+aTfHqy2tahjnVWChRipYpYrTy+XFAuUPia9CTaspCx8ZrirPUqYnbnaPEtnzYff2a4LX0B2LT1hKlOiA==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.47.0.tgz", + "integrity": "sha512-rVKuKJ6HFVTNXNo8WuC3lBL/9zQ0OZfga/2dLseg/jlQZzUlWijsA57trnA92pcYxs32HBPSfKpuA88ZAVBFpA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0" }, @@ -3960,12 +3772,12 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.46.0.tgz", - "integrity": "sha512-5cYnBIMZuTSLFUt0pMH+NQNdI5/2YeCVuz29Mo2lkudbBUOvzGmzl/Y6LG1JEw2j6zuJx5IgO5CKNrJqAIzTWA==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.47.0.tgz", + "integrity": "sha512-xTtWbqdvlxRfhYidLEq0XvQUGqqgT4Fom21nxJ7XYvOoUJ4KNOxFBnfGW9RcXtFHDkux6rIjNP5CiPCYMZ007g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -3976,12 +3788,12 @@ } }, "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.44.0.tgz", - "integrity": "sha512-SmAbOKTi0lgdTN9XMXOaf+4jw670MpiK3pw9/to/kRlTvNWwWA4RD34trCcoL7Gf2IYoXuj56Oo4Z5C7N98ukw==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.45.0.tgz", + "integrity": "sha512-kFdY4IMth8obBPXoAlpLkea7l85Joe+p7oep+BexrHQ0iX+0cvnfoYBMMSE/vAp6T1N3Nu6RDT2Wzf3mqkHxjw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -3991,14 +3803,31 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-oracledb": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.27.0.tgz", + "integrity": "sha512-b/JBJroC22DqgeMUSLYyleN6ohyXbCK1YGvBsCuDdiYUmOOyyWYSKdm4D26hTwFv1TKce+Im6aGcXF1hq2WKuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.201.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/oracledb": "6.5.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.52.0.tgz", - "integrity": "sha512-OBpqlxTqmFkZGHaHV4Pzd95HkyKVS+vf0N5wVX3BSb8uqsvOrW62I1qt+2jNsZ13dtG5eOzvcsQTMGND76wizA==", + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.53.0.tgz", + "integrity": "sha512-riWbJvSviTAsjeuq8fn7Y7+CXEYf3sGR18WfLeM7GgSnptTOur1++SLTN7XogqiwP3LFFQ0GLoYe+hxVOEyEpw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0", "@types/pg": "8.6.1", @@ -4012,14 +3841,14 @@ } }, "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.47.0.tgz", - "integrity": "sha512-OFOy/TGtGXMYWrF4xPKhLN1evdqUpbuoKODzeh3GSjFkcooZZf4m/Hpzu12FV+s0wDBf43oAjXbNJWeCJQMrug==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.48.0.tgz", + "integrity": "sha512-+X+GTaXFuExrmQ3XS1HH8E+4KkKQ1HPzjNGnckuW/SQVOxRGeZMwJu1s60lx4eLpQuXXRh9nJaCAqMi/As347w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.200.0", + "@opentelemetry/api-logs": "^0.201.0", "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4029,12 +3858,12 @@ } }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.47.0.tgz", - "integrity": "sha512-T2YvuX/LaJEQKgKvIQJlbSMSzxp6oBm+9PMgfn7QcBXzSY9tyeyDF6QjLAKNvxs+BJeQzFmDlahjoEyatzxRWA==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.48.0.tgz", + "integrity": "sha512-bp82CqAcBNk0+nneAX2L+wbCKiNHTnTEJlppOEjxESIR8AocSKO7gnWpotTh5Bki2UULUn62MBXJmRnIzj0ikw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -4046,12 +3875,12 @@ } }, "node_modules/@opentelemetry/instrumentation-redis-4": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.47.0.tgz", - "integrity": "sha512-9LywJGp1fmmLj6g1+Rv91pVE3ATle1C/qIya9ZLwPywXTOdFIARI/gvvvlI7uFABoLojj2dSaI/5JQrq4C1HSg==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.48.0.tgz", + "integrity": "sha512-aHZGrVwOsCM5u2PQdK1/PJuIWjGjYhOKEqqaPg3Mere2C6brwp+ih1bjcGyMRBS+7KNn5OSPcsFWpcW17Bfotw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/redis-common": "^0.37.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -4063,13 +3892,13 @@ } }, "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.46.0.tgz", - "integrity": "sha512-du1FjKsTGQH6q8QjG0Bxlg0L79Co/Ey0btKKb2sg7fvg0YX6LKdR2N1fzfne/A9k+WjQ5v28JuUXOk2cEPYU/Q==", + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.47.0.tgz", + "integrity": "sha512-A1VixeXnRAQQfWidjnNqOwqGp1K5/r6fIyCdL+1Yvde11HiruMQOf6B71D7wWJHRtNKpLhq3o8JzeNGJoBEMpA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4080,12 +3909,12 @@ } }, "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.45.0.tgz", - "integrity": "sha512-CGEeT73Wy/nLQw+obG/mBCIgMbZQKrGG6hzbEdtQ4G2jqI97w7pLWdM4DvkpWVBNcxMpO13dX1nn2OiyZXND3Q==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.46.0.tgz", + "integrity": "sha512-p98dJcw0reSyfkhRwzx8HrhyjcKmyguIE0KCLcxBnvQFnPL7EfUR2up2M9ggceFiZO5GUo1gk+r/mP+B9VBsQw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4095,13 +3924,28 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.47.0.tgz", - "integrity": "sha512-qAc+XCcRmZYjs8KJIPv+MMR2wPPPOppwoarzKRR4G+yvOBs1xMwbbkqNHifKga0XcfFX4KVr7Z5QQ6ZZzWyLtg==", + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.15.0.tgz", + "integrity": "sha512-K3aPMYImALNsovPUjlIHctS2oH1YESlIAQMgiHXvcUxxz6+d66pPE1a4IoGP19iFOmRDMjshgHR/0DXMOEvZKg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.48.0.tgz", + "integrity": "sha512-bVFiRvQnAW9hT+8FZVuhhybAvopAShLGm6LYz8raNZokxEw2FzGDVXONWaAM5D2/RbCbMl7R+PLN//3SEU/k0g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -4112,12 +3956,12 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.19.0.tgz", - "integrity": "sha512-hNC/Bz+g4RvwaKsbA1VD+9x8X2Ml+fN2uba4dniIdQIrAItLdet4xx/7TEoWYtyVJQozphvpnIsUp52Rw4djCA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.20.0.tgz", + "integrity": "sha512-8OqIj554Rh8sll9myfDaFD1cYY8XKpxK3SMzCTZGc4BqS61gU0kd7UEydZeplrkQHDgySP4nvtFfkQCaZyTS4Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.200.0", + "@opentelemetry/instrumentation": "^0.201.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/tedious": "^4.0.14" }, @@ -4129,13 +3973,13 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.11.0.tgz", - "integrity": "sha512-H6ijJnKVZBB0Lhm6NsaBt0rUz+i52LriLhrpGAE8SazB0jCIVY4MrL2dNib/4w8zA+Fw9zFwERJvKXUIbSD1ew==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.12.0.tgz", + "integrity": "sha512-SLqTWPWWwqSZVYZw3a9sdcNXsahJfimvDpYaoDd6ryvQGDlOrHVKr56gL5qD3XDVa67DmV5ZQrxRrnYUdlp3BQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4145,13 +3989,13 @@ } }, "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.45.0.tgz", - "integrity": "sha512-LZz3/6QvzoneSqD/xnB8wq/g1fy8oe2PwfZ15zS2YA5mnjuSqlqgl+k3sib7wfIYHMP1D3ajfbDB6UOJBALj/w==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.46.0.tgz", + "integrity": "sha512-/nvmsLSON9Ki8C32kOMAkzsCpFfpjI2Fvr51uAY8/8bwG258MUUN8fCbAOMaiaPEKiB807wsE/aym83LYiB0ng==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.200.0", - "@opentelemetry/instrumentation": "^0.200.0" + "@opentelemetry/api-logs": "^0.201.0", + "@opentelemetry/instrumentation": "^0.201.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4161,13 +4005,13 @@ } }, "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.200.0.tgz", - "integrity": "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.201.1.tgz", + "integrity": "sha512-FiS/mIWmZXyRxYGyXPHY+I/4+XrYVTD7Fz/zwOHkVPQsA1JTakAOP9fAi6trXMio0dIpzvQujLNiBqGM7ExrQw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-transformer": "0.200.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.201.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4177,15 +4021,15 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.200.0.tgz", - "integrity": "sha512-CK2S+bFgOZ66Bsu5hlDeOX6cvW5FVtVjFFbWuaJP0ELxJKBB6HlbLZQ2phqz/uLj1cWap5xJr/PsR3iGoB7Vqw==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.201.1.tgz", + "integrity": "sha512-Y0h9hiMvNtUuXUMkYNAt81hxnFuOHHSeu/RC+pXcHe7S6ac0ROlcjdabBKmYSadJxRrP4YfLahLRuNkVtZow4w==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/otlp-exporter-base": "0.200.0", - "@opentelemetry/otlp-transformer": "0.200.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4195,17 +4039,17 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.200.0.tgz", - "integrity": "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.201.1.tgz", + "integrity": "sha512-+q/8Yuhtu9QxCcjEAXEO8fXLjlSnrnVwfzi9jiWaMAppQp69MoagHHomQj02V2WnGjvBod5ajgkbK4IoWab50A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-logs": "0.200.0", - "@opentelemetry/sdk-metrics": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0", + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "engines": { @@ -4216,9 +4060,9 @@ } }, "node_modules/@opentelemetry/propagation-utils": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.31.0.tgz", - "integrity": "sha512-Gnxes8Mwm7BwLCDobUD1A5YoFWIKDch6WQWvO+jc0uvfI4vujDExVghbGg5sTJhHc2Sg2cU0+ANgV/jUjdS79w==", + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.31.1.tgz", + "integrity": "sha512-YLNt7SWy4HZwI9d+4+OevQs2Gmof27TkjR3v029UGw8zFOcyONyIQhHHx7doyRbrLpWZtUc91cnCA4mKhArCXw==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4228,12 +4072,12 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.0.tgz", - "integrity": "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.1.tgz", + "integrity": "sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0" + "@opentelemetry/core": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4243,12 +4087,12 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.0.tgz", - "integrity": "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.1.tgz", + "integrity": "sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0" + "@opentelemetry/core": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4267,9 +4111,9 @@ } }, "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.31.0.tgz", - "integrity": "sha512-Ty3GkSnht10UySMdHC1ngwGEYMbTBxt0/PMpjwbM6ibxkgf57apx04cSeHVm9TwBE/vm9+4/zt4RciCqyWQwtA==", + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.31.1.tgz", + "integrity": "sha512-RPitvB5oHZsECnK7xtUAFdyBXRdtJbY0eEzQPBrLMQv4l/FN4pETijqv6LcKBbn6tevaoBU2bqOGnVoL4uX4Tg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4284,9 +4128,9 @@ } }, "node_modules/@opentelemetry/resource-detector-aws": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.0.0.tgz", - "integrity": "sha512-jvHvLAXzFPJJhj0AdbMOpup+Fchef32sHM1Suj4NgJGKxTO47T84i5OjKiG/81YEoCaKmlTefezNbuaGCrPd3w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.1.0.tgz", + "integrity": "sha512-7QG5wQXMiHseKIyU69m8vfZgLhrxFx48DdyaQEYj6GXjE/Xrv1nS3bUwhICjb6+4NorB9+1pFCvJ/4S01CCCjQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4301,9 +4145,9 @@ } }, "node_modules/@opentelemetry/resource-detector-azure": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.7.0.tgz", - "integrity": "sha512-aR2ALsK+b/+5lLDhK9KTK8rcuKg7+sqa/Cg+QCeasqoy7qby70FRtAbQcZGljJ5BLBcVPYjl1hcTYIUyL3Laww==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.8.0.tgz", + "integrity": "sha512-YBsJQrt0NGT66BgdVhhTkv7/oe/rTflX/rKteptVK6HNo7z8wbeAbB4SnSNJFfF+v3XrP/ruiTxKnNzoh/ampw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4318,9 +4162,9 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.7.0.tgz", - "integrity": "sha512-B6DmocHE6bCJt6Iy6z7p+ESjrp7WI4MJN2jWa2MBj9UEZ60Mj/q4BZ8qv0NSmcOYuJhjykNqCUmA+dAOnQn/Kw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.7.1.tgz", + "integrity": "sha512-I2vXgdA8mhIlAktIp7NovicalqKPaas9APH5wQxIzMK6jPjZmwS5x0MBW+sTsaFM4pnOf/Md9enoDnnR5CLq5A==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4335,9 +4179,9 @@ } }, "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.34.0.tgz", - "integrity": "sha512-Mug9Oing1nVQE8pYT33UKuPSEa/wjQTMk3feS9F84h4U7oZIx5Mz3yddj3OHOPgrW/7d1Ve/mG7jmYqBI9tpTg==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.35.0.tgz", + "integrity": "sha512-JYkyOUc7TZAyHy37N2aPAwFvRdET0+E5qIRjmQLPop9LQi4+N0sKf65g4xCwuY/0M721T/424G3zneJjxyiooA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -4353,12 +4197,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.0.tgz", - "integrity": "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", + "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -4369,14 +4213,14 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.200.0.tgz", - "integrity": "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.201.1.tgz", + "integrity": "sha512-Ug8gtpssUNUnfpotB9ZhnSsPSGDu+7LngTMgKl31mmVJwLAKyl6jC8diZrMcGkSgBh0o5dbg9puvLyR25buZfw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0" + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4386,13 +4230,13 @@ } }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.0.tgz", - "integrity": "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4402,32 +4246,32 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.200.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.200.0.tgz", - "integrity": "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A==", + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.201.1.tgz", + "integrity": "sha512-OdkYe6ZEFbPq+YXhebuiYpPECIBrrKgFJoAQVATllKlB5RDQDTE4J84/8LwGfQqSxBiSK2u1aSaFpzgBVoBrKA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.200.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", - "@opentelemetry/exporter-logs-otlp-http": "0.200.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", - "@opentelemetry/exporter-prometheus": "0.200.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", - "@opentelemetry/exporter-trace-otlp-http": "0.200.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", - "@opentelemetry/exporter-zipkin": "2.0.0", - "@opentelemetry/instrumentation": "0.200.0", - "@opentelemetry/propagator-b3": "2.0.0", - "@opentelemetry/propagator-jaeger": "2.0.0", - "@opentelemetry/resources": "2.0.0", - "@opentelemetry/sdk-logs": "0.200.0", - "@opentelemetry/sdk-metrics": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0", - "@opentelemetry/sdk-trace-node": "2.0.0", + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.201.1", + "@opentelemetry/exporter-logs-otlp-http": "0.201.1", + "@opentelemetry/exporter-logs-otlp-proto": "0.201.1", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.201.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.201.1", + "@opentelemetry/exporter-metrics-otlp-proto": "0.201.1", + "@opentelemetry/exporter-prometheus": "0.201.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.201.1", + "@opentelemetry/exporter-trace-otlp-http": "0.201.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.201.1", + "@opentelemetry/exporter-zipkin": "2.0.1", + "@opentelemetry/instrumentation": "0.201.1", + "@opentelemetry/propagator-b3": "2.0.1", + "@opentelemetry/propagator-jaeger": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -4438,13 +4282,13 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.0.tgz", - "integrity": "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.0.0", - "@opentelemetry/resources": "2.0.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -4455,14 +4299,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.0.tgz", - "integrity": "sha512-omdilCZozUjQwY3uZRBwbaRMJ3p09l4t187Lsdf0dGMye9WKD4NGcpgZRvqhI1dwcH6og+YXQEtoO9Wx3ykilg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", + "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.0.0", - "@opentelemetry/core": "2.0.0", - "@opentelemetry/sdk-trace-base": "2.0.0" + "@opentelemetry/context-async-hooks": "2.0.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4495,6 +4339,16 @@ "@opentelemetry/api": "^1.1.0" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@photostructure/tz-lookup": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-11.2.0.tgz", @@ -4616,12 +4470,12 @@ } }, "node_modules/@react-email/code-block": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.12.tgz", - "integrity": "sha512-Faw3Ij9+/Qwq6moWaeHnV8Hn7ekc/EqyAzPi6yUar21dhcqYugCC4Da1x4d9nA9zC0H9KU3lYVJczh8D3cA+Eg==", + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.13.tgz", + "integrity": "sha512-4DE4yPSgKEOnZMzcrDvRuD6mxsNxOex0hCYEG9F9q23geYgb2WCCeGBvIUXVzK69l703Dg4Vzrd5qUjl+JfcwA==", "license": "MIT", "dependencies": { - "prismjs": "1.30.0" + "prismjs": "^1.30.0" }, "engines": { "node": ">=18.0.0" @@ -4655,14 +4509,14 @@ } }, "node_modules/@react-email/components": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.36.tgz", - "integrity": "sha512-VMh+OQplAnG8JMLlJjdnjt+ThJZ+JVkp0q2YMS2NEz+T88N22bLD2p7DZO0QgtNaKgumOhJI/0a2Q7VzCrwu5g==", + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.41.tgz", + "integrity": "sha512-WUI3wHwra3QS0pwrovSU6b0I0f3TvY33ph0y44LuhSYDSQlMRyeOzgoT6HRDY5FXMDF57cHYq9WoKwpwP0yd7Q==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", "@react-email/button": "0.0.19", - "@react-email/code-block": "0.0.12", + "@react-email/code-block": "0.0.13", "@react-email/code-inline": "0.0.5", "@react-email/column": "0.0.13", "@react-email/container": "0.0.15", @@ -4673,13 +4527,13 @@ "@react-email/html": "0.0.11", "@react-email/img": "0.0.11", "@react-email/link": "0.0.12", - "@react-email/markdown": "0.0.14", - "@react-email/preview": "0.0.12", - "@react-email/render": "1.0.6", + "@react-email/markdown": "0.0.15", + "@react-email/preview": "0.0.13", + "@react-email/render": "1.1.2", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.0.4", - "@react-email/text": "0.1.1" + "@react-email/tailwind": "1.0.5", + "@react-email/text": "0.1.4" }, "engines": { "node": ">=18.0.0" @@ -4782,12 +4636,12 @@ } }, "node_modules/@react-email/markdown": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.14.tgz", - "integrity": "sha512-5IsobCyPkb4XwnQO8uFfGcNOxnsg3311GRXhJ3uKv51P7Jxme4ycC/MITnwIZ10w2zx7HIyTiqVzTj4XbuIHbg==", + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.15.tgz", + "integrity": "sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==", "license": "MIT", "dependencies": { - "md-to-react-email": "5.0.5" + "md-to-react-email": "^5.0.5" }, "engines": { "node": ">=18.0.0" @@ -4797,9 +4651,9 @@ } }, "node_modules/@react-email/preview": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.12.tgz", - "integrity": "sha512-g/H5fa9PQPDK6WUEG7iTlC19sAktI23qyoiJtMLqQiXFCfWeQMhqjLGKeLSKkfzszqmfJCjZtpSiKtBoOdxp3Q==", + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.13.tgz", + "integrity": "sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4809,14 +4663,14 @@ } }, "node_modules/@react-email/render": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.0.6.tgz", - "integrity": "sha512-zNueW5Wn/4jNC1c5LFgXzbUdv5Lhms+FWjOvWAhal7gx5YVf0q6dPJ0dnR70+ifo59gcMLwCZEaTS9EEuUhKvQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.2.tgz", + "integrity": "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==", "license": "MIT", "dependencies": { - "html-to-text": "9.0.5", - "prettier": "3.5.3", - "react-promise-suspense": "0.3.4" + "html-to-text": "^9.0.5", + "prettier": "^3.5.3", + "react-promise-suspense": "^0.3.4" }, "engines": { "node": ">=18.0.0" @@ -4851,9 +4705,9 @@ } }, "node_modules/@react-email/tailwind": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.0.4.tgz", - "integrity": "sha512-tJdcusncdqgvTUYZIuhNC6LYTfL9vNTSQpwWdTCQhQ1lsrNCEE4OKCSdzSV3S9F32pi0i0xQ+YPJHKIzGjdTSA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.0.5.tgz", + "integrity": "sha512-BH00cZSeFfP9HiDASl+sPHi7Hh77W5nzDgdnxtsVr/m3uQD9g180UwxcE3PhOfx0vRdLzQUU8PtmvvDfbztKQg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4863,9 +4717,9 @@ } }, "node_modules/@react-email/text": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.1.tgz", - "integrity": "sha512-Zo9tSEzkO3fODLVH1yVhzVCiwETfeEL5wU93jXKWo2DHoMuiZ9Iabaso3T0D0UjhrCB1PBMeq2YiejqeToTyIQ==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.4.tgz", + "integrity": "sha512-cMNE02y8172DocpNGh97uV5HSTawaS4CKG/zOku8Pu+m6ehBKbAjgtQZDIxhgstw8+TWraFB8ltS1DPjfG8nLA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4897,216 +4751,6 @@ } } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.40.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", @@ -5135,48 +4779,6 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@scarf/scarf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", @@ -5265,9 +4867,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.21.tgz", - "integrity": "sha512-/Y3BJLcwd40pExmdar8MH2UGGvCBrqNN7hauOMckrEX2Ivcbv3IMhrbGX4od1dnF880Ed8y/E9aStZCIQi0EGw==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.29.tgz", + "integrity": "sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5283,16 +4885,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.11.21", - "@swc/core-darwin-x64": "1.11.21", - "@swc/core-linux-arm-gnueabihf": "1.11.21", - "@swc/core-linux-arm64-gnu": "1.11.21", - "@swc/core-linux-arm64-musl": "1.11.21", - "@swc/core-linux-x64-gnu": "1.11.21", - "@swc/core-linux-x64-musl": "1.11.21", - "@swc/core-win32-arm64-msvc": "1.11.21", - "@swc/core-win32-ia32-msvc": "1.11.21", - "@swc/core-win32-x64-msvc": "1.11.21" + "@swc/core-darwin-arm64": "1.11.29", + "@swc/core-darwin-x64": "1.11.29", + "@swc/core-linux-arm-gnueabihf": "1.11.29", + "@swc/core-linux-arm64-gnu": "1.11.29", + "@swc/core-linux-arm64-musl": "1.11.29", + "@swc/core-linux-x64-gnu": "1.11.29", + "@swc/core-linux-x64-musl": "1.11.29", + "@swc/core-win32-arm64-msvc": "1.11.29", + "@swc/core-win32-ia32-msvc": "1.11.29", + "@swc/core-win32-x64-msvc": "1.11.29" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -5304,9 +4906,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.21.tgz", - "integrity": "sha512-v6gjw9YFWvKulCw3ZA1dY+LGMafYzJksm1mD4UZFZ9b36CyHFowYVYug1ajYRIRqEvvfIhHUNV660zTLoVFR8g==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.29.tgz", + "integrity": "sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==", "cpu": [ "arm64" ], @@ -5321,9 +4923,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.21.tgz", - "integrity": "sha512-CUiTiqKlzskwswrx9Ve5NhNoab30L1/ScOfQwr1duvNlFvarC8fvQSgdtpw2Zh3MfnfNPpyLZnYg7ah4kbT9JQ==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.29.tgz", + "integrity": "sha512-S3eTo/KYFk+76cWJRgX30hylN5XkSmjYtCBnM4jPLYn7L6zWYEPajsFLmruQEiTEDUg0gBEWLMNyUeghtswouw==", "cpu": [ "x64" ], @@ -5338,9 +4940,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.21.tgz", - "integrity": "sha512-YyBTAFM/QPqt1PscD8hDmCLnqPGKmUZpqeE25HXY8OLjl2MUs8+O4KjwPZZ+OGxpdTbwuWFyMoxjcLy80JODvg==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.29.tgz", + "integrity": "sha512-o9gdshbzkUMG6azldHdmKklcfrcMx+a23d/2qHQHPDLUPAN+Trd+sDQUYArK5Fcm7TlpG4sczz95ghN0DMkM7g==", "cpu": [ "arm" ], @@ -5355,9 +4957,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.21.tgz", - "integrity": "sha512-DQD+ooJmwpNsh4acrftdkuwl5LNxxg8U4+C/RJNDd7m5FP9Wo4c0URi5U0a9Vk/6sQNh9aSGcYChDpqCDWEcBw==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.29.tgz", + "integrity": "sha512-sLoaciOgUKQF1KX9T6hPGzvhOQaJn+3DHy4LOHeXhQqvBgr+7QcZ+hl4uixPKTzxk6hy6Hb0QOvQEdBAAR1gXw==", "cpu": [ "arm64" ], @@ -5372,9 +4974,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.21.tgz", - "integrity": "sha512-y1L49+snt1a1gLTYPY641slqy55QotPdtRK9Y6jMi4JBQyZwxC8swWYlQWb+MyILwxA614fi62SCNZNznB3XSA==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.29.tgz", + "integrity": "sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==", "cpu": [ "arm64" ], @@ -5389,9 +4991,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.21.tgz", - "integrity": "sha512-NesdBXv4CvVEaFUlqKj+GA4jJMNUzK2NtKOrUNEtTbXaVyNiXjFCSaDajMTedEB0jTAd9ybB0aBvwhgkJUWkWA==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.29.tgz", + "integrity": "sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==", "cpu": [ "x64" ], @@ -5406,9 +5008,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.21.tgz", - "integrity": "sha512-qFV60pwpKVOdmX67wqQzgtSrUGWX9Cibnp1CXyqZ9Mmt8UyYGvmGu7p6PMbTyX7vdpVUvWVRf8DzrW2//wmVHg==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.29.tgz", + "integrity": "sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==", "cpu": [ "x64" ], @@ -5423,9 +5025,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.21.tgz", - "integrity": "sha512-DJJe9k6gXR/15ZZVLv1SKhXkFst8lYCeZRNHH99SlBodvu4slhh/MKQ6YCixINRhCwliHrpXPym8/5fOq8b7Ig==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.29.tgz", + "integrity": "sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==", "cpu": [ "arm64" ], @@ -5440,9 +5042,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.21.tgz", - "integrity": "sha512-TqEXuy6wedId7bMwLIr9byds+mKsaXVHctTN88R1UIBPwJA92Pdk0uxDgip0pEFzHB/ugU27g6d8cwUH3h2eIw==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.29.tgz", + "integrity": "sha512-h+NjOrbqdRBYr5ItmStmQt6x3tnhqgwbj9YxdGPepbTDamFv7vFnhZR0YfB3jz3UKJ8H3uGJ65Zw1VsC+xpFkg==", "cpu": [ "ia32" ], @@ -5457,9 +5059,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.21.tgz", - "integrity": "sha512-BT9BNNbMxdpUM1PPAkYtviaV0A8QcXttjs2MDtOeSqqvSJaPtyM+Fof2/+xSwQDmDEFzbGCcn75M5+xy3lGqpA==", + "version": "1.11.29", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.29.tgz", + "integrity": "sha512-Q8cs2BDV9wqDvqobkXOYdC+pLUSEpX/KvI0Dgfun1F+LzuLotRFuDhrvkU9ETJA6OnD2+Fn/ieHgloiKA/Mn/g==", "cpu": [ "x64" ], @@ -5502,25 +5104,49 @@ } }, "node_modules/@testcontainers/postgresql": { - "version": "10.24.2", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.24.2.tgz", - "integrity": "sha512-s4k/QVSmpXFzxNBeft84x172Hx5tNiGeDRWwU8iOAHjtcnyXd4aBORF0GruNojTo7TOIuAM0ArBZUMqRBLJKwQ==", + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.28.0.tgz", + "integrity": "sha512-NN25rruG5D4Q7pCNIJuHwB+G85OSeJ3xHZ2fWx0O6sPoPEfCYwvpj8mq99cyn68nxFkFYZeyrZJtSFO+FnydiA==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^10.24.2" + "testcontainers": "^10.28.0" } }, "node_modules/@testcontainers/redis": { - "version": "10.24.2", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-10.24.2.tgz", - "integrity": "sha512-m4/FZW5ltZPaK9pQTKNipjpBk73Vdj7Ql3sFr26A9dOr0wJyM3Wnc9jeHTNRal7RDnY5rvumXAIUWbBlvKMJEw==", + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-10.28.0.tgz", + "integrity": "sha512-xDNKSJTBmQca/3v5sdHmqSCYr68vjvAGSxoHCuWylha77gAYn88g5nUZK0ocNbUZgBq69KhIzj/f9zlHkw34uA==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^10.24.2" + "testcontainers": "^10.28.0" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, "node_modules/@turf/boolean-point-in-polygon": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.2.0.tgz", @@ -5617,6 +5243,17 @@ "@types/node": "*" } }, + "node_modules/@types/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-g4vmPIwbTii9dX1HVioHbOolubEaf4re4vDxuzpKrzz9uI7uarBExi9begX0cXyIB85jXZ5X2A/v8rsHZxSAPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/node": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -5705,9 +5342,9 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", + "integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==", "dev": true, "license": "MIT", "dependencies": { @@ -5779,9 +5416,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", - "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", + "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", "dev": true, "license": "MIT" }, @@ -5844,9 +5481,9 @@ } }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -5879,6 +5516,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/pg": { "version": "8.6.1", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", @@ -5900,9 +5546,9 @@ } }, "node_modules/@types/picomatch": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.2.tgz", - "integrity": "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", + "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", "dev": true, "license": "MIT" }, @@ -5931,9 +5577,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", - "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz", + "integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==", "dev": true, "license": "MIT", "dependencies": { @@ -5951,9 +5597,9 @@ } }, "node_modules/@types/sanitize-html": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.15.0.tgz", - "integrity": "sha512-71Z6PbYsVKfp4i6Jvr37s5ql6if1Q/iJQT80NbaSi7uGaG8CqBMXP0pk/EsURAOuGdk5IJCd/vnzKrR7S3Txsw==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.16.0.tgz", + "integrity": "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==", "dev": true, "license": "MIT", "dependencies": { @@ -6090,21 +5736,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6119,17 +5765,27 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -6145,14 +5801,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6163,16 +5819,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6187,9 +5843,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "license": "MIT", "engines": { @@ -6201,20 +5857,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6254,16 +5910,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6278,13 +5934,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6296,9 +5952,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz", + "integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==", "dev": true, "license": "MIT", "dependencies": { @@ -6311,7 +5967,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -6319,8 +5975,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.4", + "vitest": "3.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -6329,14 +5985,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", + "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -6345,13 +6001,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", + "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -6382,9 +6038,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", + "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", "dev": true, "license": "MIT", "dependencies": { @@ -6395,13 +6051,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", + "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.4", "pathe": "^2.0.3" }, "funding": { @@ -6409,13 +6065,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", + "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -6424,9 +6080,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", + "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -6437,13 +6093,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -6970,12 +6626,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/archiver-utils/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7455,63 +7105,20 @@ } }, "node_modules/bullmq": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-4.18.2.tgz", - "integrity": "sha512-Cx0O98IlGiFw7UBa+zwGz+nH0Pcl1wfTvMVBlsMna3s0219hXroVovh1xPRgomyUcbyciHiugGCkW0RRNZDHYQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.53.0.tgz", + "integrity": "sha512-AbzcwR+9GdgrenolOC9kApF+TkUKZpUCMiFbXgRYw9ivWhOfLCqKeajIptM7NdwhY7cpXgv+QpbweUuQZUxkyA==", "license": "MIT", "dependencies": { - "cron-parser": "^4.6.0", - "glob": "^8.0.3", - "ioredis": "^5.3.2", - "lodash": "^4.17.21", - "msgpackr": "^1.6.2", + "cron-parser": "^4.9.0", + "ioredis": "^5.4.1", + "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0" } }, - "node_modules/bullmq/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/bullmq/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/bullmq/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -7552,6 +7159,190 @@ "node": ">=8" } }, + "node_modules/cacache": { + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", + "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -7760,13 +7551,13 @@ "license": "MIT" }, "node_modules/class-validator": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", - "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", "license": "MIT", "dependencies": { "@types/validator": "^13.11.8", - "libphonenumber-js": "^1.10.53", + "libphonenumber-js": "^1.11.1", "validator": "^13.9.0" } }, @@ -8033,6 +7824,60 @@ "node": ">= 14" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -8311,6 +8156,20 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", + "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.2", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -8318,6 +8177,57 @@ "dev": true, "license": "MIT" }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -8353,6 +8263,27 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -8463,9 +8394,9 @@ "license": "MIT" }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -8752,6 +8683,16 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -8886,6 +8827,23 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -8914,9 +8872,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -9014,20 +8972,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -9075,22 +9033,25 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz", - "integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz", + "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==", "dev": true, "license": "MIT", "dependencies": { @@ -9343,16 +9304,6 @@ "exiftool-vendored.pl": "13.0.1" } }, - "node_modules/exiftool-vendored.exe": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-13.0.0.tgz", - "integrity": "sha512-4zAMuFGgxZkOoyQIzZMHv1HlvgyJK3AkNqjAgm8A8V0UmOZO7yv3pH49cDV1OduzFJqgs6yQ6eG4OGydhKtxlg==", - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/exiftool-vendored.pl": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-13.0.1.tgz", @@ -9373,6 +9324,13 @@ "node": ">=12.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -9547,6 +9505,27 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -9593,6 +9572,24 @@ "stream-source": "0.3" } }, + "node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -9824,16 +9821,19 @@ } }, "node_modules/formidable": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", - "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^2.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -9921,20 +9921,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -10216,9 +10202,9 @@ } }, "node_modules/globals": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", - "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "dev": true, "license": "MIT", "engines": { @@ -10376,16 +10362,6 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", - "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hosted-git-info": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", @@ -10399,12 +10375,18 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/html-escaper": { "version": "2.0.2", @@ -10448,6 +10430,13 @@ "entities": "^4.4.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -10464,6 +10453,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -10698,6 +10711,20 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -10829,6 +10856,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -11055,9 +11089,9 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz", + "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -11081,6 +11115,136 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -11212,9 +11376,9 @@ } }, "node_modules/kysely": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.0.tgz", - "integrity": "sha512-hq8VcLy57Ww7oPTTVEOrT9ml+g8ehbbmEUkHmW4Xtubu+NHdKZi6SH6egmD4cjDhn3b/0s0h/6AjdPayOTJhNw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.2.tgz", + "integrity": "sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -11326,6 +11490,25 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/load-esm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", + "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -11423,16 +11606,10 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/luxon": { "version": "3.6.1", @@ -11489,6 +11666,29 @@ "semver": "bin/semver.js" } }, + "node_modules/make-fetch-happen": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", + "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/marked": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", @@ -11690,6 +11890,128 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", + "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -12055,9 +12377,9 @@ } }, "node_modules/nestjs-cls": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.4.2.tgz", - "integrity": "sha512-KQPOhD7ya82gSEc+XDwFKERPMaWK95bzV4E2pLmx8oC1hfMNuVc4dkWmEKJiu+o0hCWP/v51iWNgOGHKnJ9Raw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-5.4.3.tgz", + "integrity": "sha512-yHEHyVoe6rsvj3XRPFonBKPXPjDREyHfKZ9PTStSLJTZAV3wey1Q89TquSj6QciqXB5387GiHv9DG+ja6iAUHw==", "license": "MIT", "engines": { "node": ">=18" @@ -12205,6 +12527,31 @@ } } }, + "node_modules/node-gyp": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz", + "integrity": "sha512-T0S1zqskVUSxcsSTkAsLc7xCycrRYmtDHadDinzocrThjyQCn5kMlEBSj6H4qDbgsIOSLmmlRIeb0lZXj+UArA==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", @@ -12220,6 +12567,125 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -12293,6 +12759,22 @@ "set-blocking": "^2.0.0" } }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/oauth4webapi": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.1.tgz", + "integrity": "sha512-txg/jZQwcbaF7PMJgY7aoxc9QuCxHVFMiEkDIJ60DwDz3PbtXPQnrzo+3X4IRYGChIwWLabRBRpf1k9hO9+xrQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12333,15 +12815,6 @@ "node": ">= 0.4" } }, - "node_modules/oidc-token-hash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", - "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -12405,29 +12878,18 @@ } }, "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.5.0.tgz", + "integrity": "sha512-fAfYaTnOYE2kQCqEJGX9KDObW2aw7IQy4jWpU/+3D3WoCFLbix5Hg6qIPQ6Js9r7f8jDUmsnnguRNCSw4wU/IQ==", "license": "MIT", "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" + "jose": "^6.0.10", + "oauth4webapi": "^3.5.1" }, "funding": { "url": "https://github.com/sponsors/panva" } }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -12531,6 +12993,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -12573,6 +13048,32 @@ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -12822,23 +13323,36 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/peek-readable": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", + "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/pg": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", - "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.8.0", - "pg-protocol": "^1.8.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "pg-connection-string": "^2.9.0", + "pg-pool": "^3.10.0", + "pg-protocol": "^1.10.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" }, "engines": { "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.1" + "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -12850,16 +13364,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", + "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", "license": "MIT" }, "node_modules/pg-int8": { @@ -12872,18 +13386,18 @@ } }, "node_modules/pg-pool": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", - "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", + "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", - "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", + "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", "license": "MIT" }, "node_modules/pg-types": { @@ -13244,6 +13758,16 @@ "node": ">=6" } }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -13259,6 +13783,20 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -13596,12 +14134,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/react-email/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/react-email/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -14156,6 +14688,13 @@ "node": ">= 18" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -14233,9 +14772,9 @@ } }, "node_modules/sanitize-html": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.15.0.tgz", - "integrity": "sha512-wIjst57vJGpLyBP8ioUbg6ThwJie5SuSIjHxJg53v5Fg+kUK+AXlb7bK3RNXpp315MvwM+0OBGCV6h5pPHsVhA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", + "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", @@ -14246,6 +14785,19 @@ "postcss": "^8.3.11" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -14284,9 +14836,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14410,15 +14962,15 @@ "license": "MIT" }, "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" + "detect-libc": "^2.0.4", + "semver": "^7.7.2" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -14427,25 +14979,27 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" + "@img/sharp-darwin-arm64": "0.34.2", + "@img/sharp-darwin-x64": "0.34.2", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.2", + "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-s390x": "0.34.2", + "@img/sharp-linux-x64": "0.34.2", + "@img/sharp-linuxmusl-arm64": "0.34.2", + "@img/sharp-linuxmusl-x64": "0.34.2", + "@img/sharp-wasm32": "0.34.2", + "@img/sharp-win32-arm64": "0.34.2", + "@img/sharp-win32-ia32": "0.34.2", + "@img/sharp-win32-x64": "0.34.2" } }, "node_modules/shebang-command": { @@ -14611,6 +15165,17 @@ "integrity": "sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg==", "license": "BSD-3-Clause" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -14746,6 +15311,46 @@ "node": ">= 0.6" } }, + "node_modules/socks": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14827,10 +15432,17 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/sql-formatter": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.5.2.tgz", - "integrity": "sha512-+9xZgiv1DP/c7GxkkBUHRZOf4j35gquVdwEm0rg16qKRYeFkv1+/vEeO13fsUbbz06KUotIyASJ+hyau8LM8Kg==", + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.2.tgz", + "integrity": "sha512-ZjqOfJGuB97UeHzTJoTbadlM0h9ynehtSTHNUbGfXR4HZ4rCIoD2oIW91W+A5oE76k8hl0Uz5GD8Sx3Pt9Xa3w==", "dev": true, "license": "MIT", "dependencies": { @@ -14897,6 +15509,19 @@ "nan": "^2.20.0" } }, + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -15109,6 +15734,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", + "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -15202,13 +15844,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/sucrase/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "peer": true - }, "node_modules/sucrase/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -15243,9 +15878,9 @@ } }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", "dev": true, "license": "MIT", "dependencies": { @@ -15254,7 +15889,7 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -15264,14 +15899,14 @@ } }, "node_modules/supertest": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", - "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.1" }, "engines": { "node": ">=14.18.0" @@ -15320,6 +15955,13 @@ "node": ">=0.10" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", @@ -15752,13 +16394,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/test-exclude/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/test-exclude/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -15793,9 +16428,9 @@ } }, "node_modules/testcontainers": { - "version": "10.24.2", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.24.2.tgz", - "integrity": "sha512-Don3EXEQuSw14+nFG9pj48fL9ck/jXDfR9Rb0K3acOyn/gg97+gsnfZaLzpdejl9GcPJVKxACNRe3SYVC2uWqg==", + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", "dev": true, "license": "MIT", "dependencies": { @@ -15813,7 +16448,7 @@ "ssh-remote-port-forward": "^1.0.4", "tar-fs": "^3.0.7", "tmp": "^0.2.3", - "undici": "^5.28.5" + "undici": "^5.29.0" } }, "node_modules/testcontainers/node_modules/tmp": { @@ -15891,6 +16526,23 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -15921,6 +16573,26 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -15954,6 +16626,23 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -15963,6 +16652,19 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -16119,9 +16821,9 @@ "license": "MIT" }, "node_modules/typeorm": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.22.tgz", - "integrity": "sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.24.tgz", + "integrity": "sha512-4IrHG7A0tY8l5gEGXfW56VOMfUVWEkWlH/h5wmcyZ+V8oCiLj7iTPp0lEjMEZVrxEkGSdP9ErgTKHKXQApl/oA==", "license": "MIT", "dependencies": { "@sqltools/formatter": "^1.2.5", @@ -16130,6 +16832,7 @@ "buffer": "^6.0.3", "dayjs": "^1.11.13", "debug": "^4.4.0", + "dedent": "^1.6.0", "dotenv": "^16.4.7", "glob": "^10.4.5", "sha.js": "^2.4.11", @@ -16291,12 +16994,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/typeorm/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/typeorm/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -16356,15 +17053,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -16465,6 +17162,18 @@ "node": ">= 4.0.0" } }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", @@ -16497,6 +17206,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unique-filename": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/unique-slug": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -16517,29 +17252,30 @@ } }, "node_modules/unplugin": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", - "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.4.tgz", + "integrity": "sha512-m4PjxTurwpWfpMomp8AptjD5yj8qEZN5uQjjGM3TAs9MWWD2tXSSNNj6jGR2FoVGod4293ytyV6SwBbertfyJg==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.14.1", + "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.12.0" } }, "node_modules/unplugin-swc": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.1.tgz", - "integrity": "sha512-/ZLrPNjChhGx3Z95pxJ4tQgfI6rWqukgYHKflrNB4zAV1izOQuDhkTn55JWeivpBxDCoK7M/TStb2aS/14PS/g==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.3.tgz", + "integrity": "sha512-lfBT7Wtauf/1y89xGt+x8+T7yB7bCMq/qXeXcOcqQddKDULGEg/4O2201Eh6eCBxbEi8J1Tmy2scG5dhiBJONg==", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.0", + "@rollup/pluginutils": "^5.1.4", "load-tsconfig": "^0.2.5", - "unplugin": "^1.11.0" + "unplugin": "^2.3.4" }, "peerDependencies": { "@swc/core": "^1.2.108" @@ -16734,15 +17470,15 @@ } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", + "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", + "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, @@ -16776,278 +17512,6 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/@esbuild/linux-x64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", @@ -17065,125 +17529,6 @@ "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -17255,31 +17600,32 @@ } }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", + "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.4", + "@vitest/mocker": "3.1.4", + "@vitest/pretty-format": "^3.1.4", + "@vitest/runner": "3.1.4", + "@vitest/snapshot": "3.1.4", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -17295,8 +17641,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.4", + "@vitest/ui": "3.1.4", "happy-dom": "*", "jsdom": "*" }, @@ -17324,6 +17670,19 @@ } } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -17354,9 +17713,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "version": "5.99.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", + "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17549,6 +17908,29 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -17717,6 +18099,23 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index 76415da7c8..1aeafbf7cf 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.131.3", + "version": "1.134.0", "description": "", "author": "", "private": true, @@ -23,12 +23,12 @@ "test:medium": "vitest --config test/vitest.config.medium.mjs", "typeorm": "typeorm", "lifecycle": "node ./dist/utils/lifecycle.js", + "migrations:debug": "node ./dist/bin/migrations.js debug", "migrations:generate": "node ./dist/bin/migrations.js generate", "migrations:create": "node ./dist/bin/migrations.js create", "migrations:run": "node ./dist/bin/migrations.js run", - "typeorm:migrations:revert": "typeorm migration:revert -d ./dist/bin/database.js", - "typeorm:schema:drop": "typeorm query -d ./dist/bin/database.js 'DROP schema public cascade; CREATE schema public;'", - "typeorm:schema:reset": "npm run typeorm:schema:drop && npm run migrations:run", + "schema:drop": "node ./dist/bin/migrations.js query 'DROP schema public cascade; CREATE schema public;'", + "schema:reset": "npm run schema:drop && npm run migrations:run", "kysely:codegen": "npx kysely-codegen --include-pattern=\"(public|vectors).*\" --dialect postgres --url postgres://postgres:postgres@localhost/immich --log-level debug --out-file=./src/db.d.ts", "sync:open-api": "node ./dist/bin/sync-open-api.js", "sync:sql": "node ./dist/bin/sync-sql.js", @@ -45,19 +45,20 @@ "@nestjs/schedule": "^5.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", - "@opentelemetry/auto-instrumentations-node": "^0.57.0", + "@opentelemetry/auto-instrumentations-node": "^0.59.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.200.0", - "@opentelemetry/sdk-node": "^0.200.0", - "@react-email/components": "^0.0.36", + "@opentelemetry/exporter-prometheus": "^0.201.0", + "@opentelemetry/sdk-node": "^0.201.0", + "@react-email/components": "^0.0.41", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^5.1.1", - "bullmq": "^4.8.0", + "bullmq": "^5.51.0", "chokidar": "^3.5.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", "exiftool-vendored": "^28.3.1", @@ -78,7 +79,7 @@ "nestjs-kysely": "^1.1.0", "nestjs-otel": "^6.0.0", "nodemailer": "^6.9.13", - "openid-client": "^5.4.3", + "openid-client": "^6.3.3", "pg": "^8.11.3", "picomatch": "^4.0.2", "react": "^19.0.0", @@ -89,7 +90,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.33.0", + "sharp": "^0.34.2", "sirv": "^3.0.0", "tailwindcss-preset-email": "^1.3.2", "thumbhash": "^0.1.1", @@ -109,6 +110,7 @@ "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", "@types/bcrypt": "^5.0.0", + "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", @@ -116,9 +118,9 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "@types/nodemailer": "^6.4.14", - "@types/picomatch": "^3.0.0", + "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", "@types/react": "^19.0.0", "@types/sanitize-html": "^2.13.0", @@ -131,8 +133,10 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^57.0.0", "globals": "^16.0.0", + "jsdom": "^26.1.0", "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.0", + "node-addon-api": "^8.3.1", + "node-gyp": "^11.2.0", "patch-package": "^8.0.0", "pngjs": "^7.0.0", "prettier": "^3.0.2", @@ -151,6 +155,9 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" + }, + "overrides": { + "sharp": "^0.34.2" } } diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 5720f7af0b..153b525fe5 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -17,12 +17,12 @@ import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { repositories } from 'src/repositories'; import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; -import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; import { services } from 'src/services'; import { AuthService } from 'src/services/auth.service'; import { CliService } from 'src/services/cli.service'; +import { JobService } from 'src/services/job.service'; import { getKyselyConfig } from 'src/utils/database'; const common = [...repositories, ...services, GlobalExceptionFilter]; @@ -44,7 +44,7 @@ const imports = [ BullModule.registerQueue(...bull.queues), ClsModule.forRoot(cls.config), OpenTelemetryModule.forRoot(otel), - KyselyModule.forRoot(getKyselyConfig(database.config.kysely)), + KyselyModule.forRoot(getKyselyConfig(database.config)), ]; class BaseModule implements OnModuleInit, OnModuleDestroy { @@ -52,7 +52,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { @Inject(IWorker) private worker: ImmichWorker, logger: LoggingRepository, private eventRepository: EventRepository, - private jobRepository: JobRepository, + private jobService: JobService, private telemetryRepository: TelemetryRepository, private authService: AuthService, ) { @@ -62,10 +62,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { this.telemetryRepository.setup({ repositories }); - this.jobRepository.setup({ services }); - if (this.worker === ImmichWorker.MICROSERVICES) { - this.jobRepository.startWorkers(); - } + this.jobService.setServices(services); this.eventRepository.setAuthFn(async (client) => this.authService.authenticate({ diff --git a/server/src/bin/database.ts b/server/src/bin/database.ts deleted file mode 100644 index 7ea56e0fc0..0000000000 --- a/server/src/bin/database.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ConfigRepository } from 'src/repositories/config.repository'; -import { DataSource } from 'typeorm'; - -const { database } = new ConfigRepository().getEnv(); - -/** - * @deprecated - DO NOT USE THIS - * - * this export is ONLY to be used for TypeORM commands in package.json#scripts - */ -export const dataSource = new DataSource({ ...database.config.typeorm, host: 'localhost' }); diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index fb50323dff..b3329e6331 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node process.env.DB_URL = process.env.DB_URL || 'postgres://postgres:postgres@localhost:5432/immich'; -import { Kysely } from 'kysely'; -import { writeFileSync } from 'node:fs'; +import { Kysely, sql } from 'kysely'; +import { mkdirSync, writeFileSync } from 'node:fs'; import { basename, dirname, extname, join } from 'node:path'; import postgres from 'postgres'; import { ConfigRepository } from 'src/repositories/config.repository'; @@ -10,7 +10,7 @@ import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import 'src/schema'; import { schemaDiff, schemaFromCode, schemaFromDatabase } from 'src/sql-tools'; -import { getKyselyConfig } from 'src/utils/database'; +import { asPostgresConnectionConfig, getKyselyConfig } from 'src/utils/database'; const main = async () => { const command = process.argv[2]; @@ -23,8 +23,13 @@ const main = async () => { } case 'run': { - const only = process.argv[3] as 'kysely' | 'typeorm' | undefined; - await run(only); + await runMigrations(); + return; + } + + case 'query': { + const query = process.argv[3]; + await runQuery(query); return; } @@ -48,14 +53,25 @@ const main = async () => { } }; -const run = async (only?: 'kysely' | 'typeorm') => { +const getDatabaseClient = () => { const configRepository = new ConfigRepository(); const { database } = configRepository.getEnv(); - const logger = new LoggingRepository(undefined, configRepository); - const db = new Kysely(getKyselyConfig(database.config.kysely)); - const databaseRepository = new DatabaseRepository(db, logger, configRepository); + return new Kysely(getKyselyConfig(database.config)); +}; - await databaseRepository.runMigrations({ only }); +const runQuery = async (query: string) => { + const db = getDatabaseClient(); + await sql.raw(query).execute(db); + await db.destroy(); +}; + +const runMigrations = async () => { + const configRepository = new ConfigRepository(); + const logger = LoggingRepository.create(); + const db = getDatabaseClient(); + const databaseRepository = new DatabaseRepository(db, logger, configRepository); + await databaseRepository.runMigrations(); + await db.destroy(); }; const debug = async () => { @@ -81,14 +97,15 @@ const create = (path: string, up: string[], down: string[]) => { const filename = `${timestamp}-${name}.ts`; const folder = dirname(path); const fullPath = join(folder, filename); - writeFileSync(fullPath, asMigration('typeorm', { name, timestamp, up, down })); + mkdirSync(folder, { recursive: true }); + writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down })); console.log(`Wrote ${fullPath}`); }; const compare = async () => { const configRepository = new ConfigRepository(); const { database } = configRepository.getEnv(); - const db = postgres(database.config.kysely); + const db = postgres(asPostgresConnectionConfig(database.config)); const source = schemaFromCode(); const target = await schemaFromDatabase(db, {}); @@ -108,6 +125,7 @@ const compare = async () => { const down = schemaDiff(target, source, { tables: { ignoreExtra: false }, functions: { ignoreExtra: false }, + extension: { ignoreMissing: true }, }); return { up, down }; diff --git a/server/src/bin/sync-sql.ts b/server/src/bin/sync-sql.ts index 47e6610a74..a114830e09 100644 --- a/server/src/bin/sync-sql.ts +++ b/server/src/bin/sync-sql.ts @@ -72,13 +72,15 @@ class SqlGenerator { await rm(this.options.targetDir, { force: true, recursive: true }); await mkdir(this.options.targetDir); - process.env.DB_HOSTNAME = 'localhost'; + if (!process.env.DB_HOSTNAME) { + process.env.DB_HOSTNAME = 'localhost'; + } const { database, cls, otel } = new ConfigRepository().getEnv(); const moduleFixture = await Test.createTestingModule({ imports: [ KyselyModule.forRoot({ - ...getKyselyConfig(database.config.kysely), + ...getKyselyConfig(database.config), log: (event) => { if (event.level === 'query') { this.sqlLogger.logQuery(event.query.sql); diff --git a/server/src/config.ts b/server/src/config.ts index 566adbd693..a9fdffbd62 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -5,6 +5,7 @@ import { CQMode, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -96,6 +97,8 @@ export interface SystemConfig { scope: string; signingAlgorithm: string; profileSigningAlgorithm: string; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; + timeout: number; storageLabelClaim: string; storageQuotaClaim: string; }; @@ -260,6 +263,8 @@ export const defaults = Object.freeze({ profileSigningAlgorithm: 'none', storageLabelClaim: 'preferred_username', storageQuotaClaim: 'immich_quota', + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST, + timeout: 30_000, }, passwordLogin: { enabled: true, diff --git a/server/src/constants.ts b/server/src/constants.ts index 6c0319fcee..2e25797938 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -1,9 +1,10 @@ import { Duration } from 'luxon'; import { readFileSync } from 'node:fs'; import { SemVer } from 'semver'; -import { DatabaseExtension, ExifOrientation } from 'src/enum'; +import { DatabaseExtension, ExifOrientation, VectorIndex } from 'src/enum'; export const POSTGRES_VERSION_RANGE = '>=14.0.0'; +export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.5'; export const VECTORS_VERSION_RANGE = '>=0.2 <0.4'; export const VECTOR_VERSION_RANGE = '>=0.5 <1'; @@ -20,8 +21,22 @@ export const EXTENSION_NAMES: Record = { earthdistance: 'earthdistance', vector: 'pgvector', vectors: 'pgvecto.rs', + vchord: 'VectorChord', } as const; +export const VECTOR_EXTENSIONS = [ + DatabaseExtension.VECTORCHORD, + DatabaseExtension.VECTORS, + DatabaseExtension.VECTOR, +] as const; + +export const VECTOR_INDEX_TABLES = { + [VectorIndex.CLIP]: 'smart_search', + [VectorIndex.FACE]: 'face_search', +} as const; + +export const VECTORCHORD_LIST_SLACK_FACTOR = 1.2; + export const SALT_ROUNDS = 10; export const IWorker = 'IWorker'; diff --git a/server/src/controllers/activity.controller.spec.ts b/server/src/controllers/activity.controller.spec.ts new file mode 100644 index 0000000000..bf2038048f --- /dev/null +++ b/server/src/controllers/activity.controller.spec.ts @@ -0,0 +1,81 @@ +import { ActivityController } from 'src/controllers/activity.controller'; +import { ActivityService } from 'src/services/activity.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(ActivityController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(ActivityService); + + beforeAll(async () => { + ctx = await controllerSetup(ActivityController, [{ provide: ActivityService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /activities', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/activities'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require an albumId', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/activities'); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + }); + + it('should reject an invalid albumId', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/activities').query({ albumId: '123' }); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + }); + + it('should reject an invalid assetId', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .get('/activities') + .query({ albumId: factory.uuid(), assetId: '123' }); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['assetId must be a UUID']))); + }); + }); + + describe('POST /activities', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/activities'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require an albumId', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/activities').send({ albumId: '123' }); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['albumId must be a UUID']))); + }); + + it('should require a comment when type is comment', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/activities') + .send({ albumId: factory.uuid(), type: 'comment', comment: null }); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(['comment must be a string', 'comment should not be empty'])); + }); + }); + + describe('DELETE /activities/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/activities/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/activities/123`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + }); +}); diff --git a/server/src/controllers/album.controller.spec.ts b/server/src/controllers/album.controller.spec.ts new file mode 100644 index 0000000000..9b8a19c129 --- /dev/null +++ b/server/src/controllers/album.controller.spec.ts @@ -0,0 +1,88 @@ +import { AlbumController } from 'src/controllers/album.controller'; +import { AlbumService } from 'src/services/album.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(AlbumController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(AlbumService); + + beforeAll(async () => { + ctx = await controllerSetup(AlbumController, [{ provide: AlbumService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /albums', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/albums'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should reject an invalid shared param', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/albums?shared=invalid'); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(['shared must be a boolean value'])); + }); + + it('should reject an invalid assetId param', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/albums?assetId=invalid'); + expect(status).toEqual(400); + expect(body).toEqual(factory.responses.badRequest(['assetId must be a UUID'])); + }); + }); + + describe('GET /albums/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/albums/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /albums/statistics', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/albums/statistics'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /albums', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/albums').send({ albumName: 'New album' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /albums/:id/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/albums/${factory.uuid()}/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PATCH /albums/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).patch(`/albums/${factory.uuid()}`).send({ albumName: 'New album name' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('DELETE /albums/:id/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/albums/${factory.uuid()}/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT :id/users', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/albums/${factory.uuid()}/users`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/api-key.controller.spec.ts b/server/src/controllers/api-key.controller.spec.ts new file mode 100644 index 0000000000..434fa2b7aa --- /dev/null +++ b/server/src/controllers/api-key.controller.spec.ts @@ -0,0 +1,76 @@ +import { APIKeyController } from 'src/controllers/api-key.controller'; +import { Permission } from 'src/enum'; +import { ApiKeyService } from 'src/services/api-key.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(APIKeyController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(ApiKeyService); + + beforeAll(async () => { + ctx = await controllerSetup(APIKeyController, [{ provide: ApiKeyService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('POST /api-keys', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/api-keys').send({ name: 'API Key' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /api-keys', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/api-keys'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /api-keys/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/api-keys/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/api-keys/123`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + }); + + describe('PUT /api-keys/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/api-keys/${factory.uuid()}`).send({ name: 'new name' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/api-keys/123`) + .send({ name: 'new name', permissions: [Permission.ALL] }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + }); + + describe('DELETE /api-keys/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/api-keys/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/api-keys/123`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + }); +}); diff --git a/server/src/controllers/app.controller.spec.ts b/server/src/controllers/app.controller.spec.ts new file mode 100644 index 0000000000..4cc00e78c5 --- /dev/null +++ b/server/src/controllers/app.controller.spec.ts @@ -0,0 +1,49 @@ +import { AppController } from 'src/controllers/app.controller'; +import { SystemConfigService } from 'src/services/system-config.service'; +import request from 'supertest'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(AppController.name, () => { + let ctx: ControllerContext; + + beforeAll(async () => { + ctx = await controllerSetup(AppController, [ + { provide: SystemConfigService, useValue: mockBaseService(SystemConfigService) }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + ctx.reset(); + }); + + describe('GET /.well-known/immich', () => { + it('should not be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/.well-known/immich'); + expect(ctx.authenticate).not.toHaveBeenCalled(); + }); + + it('should return a 200 status code', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/.well-known/immich'); + expect(status).toBe(200); + expect(body).toEqual({ + api: { + endpoint: '/api', + }, + }); + }); + }); + + describe('GET /custom.css', () => { + it('should not be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/custom.css'); + expect(ctx.authenticate).not.toHaveBeenCalled(); + }); + + it('should reply with text/css', async () => { + const { status, headers } = await request(ctx.getHttpServer()).get('/custom.css'); + expect(status).toBe(200); + expect(headers['content-type']).toEqual('text/css; charset=utf-8'); + }); + }); +}); diff --git a/server/src/controllers/asset-media.controller.spec.ts b/server/src/controllers/asset-media.controller.spec.ts new file mode 100644 index 0000000000..67bdeff222 --- /dev/null +++ b/server/src/controllers/asset-media.controller.spec.ts @@ -0,0 +1,128 @@ +import { AssetMediaController } from 'src/controllers/asset-media.controller'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { AssetMediaService } from 'src/services/asset-media.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +const makeUploadDto = (options?: { omit: string }): Record => { + const dto: Record = { + deviceAssetId: 'example-image', + deviceId: 'TEST', + fileCreatedAt: new Date().toISOString(), + fileModifiedAt: new Date().toISOString(), + isFavorite: 'testing', + duration: '0:00:00.000000', + }; + + const omit = options?.omit; + if (omit) { + delete dto[omit]; + } + + return dto; +}; + +describe(AssetMediaController.name, () => { + let ctx: ControllerContext; + const assetData = Buffer.from('123'); + const filename = 'example.png'; + + beforeAll(async () => { + ctx = await controllerSetup(AssetMediaController, [ + { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, + { provide: AssetMediaService, useValue: mockBaseService(AssetMediaService) }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + ctx.reset(); + }); + + describe('POST /assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post(`/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require `deviceAssetId`', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto({ omit: 'deviceAssetId' }) }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should require `deviceId`', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto({ omit: 'deviceId' }) }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should require `fileCreatedAt`', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto({ omit: 'fileCreatedAt' }) }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should require `fileModifiedAt`', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto({ omit: 'fileModifiedAt' }) }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should require `duration`', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto({ omit: 'duration' }) }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should throw if `isFavorite` is not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto(), isFavorite: 'not-a-boolean' }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + it('should throw if `visibility` is not an enum', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ ...makeUploadDto(), visibility: 'not-a-boolean' }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + }); + + // TODO figure out how to deal with `sendFile` + describe.skip('GET /assets/:id/original', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/original`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + // TODO figure out how to deal with `sendFile` + describe.skip('GET /assets/:id/thumbnail', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/thumbnail`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/server/src/controllers/asset.controller.spec.ts b/server/src/controllers/asset.controller.spec.ts new file mode 100644 index 0000000000..66d2d7c206 --- /dev/null +++ b/server/src/controllers/asset.controller.spec.ts @@ -0,0 +1,118 @@ +import { AssetController } from 'src/controllers/asset.controller'; +import { AssetService } from 'src/services/asset.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(AssetController.name, () => { + let ctx: ControllerContext; + + beforeAll(async () => { + ctx = await controllerSetup(AssetController, [{ provide: AssetService, useValue: mockBaseService(AssetService) }]); + return () => ctx.close(); + }); + + beforeEach(() => { + ctx.reset(); + }); + + describe('PUT /assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('DELETE /assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()) + .delete(`/assets`) + .send({ ids: [factory.uuid()] }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .delete(`/assets`) + .send({ ids: ['123'] }); + + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['each value in ids must be a UUID'])); + }); + }); + + describe('GET /assets/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/assets/123`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + }); + + describe('PUT /assets/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/123`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/123`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + + it('should reject invalid gps coordinates', async () => { + for (const test of [ + { latitude: 12 }, + { longitude: 12 }, + { latitude: 12, longitude: 'abc' }, + { latitude: 'abc', longitude: 12 }, + { latitude: null, longitude: 12 }, + { latitude: 12, longitude: null }, + { latitude: 91, longitude: 12 }, + { latitude: -91, longitude: 12 }, + { latitude: 12, longitude: -181 }, + { latitude: 12, longitude: 181 }, + ]) { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}`).send(test); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + } + }); + + it('should reject invalid rating', async () => { + for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}`).send(test); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest()); + } + }); + }); + + describe('GET /assets/statistics', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/statistics`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /assets/random', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/random`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should not allow count to be a string', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/assets/random?count=ABC'); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest(['count must be a positive number', 'count must be an integer number']), + ); + }); + }); +}); diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index 9a7252a087..925b64c8a8 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { EndpointLifecycle } from 'src/decorators'; -import { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AssetBulkDeleteDto, AssetBulkUpdateDto, @@ -13,7 +13,6 @@ import { UpdateAssetDto, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { MemoryLaneDto } from 'src/dtos/search.dto'; import { RouteKey } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { AssetService } from 'src/services/asset.service'; @@ -24,12 +23,6 @@ import { UUIDParamDto } from 'src/validation'; export class AssetController { constructor(private service: AssetService) {} - @Get('memory-lane') - @Authenticated() - getMemoryLane(@Auth() auth: AuthDto, @Query() dto: MemoryLaneDto): Promise { - return this.service.getMemoryLane(auth, dto); - } - @Get('random') @Authenticated() @EndpointLifecycle({ deprecatedAt: 'v1.116.0' }) diff --git a/server/src/controllers/auth.controller.spec.ts b/server/src/controllers/auth.controller.spec.ts new file mode 100644 index 0000000000..4129b24124 --- /dev/null +++ b/server/src/controllers/auth.controller.spec.ts @@ -0,0 +1,191 @@ +import { AuthController } from 'src/controllers/auth.controller'; +import { LoginResponseDto } from 'src/dtos/auth.dto'; +import { AuthService } from 'src/services/auth.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(AuthController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(AuthService); + + beforeAll(async () => { + ctx = await controllerSetup(AuthController, [{ provide: AuthService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('POST /auth/admin-sign-up', () => { + const name = 'admin'; + const email = 'admin@immich.cloud'; + const password = 'password'; + + it('should require an email address', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/admin-sign-up').send({ name, password }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest()); + }); + + it('should require a password', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/admin-sign-up').send({ name, email }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest()); + }); + + it('should require a name', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/admin-sign-up').send({ email, password }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest()); + }); + + it('should require a valid email', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/auth/admin-sign-up') + .send({ name, email: 'immich', password }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest()); + }); + + it('should transform email to lower case', async () => { + service.adminSignUp.mockReset(); + const { status } = await request(ctx.getHttpServer()) + .post('/auth/admin-sign-up') + .send({ name: 'admin', password: 'password', email: 'aDmIn@IMMICH.cloud' }); + expect(status).toEqual(201); + expect(service.adminSignUp).toHaveBeenCalledWith(expect.objectContaining({ email: 'admin@immich.cloud' })); + }); + + it('should accept an email with a local domain', async () => { + const { status } = await request(ctx.getHttpServer()) + .post('/auth/admin-sign-up') + .send({ name: 'admin', password: 'password', email: 'admin@local' }); + expect(status).toEqual(201); + }); + }); + + describe('POST /auth/login', () => { + it(`should require an email and password`, async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/login').send({ name: 'admin' }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'email should not be empty', + 'email must be an email', + 'password should not be empty', + 'password must be a string', + ]), + ); + }); + + it(`should not allow null email`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: null, password: 'password' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['email should not be empty', 'email must be an email'])); + }); + + it(`should not allow null password`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: 'admin@immich.cloud', password: null }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['password should not be empty', 'password must be a string'])); + }); + + it('should reject an invalid email', async () => { + service.login.mockResolvedValue({ accessToken: 'access-token' } as LoginResponseDto); + + const { status, body } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: [], password: 'password' }); + + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['email must be an email'])); + }); + + it('should transform the email to all lowercase', async () => { + service.login.mockResolvedValue({ accessToken: 'access-token' } as LoginResponseDto); + + const { status } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: 'aDmIn@iMmIcH.ApP', password: 'password' }); + + expect(status).toBe(201); + expect(service.login).toHaveBeenCalledWith( + expect.objectContaining({ email: 'admin@immich.app' }), + expect.anything(), + ); + }); + + it('should accept an email with a local domain', async () => { + service.login.mockResolvedValue({ accessToken: 'access-token' } as LoginResponseDto); + + const { status } = await request(ctx.getHttpServer()) + .post('/auth/login') + .send({ name: 'admin', email: 'admin@local', password: 'password' }); + + expect(status).toEqual(201); + expect(service.login).toHaveBeenCalledWith(expect.objectContaining({ email: 'admin@local' }), expect.anything()); + }); + }); + + describe('POST /auth/change-password', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()) + .post('/auth/change-password') + .send({ password: 'password', newPassword: 'Password1234' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /auth/pin-code', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/auth/pin-code').send({ pinCode: '123456' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should reject 5 digits', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/pin-code').send({ pinCode: '12345' }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest(['pinCode must be a 6-digit numeric string'])); + }); + + it('should reject 7 digits', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/pin-code').send({ pinCode: '1234567' }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest(['pinCode must be a 6-digit numeric string'])); + }); + + it('should reject non-numbers', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/auth/pin-code').send({ pinCode: 'A12345' }); + expect(status).toEqual(400); + expect(body).toEqual(errorDto.badRequest(['pinCode must be a 6-digit numeric string'])); + }); + }); + + describe('PUT /auth/pin-code', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put('/auth/pin-code').send({ pinCode: '123456', newPinCode: '654321' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('DELETE /auth/pin-code', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete('/auth/pin-code').send({ pinCode: '123456' }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /auth/status', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/auth/status'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 92fa59f6bf..78c611d761 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -1,12 +1,17 @@ -import { Body, Controller, HttpCode, HttpStatus, Post, Req, Res } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Post, Put, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { AuthDto, + AuthStatusResponseDto, ChangePasswordDto, LoginCredentialDto, LoginResponseDto, LogoutResponseDto, + PinCodeChangeDto, + PinCodeResetDto, + PinCodeSetupDto, + SessionUnlockDto, SignUpDto, ValidateAccessTokenResponseDto, } from 'src/dtos/auth.dto'; @@ -23,8 +28,8 @@ export class AuthController { @Post('login') async login( - @Body() loginCredential: LoginCredentialDto, @Res({ passthrough: true }) res: Response, + @Body() loginCredential: LoginCredentialDto, @GetLoginDetails() loginDetails: LoginDetails, ): Promise { const body = await this.service.login(loginCredential, loginDetails); @@ -74,4 +79,42 @@ export class AuthController { ImmichCookie.IS_AUTHENTICATED, ]); } + + @Get('status') + @Authenticated() + getAuthStatus(@Auth() auth: AuthDto): Promise { + return this.service.getAuthStatus(auth); + } + + @Post('pin-code') + @Authenticated() + setupPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise { + return this.service.setupPinCode(auth, dto); + } + + @Put('pin-code') + @Authenticated() + async changePinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise { + return this.service.changePinCode(auth, dto); + } + + @Delete('pin-code') + @Authenticated() + async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise { + return this.service.resetPinCode(auth, dto); + } + + @Post('session/unlock') + @HttpCode(HttpStatus.OK) + @Authenticated() + async unlockAuthSession(@Auth() auth: AuthDto, @Body() dto: SessionUnlockDto): Promise { + return this.service.unlockSession(auth, dto); + } + + @Post('session/lock') + @HttpCode(HttpStatus.OK) + @Authenticated() + async lockAuthSession(@Auth() auth: AuthDto): Promise { + return this.service.lockSession(auth); + } } diff --git a/server/src/controllers/download.controller.spec.ts b/server/src/controllers/download.controller.spec.ts new file mode 100644 index 0000000000..9385c445b5 --- /dev/null +++ b/server/src/controllers/download.controller.spec.ts @@ -0,0 +1,46 @@ +import { DownloadController } from 'src/controllers/download.controller'; +import { DownloadService } from 'src/services/download.service'; +import request from 'supertest'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; +import { Readable } from 'typeorm/platform/PlatformTools.js'; + +describe(DownloadController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(DownloadService); + + beforeAll(async () => { + ctx = await controllerSetup(DownloadController, [{ provide: DownloadService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('POST /download/info', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()) + .post('/download/info') + .send({ assetIds: [factory.uuid()] }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /download/archive', () => { + it('should be an authenticated route', async () => { + const stream = new Readable({ + read() { + this.push('test'); + this.push(null); + }, + }); + service.downloadArchive.mockResolvedValue({ stream }); + await request(ctx.getHttpServer()) + .post('/download/archive') + .send({ assetIds: [factory.uuid()] }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/file-report.controller.ts b/server/src/controllers/file-report.controller.ts deleted file mode 100644 index a51a94a50e..0000000000 --- a/server/src/controllers/file-report.controller.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Body, Controller, Get, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { FileChecksumDto, FileChecksumResponseDto, FileReportDto, FileReportFixDto } from 'src/dtos/audit.dto'; -import { Authenticated } from 'src/middleware/auth.guard'; -import { AuditService } from 'src/services/audit.service'; - -@ApiTags('File Reports') -@Controller('reports') -export class ReportController { - constructor(private service: AuditService) {} - - @Get() - @Authenticated({ admin: true }) - getAuditFiles(): Promise { - return this.service.getFileReport(); - } - - @Post('checksum') - @Authenticated({ admin: true }) - getFileChecksums(@Body() dto: FileChecksumDto): Promise { - return this.service.getChecksums(dto); - } - - @Post('fix') - @Authenticated({ admin: true }) - fixAuditFiles(@Body() dto: FileReportFixDto): Promise { - return this.service.fixItems(dto.items); - } -} diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index c9d63f8bcd..9c39e580b6 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -8,11 +8,11 @@ import { AuthController } from 'src/controllers/auth.controller'; import { DownloadController } from 'src/controllers/download.controller'; import { DuplicateController } from 'src/controllers/duplicate.controller'; import { FaceController } from 'src/controllers/face.controller'; -import { ReportController } from 'src/controllers/file-report.controller'; import { JobController } from 'src/controllers/job.controller'; import { LibraryController } from 'src/controllers/library.controller'; import { MapController } from 'src/controllers/map.controller'; import { MemoryController } from 'src/controllers/memory.controller'; +import { NotificationAdminController } from 'src/controllers/notification-admin.controller'; import { NotificationController } from 'src/controllers/notification.controller'; import { OAuthController } from 'src/controllers/oauth.controller'; import { PartnerController } from 'src/controllers/partner.controller'; @@ -48,10 +48,10 @@ export const controllers = [ MapController, MemoryController, NotificationController, + NotificationAdminController, OAuthController, PartnerController, PersonController, - ReportController, SearchController, ServerController, SessionController, diff --git a/server/src/controllers/notification-admin.controller.ts b/server/src/controllers/notification-admin.controller.ts new file mode 100644 index 0000000000..9bac865bdf --- /dev/null +++ b/server/src/controllers/notification-admin.controller.ts @@ -0,0 +1,44 @@ +import { Body, Controller, HttpCode, HttpStatus, Param, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { + NotificationCreateDto, + NotificationDto, + TemplateDto, + TemplateResponseDto, + TestEmailResponseDto, +} from 'src/dtos/notification.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { NotificationAdminService } from 'src/services/notification-admin.service'; + +@ApiTags('Notifications (Admin)') +@Controller('admin/notifications') +export class NotificationAdminController { + constructor(private service: NotificationAdminService) {} + + @Post() + @Authenticated({ admin: true }) + createNotification(@Auth() auth: AuthDto, @Body() dto: NotificationCreateDto): Promise { + return this.service.create(auth, dto); + } + + @Post('test-email') + @HttpCode(HttpStatus.OK) + @Authenticated({ admin: true }) + sendTestEmailAdmin(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise { + return this.service.sendTestEmail(auth.user.id, dto); + } + + @Post('templates/:name') + @HttpCode(HttpStatus.OK) + @Authenticated({ admin: true }) + getNotificationTemplateAdmin( + @Auth() auth: AuthDto, + @Param('name') name: EmailTemplate, + @Body() dto: TemplateDto, + ): Promise { + return this.service.getTemplate(name, dto.template); + } +} diff --git a/server/src/controllers/notification.controller.spec.ts b/server/src/controllers/notification.controller.spec.ts new file mode 100644 index 0000000000..0dce7d73b5 --- /dev/null +++ b/server/src/controllers/notification.controller.spec.ts @@ -0,0 +1,64 @@ +import { NotificationController } from 'src/controllers/notification.controller'; +import { NotificationService } from 'src/services/notification.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { factory } from 'test/small.factory'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(NotificationController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(NotificationService); + + beforeAll(async () => { + ctx = await controllerSetup(NotificationController, [{ provide: NotificationService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /notifications', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/notifications'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should reject an invalid notification level`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .get(`/notifications`) + .query({ level: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('level must be one of the following values')])); + }); + }); + + describe('PUT /notifications', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/notifications'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /notifications/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/notifications/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/notifications/123`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('id must be a UUID')])); + }); + }); + + describe('PUT /notifications/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/notifications/${factory.uuid()}`).send({ readAt: factory.date() }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/notification.controller.ts b/server/src/controllers/notification.controller.ts index 39946a9fc9..c64f786850 100644 --- a/server/src/controllers/notification.controller.ts +++ b/server/src/controllers/notification.controller.ts @@ -1,32 +1,60 @@ -import { Body, Controller, HttpCode, HttpStatus, Param, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; -import { TemplateDto, TemplateResponseDto, TestEmailResponseDto } from 'src/dtos/notification.dto'; -import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { + NotificationDeleteAllDto, + NotificationDto, + NotificationSearchDto, + NotificationUpdateAllDto, + NotificationUpdateDto, +} from 'src/dtos/notification.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; +import { UUIDParamDto } from 'src/validation'; @ApiTags('Notifications') @Controller('notifications') export class NotificationController { constructor(private service: NotificationService) {} - @Post('test-email') - @HttpCode(HttpStatus.OK) - @Authenticated({ admin: true }) - sendTestEmail(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise { - return this.service.sendTestEmail(auth.user.id, dto); + @Get() + @Authenticated({ permission: Permission.NOTIFICATION_READ }) + getNotifications(@Auth() auth: AuthDto, @Query() dto: NotificationSearchDto): Promise { + return this.service.search(auth, dto); } - @Post('templates/:name') - @HttpCode(HttpStatus.OK) - @Authenticated({ admin: true }) - getNotificationTemplate( + @Put() + @Authenticated({ permission: Permission.NOTIFICATION_UPDATE }) + updateNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationUpdateAllDto): Promise { + return this.service.updateAll(auth, dto); + } + + @Delete() + @Authenticated({ permission: Permission.NOTIFICATION_DELETE }) + deleteNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationDeleteAllDto): Promise { + return this.service.deleteAll(auth, dto); + } + + @Get(':id') + @Authenticated({ permission: Permission.NOTIFICATION_READ }) + getNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.get(auth, id); + } + + @Put(':id') + @Authenticated({ permission: Permission.NOTIFICATION_UPDATE }) + updateNotification( @Auth() auth: AuthDto, - @Param('name') name: EmailTemplate, - @Body() dto: TemplateDto, - ): Promise { - return this.service.getTemplate(name, dto.template); + @Param() { id }: UUIDParamDto, + @Body() dto: NotificationUpdateDto, + ): Promise { + return this.service.update(auth, id, dto); + } + + @Delete(':id') + @Authenticated({ permission: Permission.NOTIFICATION_DELETE }) + deleteNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.delete(auth, id); } } diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index b5b94030f2..23ddff5ddc 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -29,17 +29,35 @@ export class OAuthController { } @Post('authorize') - startOAuth(@Body() dto: OAuthConfigDto): Promise { - return this.service.authorize(dto); + async startOAuth( + @Body() dto: OAuthConfigDto, + @Res({ passthrough: true }) res: Response, + @GetLoginDetails() loginDetails: LoginDetails, + ): Promise { + const { url, state, codeVerifier } = await this.service.authorize(dto); + return respondWithCookie( + res, + { url }, + { + isSecure: loginDetails.isSecure, + values: [ + { key: ImmichCookie.OAUTH_STATE, value: state }, + { key: ImmichCookie.OAUTH_CODE_VERIFIER, value: codeVerifier }, + ], + }, + ); } @Post('callback') async finishOAuth( + @Req() request: Request, @Res({ passthrough: true }) res: Response, @Body() dto: OAuthCallbackDto, @GetLoginDetails() loginDetails: LoginDetails, ): Promise { - const body = await this.service.callback(dto, loginDetails); + const body = await this.service.callback(dto, request.headers, loginDetails); + res.clearCookie(ImmichCookie.OAUTH_STATE); + res.clearCookie(ImmichCookie.OAUTH_CODE_VERIFIER); return respondWithCookie(res, body, { isSecure: loginDetails.isSecure, values: [ @@ -52,8 +70,12 @@ export class OAuthController { @Post('link') @Authenticated() - linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise { - return this.service.link(auth, dto); + linkOAuthAccount( + @Req() request: Request, + @Auth() auth: AuthDto, + @Body() dto: OAuthCallbackDto, + ): Promise { + return this.service.link(auth, dto, request.headers); } @Post('unlink') diff --git a/server/src/controllers/person.controller.spec.ts b/server/src/controllers/person.controller.spec.ts new file mode 100644 index 0000000000..0366829336 --- /dev/null +++ b/server/src/controllers/person.controller.spec.ts @@ -0,0 +1,172 @@ +import { PersonController } from 'src/controllers/person.controller'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PersonService } from 'src/services/person.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { factory } from 'test/small.factory'; +import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(PersonController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(PersonService); + + beforeAll(async () => { + ctx = await controllerSetup(PersonController, [ + { provide: PersonService, useValue: service }, + { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /people', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/people'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should require closestPersonId to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .get(`/people`) + .query({ closestPersonId: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + + it(`should require closestAssetId to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .get(`/people`) + .query({ closestAssetId: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + }); + + describe('POST /people', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/people'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should map an empty birthDate to null', async () => { + await request(ctx.getHttpServer()).post('/people').send({ birthDate: '' }); + expect(service.create).toHaveBeenCalledWith(undefined, { birthDate: null }); + }); + }); + + describe('GET /people/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/people/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /people/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/people/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid uuid', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/people/123`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('id must be a UUID')])); + }); + + it(`should not allow a null name`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post(`/people`) + .send({ name: null }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['name must be a string'])); + }); + + it(`should require featureFaceAssetId to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ featureFaceAssetId: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['featureFaceAssetId must be a UUID'])); + }); + + it(`should require isFavorite to be a boolean`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ isFavorite: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isFavorite must be a boolean value'])); + }); + + it(`should require isHidden to be a boolean`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ isHidden: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isHidden must be a boolean value'])); + }); + + it('should map an empty birthDate to null', async () => { + const id = factory.uuid(); + await request(ctx.getHttpServer()).put(`/people/${id}`).send({ birthDate: '' }); + expect(service.update).toHaveBeenCalledWith(undefined, id, { birthDate: null }); + }); + + it('should not accept an invalid birth date (false)', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ birthDate: false }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'birthDate must be a string in the format yyyy-MM-dd', + 'Birth date cannot be in the future', + ]), + ); + }); + + it('should not accept an invalid birth date (number)', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ birthDate: 123_456 }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'birthDate must be a string in the format yyyy-MM-dd', + 'Birth date cannot be in the future', + ]), + ); + }); + + it('should not accept a birth date in the future)', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/people/${factory.uuid()}`) + .send({ birthDate: '9999-01-01' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['Birth date cannot be in the future'])); + }); + }); + + describe('POST /people/:id/merge', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post(`/people/${factory.uuid()}/merge`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /people/:id/statistics', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/people/${factory.uuid()}/statistics`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index e98dd6a002..3440042eda 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -27,7 +27,9 @@ export class PersonController { constructor( private service: PersonService, private logger: LoggingRepository, - ) {} + ) { + this.logger.setContext(PersonController.name); + } @Get() @Authenticated({ permission: Permission.PERSON_READ }) diff --git a/server/src/controllers/search.controller.spec.ts b/server/src/controllers/search.controller.spec.ts new file mode 100644 index 0000000000..39d2cb8fcd --- /dev/null +++ b/server/src/controllers/search.controller.spec.ts @@ -0,0 +1,197 @@ +import { SearchController } from 'src/controllers/search.controller'; +import { SearchService } from 'src/services/search.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(SearchController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(SearchService); + + beforeAll(async () => { + ctx = await controllerSetup(SearchController, [{ provide: SearchService, useValue: service }]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('POST /search/metadata', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/search/metadata'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should reject page as a string', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ page: 'abc' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['page must not be less than 1', 'page must be an integer number'])); + }); + + it('should reject page as a negative number', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ page: -10 }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['page must not be less than 1'])); + }); + + it('should reject page as 0', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ page: 0 }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['page must not be less than 1'])); + }); + + it('should reject size as a string', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ size: 'abc' }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'size must not be greater than 1000', + 'size must not be less than 1', + 'size must be an integer number', + ]), + ); + }); + + it('should reject an invalid size', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ size: -1.5 }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['size must not be less than 1', 'size must be an integer number'])); + }); + + it('should reject an visibility as not an enum', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/metadata') + .send({ visibility: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest(['visibility must be one of the following values: archive, timeline, hidden, locked']), + ); + }); + + it('should reject an isFavorite as not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/metadata') + .send({ isFavorite: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isFavorite must be a boolean value'])); + }); + + it('should reject an isEncoded as not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/metadata') + .send({ isEncoded: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isEncoded must be a boolean value'])); + }); + + it('should reject an isOffline as not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/metadata') + .send({ isOffline: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isOffline must be a boolean value'])); + }); + + it('should reject an isMotion as not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ isMotion: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['isMotion must be a boolean value'])); + }); + + describe('POST /search/random', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/search/random'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should reject if withStacked is not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/random') + .send({ withStacked: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['withStacked must be a boolean value'])); + }); + + it('should reject if withPeople is not a boolean', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/search/random') + .send({ withPeople: 'immich' }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['withPeople must be a boolean value'])); + }); + }); + + describe('POST /search/smart', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/search/smart'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a query', async () => { + const { status, body } = await request(ctx.getHttpServer()).post('/search/smart').send({}); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['query should not be empty', 'query must be a string'])); + }); + }); + + describe('GET /search/explore', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/search/explore'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /search/person', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/search/person'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a name', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/search/person').send({}); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['name should not be empty', 'name must be a string'])); + }); + }); + + describe('GET /search/places', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/search/places'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a name', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/search/places').send({}); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(['name should not be empty', 'name must be a string'])); + }); + }); + + describe('GET /search/cities', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/search/cities'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /search/suggestions', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/search/suggestions'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a type', async () => { + const { status, body } = await request(ctx.getHttpServer()).get('/search/suggestions').send({}); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'type should not be empty', + expect.stringContaining('type must be one of the following values:'), + ]), + ); + }); + }); + }); +}); diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 367c39dae9..c51ad8e06a 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -46,7 +46,7 @@ export class SearchController { @Get('explore') @Authenticated() getExploreData(@Auth() auth: AuthDto): Promise { - return this.service.getExploreData(auth) as Promise; + return this.service.getExploreData(auth); } @Get('person') diff --git a/server/src/controllers/server.controller.spec.ts b/server/src/controllers/server.controller.spec.ts new file mode 100644 index 0000000000..6b00490d28 --- /dev/null +++ b/server/src/controllers/server.controller.spec.ts @@ -0,0 +1,35 @@ +import { ServerController } from 'src/controllers/server.controller'; +import { ServerService } from 'src/services/server.service'; +import { SystemMetadataService } from 'src/services/system-metadata.service'; +import { VersionService } from 'src/services/version.service'; +import request from 'supertest'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(ServerController.name, () => { + let ctx: ControllerContext; + const serverService = mockBaseService(ServerService); + const systemMetadataService = mockBaseService(SystemMetadataService); + const versionService = mockBaseService(VersionService); + + beforeAll(async () => { + ctx = await controllerSetup(ServerController, [ + { provide: ServerService, useValue: serverService }, + { provide: SystemMetadataService, useValue: systemMetadataService }, + { provide: VersionService, useValue: versionService }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + serverService.resetAllMocks(); + versionService.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /server/license', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/server/license'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 8327ff6d1d..267fc42ef4 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -13,8 +13,10 @@ import { ServerVersionHistoryResponseDto, ServerVersionResponseDto, } from 'src/dtos/server.dto'; +import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto'; import { Authenticated } from 'src/middleware/auth.guard'; import { ServerService } from 'src/services/server.service'; +import { SystemMetadataService } from 'src/services/system-metadata.service'; import { VersionService } from 'src/services/version.service'; @ApiTags('Server') @@ -22,6 +24,7 @@ import { VersionService } from 'src/services/version.service'; export class ServerController { constructor( private service: ServerService, + private systemMetadataService: SystemMetadataService, private versionService: VersionService, ) {} @@ -96,4 +99,10 @@ export class ServerController { getServerLicense(): Promise { return this.service.getLicense(); } + + @Get('version-check') + @Authenticated() + getVersionCheck(): Promise { + return this.systemMetadataService.getVersionCheckState(); + } } diff --git a/server/src/controllers/session.controller.ts b/server/src/controllers/session.controller.ts index d526c2e599..3838d5af80 100644 --- a/server/src/controllers/session.controller.ts +++ b/server/src/controllers/session.controller.ts @@ -1,7 +1,7 @@ -import { Controller, Delete, Get, HttpCode, HttpStatus, Param } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; -import { SessionResponseDto } from 'src/dtos/session.dto'; +import { SessionCreateDto, SessionCreateResponseDto, SessionResponseDto } from 'src/dtos/session.dto'; import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { SessionService } from 'src/services/session.service'; @@ -12,6 +12,12 @@ import { UUIDParamDto } from 'src/validation'; export class SessionController { constructor(private service: SessionService) {} + @Post() + @Authenticated({ permission: Permission.SESSION_CREATE }) + createSession(@Auth() auth: AuthDto, @Body() dto: SessionCreateDto): Promise { + return this.service.create(auth, dto); + } + @Get() @Authenticated({ permission: Permission.SESSION_READ }) getSessions(@Auth() auth: AuthDto): Promise { @@ -31,4 +37,11 @@ export class SessionController { deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } + + @Post(':id/lock') + @Authenticated({ permission: Permission.SESSION_LOCK }) + @HttpCode(HttpStatus.NO_CONTENT) + lockSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.lock(auth, id); + } } diff --git a/server/src/controllers/system-metadata.controller.ts b/server/src/controllers/system-metadata.controller.ts index bca5c65d8e..71c37d02c4 100644 --- a/server/src/controllers/system-metadata.controller.ts +++ b/server/src/controllers/system-metadata.controller.ts @@ -1,6 +1,10 @@ import { Body, Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AdminOnboardingUpdateDto, ReverseGeocodingStateResponseDto } from 'src/dtos/system-metadata.dto'; +import { + AdminOnboardingUpdateDto, + ReverseGeocodingStateResponseDto, + VersionCheckStateResponseDto, +} from 'src/dtos/system-metadata.dto'; import { Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { SystemMetadataService } from 'src/services/system-metadata.service'; @@ -28,4 +32,10 @@ export class SystemMetadataController { getReverseGeocodingState(): Promise { return this.service.getReverseGeocodingState(); } + + @Get('version-check-state') + @Authenticated({ permission: Permission.SYSTEM_METADATA_READ, admin: true }) + getVersionCheckState(): Promise { + return this.service.getVersionCheckState(); + } } diff --git a/server/src/controllers/timeline.controller.ts b/server/src/controllers/timeline.controller.ts index 92de84d346..b4ee042625 100644 --- a/server/src/controllers/timeline.controller.ts +++ b/server/src/controllers/timeline.controller.ts @@ -1,8 +1,7 @@ -import { Controller, Get, Query } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { Controller, Get, Header, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; -import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; +import { TimeBucketAssetDto, TimeBucketAssetResponseDto, TimeBucketDto } from 'src/dtos/time-bucket.dto'; import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { TimelineService } from 'src/services/timeline.service'; @@ -14,13 +13,15 @@ export class TimelineController { @Get('buckets') @Authenticated({ permission: Permission.ASSET_READ, sharedLink: true }) - getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto): Promise { + getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto) { return this.service.getTimeBuckets(auth, dto); } @Get('bucket') @Authenticated({ permission: Permission.ASSET_READ, sharedLink: true }) - getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto): Promise { - return this.service.getTimeBucket(auth, dto) as Promise; + @ApiOkResponse({ type: TimeBucketAssetResponseDto }) + @Header('Content-Type', 'application/json') + getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto) { + return this.service.getTimeBucket(auth, dto); } } diff --git a/server/src/controllers/user-admin.controller.ts b/server/src/controllers/user-admin.controller.ts index 4dfeae949a..83d7caef08 100644 --- a/server/src/controllers/user-admin.controller.ts +++ b/server/src/controllers/user-admin.controller.ts @@ -1,5 +1,6 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { AssetStatsDto, AssetStatsResponseDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; import { @@ -57,6 +58,16 @@ export class UserAdminController { return this.service.delete(auth, id, dto); } + @Get(':id/statistics') + @Authenticated({ permission: Permission.ADMIN_USER_READ, admin: true }) + getUserStatisticsAdmin( + @Auth() auth: AuthDto, + @Param() { id }: UUIDParamDto, + @Query() dto: AssetStatsDto, + ): Promise { + return this.service.getStatistics(auth, id, dto); + } + @Get(':id/preferences') @Authenticated({ permission: Permission.ADMIN_USER_READ, admin: true }) getUserPreferencesAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { diff --git a/server/src/controllers/user.controller.spec.ts b/server/src/controllers/user.controller.spec.ts new file mode 100644 index 0000000000..19f9e919de --- /dev/null +++ b/server/src/controllers/user.controller.spec.ts @@ -0,0 +1,79 @@ +import { UserController } from 'src/controllers/user.controller'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { UserService } from 'src/services/user.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { factory } from 'test/small.factory'; +import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(UserController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(UserService); + + beforeAll(async () => { + ctx = await controllerSetup(UserController, [ + { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, + { provide: UserService, useValue: service }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /users', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/users'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('GET /users/me', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/users/me'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /users/me', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put('/users/me'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + for (const key of ['email', 'name']) { + it(`should not allow null ${key}`, async () => { + const dto = { [key]: null }; + const { status, body } = await request(ctx.getHttpServer()) + .put(`/users/me`) + .set('Authorization', `Bearer token`) + .send(dto); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest()); + }); + } + }); + + describe('GET /users/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/users/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /users/me/license', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put('/users/me/license'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('DELETE /users/me/license', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete('/users/me/license'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); +}); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 9bd43a662d..1a8e31e86b 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -90,7 +90,7 @@ export class StorageCore { return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`); } - static getImagePath(asset: ThumbnailPathEntity, type: GeneratedImageType, format: ImageFormat) { + static getImagePath(asset: ThumbnailPathEntity, type: GeneratedImageType, format: 'jpeg' | 'webp') { return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}-${type}.${format}`); } diff --git a/server/src/database.ts b/server/src/database.ts index b504d39579..cfccd70b75 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -1,14 +1,16 @@ import { Selectable } from 'kysely'; -import { AssetJobStatus as DatabaseAssetJobStatus, Exif as DatabaseExif } from 'src/db'; -import { AssetEntity } from 'src/entities/asset.entity'; +import { Albums, Exif as DatabaseExif } from 'src/db'; +import { MapAsset } from 'src/dtos/asset-response.dto'; import { AlbumUserRole, AssetFileType, - AssetStatus, AssetType, + AssetVisibility, MemoryType, Permission, + SharedLinkType, SourceType, + UserAvatarColor, UserStatus, } from 'src/enum'; import { OnThisDayData, UserMetadataItem } from 'src/types'; @@ -44,7 +46,7 @@ export type Library = { exclusionPatterns: string[]; deletedAt: Date | null; refreshedAt: Date | null; - assets?: Asset[]; + assets?: MapAsset[]; }; export type AuthApiKey = { @@ -96,13 +98,33 @@ export type Memory = { data: OnThisDayData; ownerId: string; isSaved: boolean; - assets: Asset[]; + assets: MapAsset[]; +}; + +export type Asset = { + id: string; + checksum: Buffer; + deviceAssetId: string; + deviceId: string; + fileCreatedAt: Date; + fileModifiedAt: Date; + isExternal: boolean; + visibility: AssetVisibility; + libraryId: string | null; + livePhotoVideoId: string | null; + localDateTime: Date; + originalFileName: string; + originalPath: string; + ownerId: string; + sidecarPath: string | null; + type: AssetType; }; export type User = { id: string; name: string; email: string; + avatarColor: UserAvatarColor | null; profileImagePath: string; profileChangedAt: Date; }; @@ -128,39 +150,6 @@ export type StorageAsset = { encodedVideoPath: string | null; }; -export type Asset = { - createdAt: Date; - updatedAt: Date; - deletedAt: Date | null; - id: string; - updateId: string; - status: AssetStatus; - checksum: Buffer; - deviceAssetId: string; - deviceId: string; - duplicateId: string | null; - duration: string | null; - encodedVideoPath: string | null; - fileCreatedAt: Date | null; - fileModifiedAt: Date | null; - isArchived: boolean; - isExternal: boolean; - isFavorite: boolean; - isOffline: boolean; - isVisible: boolean; - libraryId: string | null; - livePhotoVideoId: string | null; - localDateTime: Date | null; - originalFileName: string; - originalPath: string; - ownerId: string; - sidecarPath: string | null; - stack?: Stack | null; - stackId: string | null; - thumbhash: Buffer | null; - type: AssetType; -}; - export type SidecarWriteAsset = { id: string; sidecarPath: string | null; @@ -173,7 +162,7 @@ export type Stack = { primaryAssetId: string; owner?: User; ownerId: string; - assets: AssetEntity[]; + assets: MapAsset[]; assetCount?: number; }; @@ -187,8 +176,31 @@ export type AuthSharedLink = { password: string | null; }; +export type SharedLink = { + id: string; + album?: Album | null; + albumId: string | null; + allowDownload: boolean; + allowUpload: boolean; + assets: MapAsset[]; + createdAt: Date; + description: string | null; + expiresAt: Date | null; + key: Buffer; + password: string | null; + showExif: boolean; + type: SharedLinkType; + userId: string; +}; + +export type Album = Selectable & { + owner: User; + assets: MapAsset[]; +}; + export type AuthSession = { id: string; + hasElevatedPermission: boolean; }; export type Partner = { @@ -220,8 +232,10 @@ export type Session = { id: string; createdAt: Date; updatedAt: Date; + expiresAt: Date | null; deviceOS: string; deviceType: string; + pinExpiresAt: Date | null; }; export type Exif = Omit, 'updatedAt' | 'updateId'>; @@ -256,11 +270,15 @@ export type AssetFace = { person?: Person | null; }; -export type AssetJobStatus = Selectable & { - asset: AssetEntity; -}; - -const userColumns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const; +const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const; +const userWithPrefixColumns = [ + 'users.id', + 'users.name', + 'users.email', + 'users.avatarColor', + 'users.profileImagePath', + 'users.profileChangedAt', +] as const; export const columns = { asset: [ @@ -271,7 +289,7 @@ export const columns = { 'assets.fileCreatedAt', 'assets.fileModifiedAt', 'assets.isExternal', - 'assets.isVisible', + 'assets.visibility', 'assets.libraryId', 'assets.livePhotoVideoId', 'assets.localDateTime', @@ -291,7 +309,7 @@ export const columns = { 'users.quotaSizeInBytes', ], authApiKey: ['api_keys.id', 'api_keys.permissions'], - authSession: ['sessions.id', 'sessions.updatedAt'], + authSession: ['sessions.id', 'sessions.updatedAt', 'sessions.pinExpiresAt'], authSharedLink: [ 'shared_links.id', 'shared_links.userId', @@ -302,7 +320,7 @@ export const columns = { 'shared_links.password', ], user: userColumns, - userWithPrefix: ['users.id', 'users.name', 'users.email', 'users.profileImagePath', 'users.profileChangedAt'], + userWithPrefix: userWithPrefixColumns, userAdmin: [ ...userColumns, 'createdAt', @@ -319,6 +337,7 @@ export const columns = { ], tag: ['tags.id', 'tags.value', 'tags.createdAt', 'tags.updatedAt', 'tags.color', 'tags.parentId'], apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'], + notification: ['id', 'createdAt', 'level', 'type', 'title', 'description', 'data', 'readAt'], syncAsset: [ 'id', 'ownerId', @@ -330,7 +349,7 @@ export const columns = { 'type', 'deletedAt', 'isFavorite', - 'isVisible', + 'visibility', 'updateId', ], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], diff --git a/server/src/db.d.ts b/server/src/db.d.ts index 7115b701ce..af1dd964fd 100644 --- a/server/src/db.d.ts +++ b/server/src/db.d.ts @@ -10,7 +10,10 @@ import { AssetOrder, AssetStatus, AssetType, + AssetVisibility, MemoryType, + NotificationLevel, + NotificationType, Permission, SharedLinkType, SourceType, @@ -71,6 +74,20 @@ export interface Albums { updateId: Generated; } +export interface AlbumsAudit { + deletedAt: Generated; + id: Generated; + albumId: string; + userId: string; +} + +export interface AlbumUsersAudit { + deletedAt: Generated; + id: Generated; + albumId: string; + userId: string; +} + export interface AlbumsAssetsAssets { albumsId: string; assetsId: string; @@ -81,6 +98,8 @@ export interface AlbumsSharedUsersUsers { albumsId: string; role: Generated; usersId: string; + updatedAt: Generated; + updateId: Generated; } export interface ApiKeys { @@ -143,17 +162,16 @@ export interface Assets { duplicateId: string | null; duration: string | null; encodedVideoPath: Generated; - fileCreatedAt: Timestamp | null; - fileModifiedAt: Timestamp | null; + fileCreatedAt: Timestamp; + fileModifiedAt: Timestamp; id: Generated; - isArchived: Generated; isExternal: Generated; isFavorite: Generated; isOffline: Generated; - isVisible: Generated; + visibility: Generated; libraryId: string | null; livePhotoVideoId: string | null; - localDateTime: Timestamp | null; + localDateTime: Timestamp; originalFileName: string; originalPath: string; ownerId: string; @@ -263,6 +281,21 @@ export interface Memories { updateId: Generated; } +export interface Notifications { + id: Generated; + createdAt: Generated; + updatedAt: Generated; + deletedAt: Timestamp | null; + updateId: Generated; + userId: string; + level: Generated; + type: NotificationType; + title: string; + description: string | null; + data: any | null; + readAt: Timestamp | null; +} + export interface MemoriesAssetsAssets { assetsId: string; memoriesId: string; @@ -326,10 +359,13 @@ export interface Sessions { deviceOS: Generated; deviceType: Generated; id: Generated; + parentId: string | null; + expiresAt: Date | null; token: string; updatedAt: Generated; updateId: Generated; userId: string; + pinExpiresAt: Timestamp | null; } export interface SessionSyncCheckpoints { @@ -446,8 +482,10 @@ export interface VersionHistory { export interface DB { activity: Activity; albums: Albums; + albums_audit: AlbumsAudit; albums_assets_assets: AlbumsAssetsAssets; albums_shared_users_users: AlbumsSharedUsersUsers; + album_users_audit: AlbumUsersAudit; api_keys: ApiKeys; asset_faces: AssetFaces; asset_files: AssetFiles; @@ -463,6 +501,7 @@ export interface DB { memories: Memories; memories_assets_assets: MemoriesAssetsAssets; migrations: Migrations; + notifications: Notifications; move_history: MoveHistory; naturalearth_countries: NaturalearthCountries; partners_audit: PartnersAudit; diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 7085899af7..6b34ffcafe 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -11,7 +11,8 @@ import { setUnion } from 'src/utils/set'; const GeneratedUuidV7Column = (options: Omit = {}) => Column({ ...options, type: 'uuid', nullable: false, default: () => `${immich_uuid_v7.name}()` }); -export const UpdateIdColumn = () => GeneratedUuidV7Column(); +export const UpdateIdColumn = (options: Omit = {}) => + GeneratedUuidV7Column(options); export const PrimaryGeneratedUuidV7Column = () => GeneratedUuidV7Column({ primary: true }); @@ -115,7 +116,7 @@ export const DummyValue = { DATE: new Date(), TIME_BUCKET: '2024-01-01T00:00:00.000Z', BOOLEAN: true, - VECTOR: '[1, 2, 3]', + VECTOR: JSON.stringify(Array.from({ length: 512 }, () => 0)), }; export const GENERATE_SQL_KEY = 'generate-sql-key'; diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index c9934ec909..40e51ef729 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -2,10 +2,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsEnum, IsString, ValidateNested } from 'class-validator'; import _ from 'lodash'; -import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AlbumUser, AuthSharedLink, User } from 'src/database'; +import { AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; -import { AlbumEntity } from 'src/entities/album.entity'; import { AlbumUserRole, AssetOrder } from 'src/enum'; import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; @@ -142,7 +142,23 @@ export class AlbumResponseDto { order?: AssetOrder; } -export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => { +export type MapAlbumDto = { + albumUsers?: AlbumUser[]; + assets?: MapAsset[]; + sharedLinks?: AuthSharedLink[]; + albumName: string; + description: string; + albumThumbnailAssetId: string | null; + createdAt: Date; + updatedAt: Date; + id: string; + ownerId: string; + owner: User; + isActivityEnabled: boolean; + order: AssetOrder; +}; + +export const mapAlbum = (entity: MapAlbumDto, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => { const albumUsers: AlbumUserResponseDto[] = []; if (entity.albumUsers) { @@ -159,7 +175,7 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt const assets = entity.assets || []; - const hasSharedLink = entity.sharedLinks?.length > 0; + const hasSharedLink = !!entity.sharedLinks && entity.sharedLinks.length > 0; const hasSharedUser = albumUsers.length > 0; let startDate = assets.at(0)?.localDateTime; @@ -190,5 +206,5 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt }; }; -export const mapAlbumWithAssets = (entity: AlbumEntity) => mapAlbum(entity, true); -export const mapAlbumWithoutAssets = (entity: AlbumEntity) => mapAlbum(entity, false); +export const mapAlbumWithAssets = (entity: MapAlbumDto) => mapAlbum(entity, true); +export const mapAlbumWithoutAssets = (entity: MapAlbumDto) => mapAlbum(entity, false); diff --git a/server/src/dtos/api-key.dto.ts b/server/src/dtos/api-key.dto.ts index 7e81ce8c60..ac6dd25bcf 100644 --- a/server/src/dtos/api-key.dto.ts +++ b/server/src/dtos/api-key.dto.ts @@ -18,6 +18,11 @@ export class APIKeyUpdateDto { @IsString() @IsNotEmpty() name!: string; + + @IsEnum(Permission, { each: true }) + @ApiProperty({ enum: Permission, enumName: 'Permission', isArray: true }) + @ArrayMinSize(1) + permissions!: Permission[]; } export class APIKeyCreateResponseDto { diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index 8837138599..a647b4515f 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -1,7 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; -import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; +import { AssetVisibility } from 'src/enum'; +import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; export enum AssetMediaSize { /** @@ -55,11 +56,8 @@ export class AssetMediaCreateDto extends AssetMediaBase { @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - - @ValidateBoolean({ optional: true }) - isVisible?: boolean; + @ValidateAssetVisibility({ optional: true }) + visibility?: AssetVisibility; @ValidateUUID({ optional: true }) livePhotoVideoId?: string; diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index 985ad04729..9bbfb450b2 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { AssetFace } from 'src/database'; +import { Selectable } from 'kysely'; +import { AssetFace, AssetFile, Exif, Stack, Tag, User } from 'src/database'; import { PropertyLifecycle } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { ExifResponseDto, mapExif } from 'src/dtos/exif.dto'; @@ -11,8 +12,8 @@ import { } from 'src/dtos/person.dto'; import { TagResponseDto, mapTag } from 'src/dtos/tag.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { AssetType } from 'src/enum'; +import { AssetStatus, AssetType, AssetVisibility } from 'src/enum'; +import { hexOrBufferToBase64 } from 'src/utils/bytes'; import { mimeTypes } from 'src/utils/mime-types'; export class SanitizedAssetResponseDto { @@ -43,6 +44,8 @@ export class AssetResponseDto extends SanitizedAssetResponseDto { isArchived!: boolean; isTrashed!: boolean; isOffline!: boolean; + @ApiProperty({ enum: AssetVisibility, enumName: 'AssetVisibility' }) + visibility!: AssetVisibility; exifInfo?: ExifResponseDto; tags?: TagResponseDto[]; people?: PersonWithFacesResponseDto[]; @@ -56,6 +59,43 @@ export class AssetResponseDto extends SanitizedAssetResponseDto { resized?: boolean; } +export type MapAsset = { + createdAt: Date; + updatedAt: Date; + deletedAt: Date | null; + id: string; + updateId: string; + status: AssetStatus; + checksum: Buffer; + deviceAssetId: string; + deviceId: string; + duplicateId: string | null; + duration: string | null; + encodedVideoPath: string | null; + exifInfo?: Selectable | null; + faces?: AssetFace[]; + fileCreatedAt: Date; + fileModifiedAt: Date; + files?: AssetFile[]; + isExternal: boolean; + isFavorite: boolean; + isOffline: boolean; + visibility: AssetVisibility; + libraryId: string | null; + livePhotoVideoId: string | null; + localDateTime: Date; + originalFileName: string; + originalPath: string; + owner?: User | null; + ownerId: string; + sidecarPath: string | null; + stack?: Stack | null; + stackId: string | null; + tags?: Tag[]; + thumbhash: Buffer | null; + type: AssetType; +}; + export class AssetStackResponseDto { id!: string; @@ -72,7 +112,7 @@ export type AssetMapOptions = { }; // TODO: this is inefficient -const peopleWithFaces = (faces: AssetFace[]): PersonWithFacesResponseDto[] => { +const peopleWithFaces = (faces?: AssetFace[]): PersonWithFacesResponseDto[] => { const result: PersonWithFacesResponseDto[] = []; if (faces) { for (const face of faces) { @@ -90,7 +130,7 @@ const peopleWithFaces = (faces: AssetFace[]): PersonWithFacesResponseDto[] => { return result; }; -const mapStack = (entity: AssetEntity) => { +const mapStack = (entity: { stack?: Stack | null }) => { if (!entity.stack) { return null; } @@ -102,16 +142,7 @@ const mapStack = (entity: AssetEntity) => { }; }; -// if an asset is jsonified in the DB before being returned, its buffer fields will be hex-encoded strings -export const hexOrBufferToBase64 = (encoded: string | Buffer) => { - if (typeof encoded === 'string') { - return Buffer.from(encoded.slice(2), 'hex').toString('base64'); - } - - return encoded.toString('base64'); -}; - -export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto { +export function mapAsset(entity: MapAsset, options: AssetMapOptions = {}): AssetResponseDto { const { stripMetadata = false, withStack = false } = options; if (stripMetadata) { @@ -145,15 +176,16 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As localDateTime: entity.localDateTime, updatedAt: entity.updatedAt, isFavorite: options.auth?.user.id === entity.ownerId ? entity.isFavorite : false, - isArchived: entity.isArchived, + isArchived: entity.visibility === AssetVisibility.ARCHIVE, isTrashed: !!entity.deletedAt, + visibility: entity.visibility, duration: entity.duration ?? '0:00:00.00000', exifInfo: entity.exifInfo ? mapExif(entity.exifInfo) : undefined, livePhotoVideoId: entity.livePhotoVideoId, tags: entity.tags?.map((tag) => mapTag(tag)), people: peopleWithFaces(entity.faces), unassignedFaces: entity.faces?.filter((face) => !face.person).map((a) => mapFacesWithoutPerson(a)), - checksum: hexOrBufferToBase64(entity.checksum), + checksum: hexOrBufferToBase64(entity.checksum)!, stack: withStack ? mapStack(entity) : undefined, isOffline: entity.isOffline, hasMetadata: true, @@ -161,10 +193,3 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As resized: true, }; } - -export class MemoryLaneResponseDto { - @ApiProperty({ type: 'integer' }) - yearsAgo!: number; - - assets!: AssetResponseDto[]; -} diff --git a/server/src/dtos/asset.dto.ts b/server/src/dtos/asset.dto.ts index 32b14055d5..940cfbf9cc 100644 --- a/server/src/dtos/asset.dto.ts +++ b/server/src/dtos/asset.dto.ts @@ -14,9 +14,9 @@ import { ValidateIf, } from 'class-validator'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; -import { AssetType } from 'src/enum'; +import { AssetType, AssetVisibility } from 'src/enum'; import { AssetStats } from 'src/repositories/asset.repository'; -import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; +import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateUUID } from 'src/validation'; export class DeviceIdDto { @IsNotEmpty() @@ -32,8 +32,8 @@ export class UpdateAssetBase { @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @ValidateBoolean({ optional: true }) - isArchived?: boolean; + @ValidateAssetVisibility({ optional: true }) + visibility?: AssetVisibility; @Optional() @IsDateString() @@ -54,6 +54,10 @@ export class UpdateAssetBase { @Max(5) @Min(-1) rating?: number; + + @Optional() + @IsString() + description?: string; } export class AssetBulkUpdateDto extends UpdateAssetBase { @@ -65,10 +69,6 @@ export class AssetBulkUpdateDto extends UpdateAssetBase { } export class UpdateAssetDto extends UpdateAssetBase { - @Optional() - @IsString() - description?: string; - @ValidateUUID({ optional: true, nullable: true }) livePhotoVideoId?: string | null; } @@ -105,8 +105,8 @@ export class AssetJobsDto extends AssetIdsDto { } export class AssetStatsDto { - @ValidateBoolean({ optional: true }) - isArchived?: boolean; + @ValidateAssetVisibility({ optional: true }) + visibility?: AssetVisibility; @ValidateBoolean({ optional: true }) isFavorite?: boolean; diff --git a/server/src/dtos/audit.dto.ts b/server/src/dtos/audit.dto.ts deleted file mode 100644 index 434da46eba..0000000000 --- a/server/src/dtos/audit.dto.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsArray, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator'; -import { AssetPathType, EntityType, PathType, PersonPathType, UserPathType } from 'src/enum'; -import { Optional, ValidateDate, ValidateUUID } from 'src/validation'; - -const PathEnum = Object.values({ ...AssetPathType, ...PersonPathType, ...UserPathType }); - -export class AuditDeletesDto { - @ValidateDate() - after!: Date; - - @ApiProperty({ enum: EntityType, enumName: 'EntityType' }) - @IsEnum(EntityType) - entityType!: EntityType; - - @Optional() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) - userId?: string; -} - -export enum PathEntityType { - ASSET = 'asset', - PERSON = 'person', - USER = 'user', -} - -export class AuditDeletesResponseDto { - needsFullSync!: boolean; - ids!: string[]; -} - -export class FileReportDto { - orphans!: FileReportItemDto[]; - extras!: string[]; -} - -export class FileChecksumDto { - @IsString({ each: true }) - filenames!: string[]; -} - -export class FileChecksumResponseDto { - filename!: string; - checksum!: string; -} - -export class FileReportFixDto { - @IsArray() - @ValidateNested({ each: true }) - @Type(() => FileReportItemDto) - items!: FileReportItemDto[]; -} - -// used both as request and response dto -export class FileReportItemDto { - @ValidateUUID() - entityId!: string; - - @ApiProperty({ enumName: 'PathEntityType', enum: PathEntityType }) - @IsEnum(PathEntityType) - entityType!: PathEntityType; - - @ApiProperty({ enumName: 'PathType', enum: PathEnum }) - @IsEnum(PathEnum) - pathType!: PathType; - - @IsString() - pathValue!: string; - - checksum?: string; -} diff --git a/server/src/dtos/auth.dto.ts b/server/src/dtos/auth.dto.ts index 7f2ffa5878..2f3ae5c14b 100644 --- a/server/src/dtos/auth.dto.ts +++ b/server/src/dtos/auth.dto.ts @@ -3,11 +3,11 @@ import { Transform } from 'class-transformer'; import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator'; import { AuthApiKey, AuthSession, AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { ImmichCookie } from 'src/enum'; -import { toEmail } from 'src/validation'; +import { Optional, PinCode, toEmail } from 'src/validation'; export type CookieResponse = { isSecure: boolean; - values: Array<{ key: ImmichCookie; value: string }>; + values: Array<{ key: ImmichCookie; value: string | null }>; }; export class AuthDto { @@ -78,6 +78,28 @@ export class ChangePasswordDto { newPassword!: string; } +export class PinCodeSetupDto { + @PinCode() + pinCode!: string; +} + +export class PinCodeResetDto { + @PinCode({ optional: true }) + pinCode?: string; + + @Optional() + @IsString() + @IsNotEmpty() + password?: string; +} + +export class SessionUnlockDto extends PinCodeResetDto {} + +export class PinCodeChangeDto extends PinCodeResetDto { + @PinCode() + newPinCode!: string; +} + export class ValidateAccessTokenResponseDto { authStatus!: boolean; } @@ -87,14 +109,38 @@ export class OAuthCallbackDto { @IsString() @ApiProperty() url!: string; + + @Optional() + @IsString() + state?: string; + + @Optional() + @IsString() + codeVerifier?: string; } export class OAuthConfigDto { @IsNotEmpty() @IsString() redirectUri!: string; + + @Optional() + @IsString() + state?: string; + + @Optional() + @IsString() + codeChallenge?: string; } export class OAuthAuthorizeResponseDto { url!: string; } + +export class AuthStatusResponseDto { + pinCode!: boolean; + password!: boolean; + isElevated!: boolean; + expiresAt?: string; + pinExpiresAt?: string; +} diff --git a/server/src/dtos/env.dto.ts b/server/src/dtos/env.dto.ts index 6c238252a6..99fd1d2149 100644 --- a/server/src/dtos/env.dto.ts +++ b/server/src/dtos/env.dto.ts @@ -1,6 +1,6 @@ import { Transform, Type } from 'class-transformer'; import { IsEnum, IsInt, IsString } from 'class-validator'; -import { ImmichEnvironment, LogLevel } from 'src/enum'; +import { DatabaseSslMode, ImmichEnvironment, LogLevel } from 'src/enum'; import { IsIPRange, Optional, ValidateBoolean } from 'src/validation'; export class EnvDto { @@ -142,6 +142,10 @@ export class EnvDto { @ValidateBoolean({ optional: true }) DB_SKIP_MIGRATIONS?: boolean; + @IsEnum(DatabaseSslMode) + @Optional() + DB_SSL_MODE?: DatabaseSslMode; + @IsString() @Optional() DB_URL?: string; @@ -150,9 +154,9 @@ export class EnvDto { @Optional() DB_USERNAME?: string; - @IsEnum(['pgvector', 'pgvecto.rs']) + @IsEnum(['pgvector', 'pgvecto.rs', 'vectorchord']) @Optional() - DB_VECTOR_EXTENSION?: 'pgvector' | 'pgvecto.rs'; + DB_VECTOR_EXTENSION?: 'pgvector' | 'pgvecto.rs' | 'vectorchord'; @IsString() @Optional() diff --git a/server/src/dtos/memory.dto.ts b/server/src/dtos/memory.dto.ts index b3054d7a4c..98231a9035 100644 --- a/server/src/dtos/memory.dto.ts +++ b/server/src/dtos/memory.dto.ts @@ -4,7 +4,6 @@ import { IsEnum, IsInt, IsObject, IsPositive, ValidateNested } from 'class-valid import { Memory } from 'src/database'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; import { MemoryType } from 'src/enum'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; @@ -103,6 +102,6 @@ export const mapMemory = (entity: Memory, auth: AuthDto): MemoryResponseDto => { type: entity.type as MemoryType, data: entity.data as unknown as MemoryData, isSaved: entity.isSaved, - assets: ('assets' in entity ? entity.assets : []).map((asset) => mapAsset(asset as AssetEntity, { auth })), + assets: ('assets' in entity ? entity.assets : []).map((asset) => mapAsset(asset, { auth })), }; }; diff --git a/server/src/dtos/notification.dto.ts b/server/src/dtos/notification.dto.ts index c1a09c801c..d9847cda17 100644 --- a/server/src/dtos/notification.dto.ts +++ b/server/src/dtos/notification.dto.ts @@ -1,4 +1,7 @@ -import { IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; export class TestEmailResponseDto { messageId!: string; @@ -11,3 +14,106 @@ export class TemplateDto { @IsString() template!: string; } + +export class NotificationDto { + id!: string; + @ValidateDate() + createdAt!: Date; + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level!: NotificationLevel; + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type!: NotificationType; + title!: string; + description?: string; + data?: any; + readAt?: Date; +} + +export class NotificationSearchDto { + @Optional() + @ValidateUUID({ optional: true }) + id?: string; + + @IsEnum(NotificationLevel) + @Optional() + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level?: NotificationLevel; + + @IsEnum(NotificationType) + @Optional() + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type?: NotificationType; + + @ValidateBoolean({ optional: true }) + unread?: boolean; +} + +export class NotificationCreateDto { + @Optional() + @IsEnum(NotificationLevel) + @ApiProperty({ enum: NotificationLevel, enumName: 'NotificationLevel' }) + level?: NotificationLevel; + + @IsEnum(NotificationType) + @Optional() + @ApiProperty({ enum: NotificationType, enumName: 'NotificationType' }) + type?: NotificationType; + + @IsString() + title!: string; + + @IsString() + @Optional({ nullable: true }) + description?: string | null; + + @Optional({ nullable: true }) + data?: any; + + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; + + @ValidateUUID() + userId!: string; +} + +export class NotificationUpdateDto { + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; +} + +export class NotificationUpdateAllDto { + @ValidateUUID({ each: true, optional: true }) + ids!: string[]; + + @ValidateDate({ optional: true, nullable: true }) + readAt?: Date | null; +} + +export class NotificationDeleteAllDto { + @ValidateUUID({ each: true }) + ids!: string[]; +} + +export type MapNotification = { + id: string; + createdAt: Date; + updateId?: string; + level: NotificationLevel; + type: NotificationType; + data: any | null; + title: string; + description: string | null; + readAt: Date | null; +}; +export const mapNotification = (notification: MapNotification): NotificationDto => { + return { + id: notification.id, + createdAt: notification.createdAt, + level: notification.level, + type: notification.type, + title: notification.title, + description: notification.description ?? undefined, + data: notification.data ?? undefined, + readAt: notification.readAt ?? undefined, + }; +}; diff --git a/server/src/dtos/person.dto.ts b/server/src/dtos/person.dto.ts index 90490715ef..c59ab905bd 100644 --- a/server/src/dtos/person.dto.ts +++ b/server/src/dtos/person.dto.ts @@ -33,7 +33,7 @@ export class PersonCreateDto { @ApiProperty({ format: 'date' }) @MaxDateString(() => DateTime.now(), { message: 'Birth date cannot be in the future' }) @IsDateStringFormat('yyyy-MM-dd') - @Optional({ nullable: true }) + @Optional({ nullable: true, emptyToNull: true }) birthDate?: Date | null; /** @@ -54,8 +54,7 @@ export class PersonUpdateDto extends PersonCreateDto { /** * Asset is used to get the feature face thumbnail. */ - @Optional() - @IsString() + @ValidateUUID({ optional: true }) featureFaceAssetId?: string; } diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index a7633dce78..579cba680e 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -5,8 +5,8 @@ import { Place } from 'src/database'; import { PropertyLifecycle } from 'src/decorators'; import { AlbumResponseDto } from 'src/dtos/album.dto'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { AssetOrder, AssetType } from 'src/enum'; -import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; +import { AssetOrder, AssetType, AssetVisibility } from 'src/enum'; +import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; class BaseSearchDto { @ValidateUUID({ optional: true, nullable: true }) @@ -22,13 +22,6 @@ class BaseSearchDto { @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type?: AssetType; - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - - @ValidateBoolean({ optional: true }) - @ApiProperty({ default: false }) - withArchived?: boolean; - @ValidateBoolean({ optional: true }) isEncoded?: boolean; @@ -41,8 +34,8 @@ class BaseSearchDto { @ValidateBoolean({ optional: true }) isOffline?: boolean; - @ValidateBoolean({ optional: true }) - isVisible?: boolean; + @ValidateAssetVisibility({ optional: true }) + visibility?: AssetVisibility; @ValidateBoolean({ optional: true }) withDeleted?: boolean; diff --git a/server/src/dtos/session.dto.ts b/server/src/dtos/session.dto.ts index b54264a5b4..f15166fbf5 100644 --- a/server/src/dtos/session.dto.ts +++ b/server/src/dtos/session.dto.ts @@ -1,18 +1,44 @@ +import { IsInt, IsPositive, IsString } from 'class-validator'; import { Session } from 'src/database'; +import { Optional } from 'src/validation'; + +export class SessionCreateDto { + /** + * session duration, in seconds + */ + @IsInt() + @IsPositive() + @Optional() + duration?: number; + + @IsString() + @Optional() + deviceType?: string; + + @IsString() + @Optional() + deviceOS?: string; +} export class SessionResponseDto { id!: string; createdAt!: string; updatedAt!: string; + expiresAt?: string; current!: boolean; deviceType!: string; deviceOS!: string; } +export class SessionCreateResponseDto extends SessionResponseDto { + token!: string; +} + export const mapSession = (entity: Session, currentId?: string): SessionResponseDto => ({ id: entity.id, createdAt: entity.createdAt.toISOString(), updatedAt: entity.updatedAt.toISOString(), + expiresAt: entity.expiresAt?.toISOString(), current: currentId === entity.id, deviceOS: entity.deviceOS, deviceType: entity.deviceType, diff --git a/server/src/dtos/shared-link.dto.ts b/server/src/dtos/shared-link.dto.ts index 6bb8ab1f0d..8d373b40b6 100644 --- a/server/src/dtos/shared-link.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -1,9 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsString } from 'class-validator'; import _ from 'lodash'; +import { SharedLink } from 'src/database'; import { AlbumResponseDto, mapAlbumWithoutAssets } from 'src/dtos/album.dto'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { SharedLinkType } from 'src/enum'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; @@ -102,7 +102,7 @@ export class SharedLinkResponseDto { showMetadata!: boolean; } -export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseDto { +export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { const linkAssets = sharedLink.assets || []; return { @@ -122,7 +122,7 @@ export function mapSharedLink(sharedLink: SharedLinkEntity): SharedLinkResponseD }; } -export function mapSharedLinkWithoutMetadata(sharedLink: SharedLinkEntity): SharedLinkResponseDto { +export function mapSharedLinkWithoutMetadata(sharedLink: SharedLink): SharedLinkResponseDto { const linkAssets = sharedLink.assets || []; const albumAssets = (sharedLink?.album?.assets || []).map((asset) => asset); @@ -137,7 +137,7 @@ export function mapSharedLinkWithoutMetadata(sharedLink: SharedLinkEntity): Shar type: sharedLink.type, createdAt: sharedLink.createdAt, expiresAt: sharedLink.expiresAt, - assets: assets.map((asset) => mapAsset(asset, { stripMetadata: true })) as AssetResponseDto[], + assets: assets.map((asset) => mapAsset(asset, { stripMetadata: true })), album: sharedLink.album ? mapAlbumWithoutAssets(sharedLink.album) : undefined, allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index a035f8ecb9..0043cfb40b 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsInt, IsPositive, IsString } from 'class-validator'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { AssetType, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AlbumUserRole, AssetOrder, AssetType, AssetVisibility, SyncEntityType, SyncRequestType } from 'src/enum'; import { Optional, ValidateDate, ValidateUUID } from 'src/validation'; export class AssetFullSyncDto { @@ -67,7 +67,7 @@ export class SyncAssetV1 { type!: AssetType; deletedAt!: Date | null; isFavorite!: boolean; - isVisible!: boolean; + visibility!: AssetVisibility; } export class SyncAssetDeleteV1 { @@ -112,6 +112,34 @@ export class SyncAssetExifV1 { fps!: number | null; } +export class SyncAlbumDeleteV1 { + albumId!: string; +} + +export class SyncAlbumUserDeleteV1 { + albumId!: string; + userId!: string; +} + +export class SyncAlbumUserV1 { + albumId!: string; + userId!: string; + role!: AlbumUserRole; +} + +export class SyncAlbumV1 { + id!: string; + ownerId!: string; + name!: string; + description!: string; + createdAt!: Date; + updatedAt!: Date; + thumbnailAssetId!: string | null; + isActivityEnabled!: boolean; + @ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder }) + order!: AssetOrder; +} + export type SyncItem = { [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; @@ -123,10 +151,13 @@ export type SyncItem = { [SyncEntityType.PartnerAssetV1]: SyncAssetV1; [SyncEntityType.PartnerAssetDeleteV1]: SyncAssetDeleteV1; [SyncEntityType.PartnerAssetExifV1]: SyncAssetExifV1; + [SyncEntityType.AlbumV1]: SyncAlbumV1; + [SyncEntityType.AlbumDeleteV1]: SyncAlbumDeleteV1; + [SyncEntityType.AlbumUserV1]: SyncAlbumUserV1; + [SyncEntityType.AlbumUserDeleteV1]: SyncAlbumUserDeleteV1; }; const responseDtos = [ - // SyncUserV1, SyncUserDeleteV1, SyncPartnerV1, @@ -134,6 +165,10 @@ const responseDtos = [ SyncAssetV1, SyncAssetDeleteV1, SyncAssetExifV1, + SyncAlbumV1, + SyncAlbumDeleteV1, + SyncAlbumUserV1, + SyncAlbumUserDeleteV1, ]; export const extraSyncModels = responseDtos; diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index eaef40a5e1..6991baf109 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -25,6 +25,7 @@ import { Colorspace, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -33,7 +34,7 @@ import { VideoContainer, } from 'src/enum'; import { ConcurrentQueueName } from 'src/types'; -import { IsCronExpression, ValidateBoolean } from 'src/validation'; +import { IsCronExpression, Optional, ValidateBoolean } from 'src/validation'; const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled; const isOAuthEnabled = (config: SystemConfigOAuthDto) => config.enabled; @@ -344,10 +345,19 @@ class SystemConfigOAuthDto { clientId!: string; @ValidateIf(isOAuthEnabled) - @IsNotEmpty() @IsString() clientSecret!: string; + @IsEnum(OAuthTokenEndpointAuthMethod) + @ApiProperty({ enum: OAuthTokenEndpointAuthMethod, enumName: 'OAuthTokenEndpointAuthMethod' }) + tokenEndpointAuthMethod!: OAuthTokenEndpointAuthMethod; + + @IsInt() + @IsPositive() + @Optional() + @ApiProperty({ type: 'integer' }) + timeout!: number; + @IsNumber() @Min(0) defaultStorageQuota!: number; diff --git a/server/src/dtos/system-metadata.dto.ts b/server/src/dtos/system-metadata.dto.ts index 1c04435341..c8e64f2300 100644 --- a/server/src/dtos/system-metadata.dto.ts +++ b/server/src/dtos/system-metadata.dto.ts @@ -13,3 +13,8 @@ export class ReverseGeocodingStateResponseDto { lastUpdate!: string | null; lastImportFileName!: string | null; } + +export class VersionCheckStateResponseDto { + checkedAt!: string | null; + releaseVersion!: string | null; +} diff --git a/server/src/dtos/time-bucket.dto.ts b/server/src/dtos/time-bucket.dto.ts index a9dfa49a07..f68ce93075 100644 --- a/server/src/dtos/time-bucket.dto.ts +++ b/server/src/dtos/time-bucket.dto.ts @@ -1,15 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { AssetOrder } from 'src/enum'; -import { TimeBucketSize } from 'src/repositories/asset.repository'; -import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; + +import { IsEnum, IsInt, IsString, Min } from 'class-validator'; +import { AssetOrder, AssetVisibility } from 'src/enum'; +import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateUUID } from 'src/validation'; export class TimeBucketDto { - @IsNotEmpty() - @IsEnum(TimeBucketSize) - @ApiProperty({ enum: TimeBucketSize, enumName: 'TimeBucketSize' }) - size!: TimeBucketSize; - @ValidateUUID({ optional: true }) userId?: string; @@ -22,9 +17,6 @@ export class TimeBucketDto { @ValidateUUID({ optional: true }) tagId?: string; - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - @ValidateBoolean({ optional: true }) isFavorite?: boolean; @@ -41,14 +33,83 @@ export class TimeBucketDto { @Optional() @ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' }) order?: AssetOrder; + + @ValidateAssetVisibility({ optional: true }) + visibility?: AssetVisibility; } export class TimeBucketAssetDto extends TimeBucketDto { @IsString() timeBucket!: string; + + @IsInt() + @Min(1) + @Optional() + page?: number; + + @IsInt() + @Min(1) + @Optional() + pageSize?: number; } -export class TimeBucketResponseDto { +export class TimelineStackResponseDto { + id!: string; + primaryAssetId!: string; + assetCount!: number; +} + +export class TimeBucketAssetResponseDto { + id!: string[]; + + ownerId!: string[]; + + ratio!: number[]; + + isFavorite!: boolean[]; + + @ApiProperty({ enum: AssetVisibility, enumName: 'AssetVisibility', isArray: true }) + visibility!: AssetVisibility[]; + + isTrashed!: boolean[]; + + isImage!: boolean[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + thumbhash!: (string | null)[]; + + localDateTime!: string[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + duration!: (string | null)[]; + + @ApiProperty({ + type: 'array', + items: { + type: 'array', + items: { type: 'string' }, + minItems: 2, + maxItems: 2, + nullable: true, + }, + description: '(stack ID, stack asset count) tuple', + }) + stack?: ([string, string] | null)[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + projectionType!: (string | null)[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + livePhotoVideoId!: (string | null)[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + city!: (string | null)[]; + + @ApiProperty({ type: 'array', items: { type: 'string', nullable: true } }) + country!: (string | null)[]; +} + +export class TimeBucketsResponseDto { @ApiProperty({ type: 'string' }) timeBucket!: string; diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts index fe92838fdb..a9d32523ae 100644 --- a/server/src/dtos/user-preferences.dto.ts +++ b/server/src/dtos/user-preferences.dto.ts @@ -137,11 +137,6 @@ export class UserPreferencesUpdateDto { purchase?: PurchaseUpdate; } -class AvatarResponse { - @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) - color!: UserAvatarColor; -} - class RatingsResponse { enabled: boolean = false; } @@ -195,7 +190,6 @@ export class UserPreferencesResponseDto implements UserPreferences { ratings!: RatingsResponse; sharedLinks!: SharedLinksResponse; tags!: TagsResponse; - avatar!: AvatarResponse; emailNotifications!: EmailNotificationsResponse; download!: DownloadResponse; purchase!: PurchaseResponse; diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts index 72e5c83b35..9d43e53f89 100644 --- a/server/src/dtos/user.dto.ts +++ b/server/src/dtos/user.dto.ts @@ -1,11 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsEmail, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator'; +import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator'; import { User, UserAdmin } from 'src/database'; import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum'; import { UserMetadataItem } from 'src/types'; -import { getPreferences } from 'src/utils/preferences'; -import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation'; +import { Optional, PinCode, ValidateBoolean, ValidateUUID, toEmail, toSanitized } from 'src/validation'; export class UserUpdateMeDto { @Optional() @@ -23,6 +22,11 @@ export class UserUpdateMeDto { @IsString() @IsNotEmpty() name?: string; + + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; } export class UserResponseDto { @@ -41,13 +45,21 @@ export class UserLicense { activatedAt!: Date; } +const emailToAvatarColor = (email: string): UserAvatarColor => { + const values = Object.values(UserAvatarColor); + const randomIndex = Math.floor( + [...email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, + ); + return values[randomIndex]; +}; + export const mapUser = (entity: User | UserAdmin): UserResponseDto => { return { id: entity.id, email: entity.email, name: entity.name, profileImagePath: entity.profileImagePath, - avatarColor: getPreferences(entity.email, (entity as UserAdmin).metadata || []).avatar.color, + avatarColor: entity.avatarColor ?? emailToAvatarColor(entity.email), profileChangedAt: entity.profileChangedAt, }; }; @@ -55,6 +67,9 @@ export const mapUser = (entity: User | UserAdmin): UserResponseDto => { export class UserAdminSearchDto { @ValidateBoolean({ optional: true }) withDeleted?: boolean; + + @ValidateUUID({ optional: true }) + id?: string; } export class UserAdminCreateDto { @@ -69,6 +84,11 @@ export class UserAdminCreateDto { @IsString() name!: string; + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; + @Optional({ nullable: true }) @IsString() @Transform(toSanitized) @@ -99,11 +119,19 @@ export class UserAdminUpdateDto { @IsString() password?: string; + @PinCode({ optional: true, nullable: true, emptyToNull: true }) + pinCode?: string | null; + @Optional() @IsString() @IsNotEmpty() name?: string; + @Optional({ nullable: true }) + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor | null; + @Optional({ nullable: true }) @IsString() @Transform(toSanitized) diff --git a/server/src/emails/album-invite.email.tsx b/server/src/emails/album-invite.email.tsx index 4bd7abc305..fdc189af97 100644 --- a/server/src/emails/album-invite.email.tsx +++ b/server/src/emails/album-invite.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumInviteEmailProps } from 'src/repositories/notification.repository'; +import { AlbumInviteEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumInviteEmail = ({ diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index 2311e896e1..3bed3a5b36 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -2,7 +2,7 @@ import { Img, Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { AlbumUpdateEmailProps } from 'src/repositories/notification.repository'; +import { AlbumUpdateEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const AlbumUpdateEmail = ({ diff --git a/server/src/emails/test.email.tsx b/server/src/emails/test.email.tsx index ac9bdbe0ea..0d87307080 100644 --- a/server/src/emails/test.email.tsx +++ b/server/src/emails/test.email.tsx @@ -1,7 +1,7 @@ import { Link, Row, Text } from '@react-email/components'; import * as React from 'react'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { TestEmailProps } from 'src/repositories/notification.repository'; +import { TestEmailProps } from 'src/repositories/email.repository'; export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => ( diff --git a/server/src/emails/welcome.email.tsx b/server/src/emails/welcome.email.tsx index 11a6602711..57e86ab252 100644 --- a/server/src/emails/welcome.email.tsx +++ b/server/src/emails/welcome.email.tsx @@ -2,7 +2,7 @@ import { Link, Section, Text } from '@react-email/components'; import * as React from 'react'; import { ImmichButton } from 'src/emails/components/button.component'; import ImmichLayout from 'src/emails/components/immich.layout'; -import { WelcomeEmailProps } from 'src/repositories/notification.repository'; +import { WelcomeEmailProps } from 'src/repositories/email.repository'; import { replaceTemplateTags } from 'src/utils/replace-template-tags'; export const WelcomeEmail = ({ baseUrl, displayName, username, password, customTemplate }: WelcomeEmailProps) => { diff --git a/server/src/entities/album.entity.ts b/server/src/entities/album.entity.ts deleted file mode 100644 index eb20c1afdd..0000000000 --- a/server/src/entities/album.entity.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AlbumUser, User } from 'src/database'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; -import { AssetOrder } from 'src/enum'; - -export class AlbumEntity { - id!: string; - owner!: User; - ownerId!: string; - albumName!: string; - description!: string; - createdAt!: Date; - updatedAt!: Date; - updateId?: string; - deletedAt!: Date | null; - albumThumbnailAsset!: AssetEntity | null; - albumThumbnailAssetId!: string | null; - albumUsers!: AlbumUser[]; - assets!: AssetEntity[]; - sharedLinks!: SharedLinkEntity[]; - isActivityEnabled!: boolean; - order!: AssetOrder; -} diff --git a/server/src/entities/asset.entity.ts b/server/src/entities/asset.entity.ts deleted file mode 100644 index 64c038a689..0000000000 --- a/server/src/entities/asset.entity.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely'; -import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; -import { AssetFace, AssetFile, AssetJobStatus, columns, Exif, Stack, Tag, User } from 'src/database'; -import { DB } from 'src/db'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; -import { AssetFileType, AssetStatus, AssetType } from 'src/enum'; -import { TimeBucketSize } from 'src/repositories/asset.repository'; -import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; -import { anyUuid, asUuid } from 'src/utils/database'; - -export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; - -export class AssetEntity { - id!: string; - deviceAssetId!: string; - owner!: User; - ownerId!: string; - libraryId?: string | null; - deviceId!: string; - type!: AssetType; - status!: AssetStatus; - originalPath!: string; - files!: AssetFile[]; - thumbhash!: Buffer | null; - encodedVideoPath!: string | null; - createdAt!: Date; - updatedAt!: Date; - updateId?: string; - deletedAt!: Date | null; - fileCreatedAt!: Date; - localDateTime!: Date; - fileModifiedAt!: Date; - isFavorite!: boolean; - isArchived!: boolean; - isExternal!: boolean; - isOffline!: boolean; - checksum!: Buffer; // sha1 checksum - duration!: string | null; - isVisible!: boolean; - livePhotoVideo!: AssetEntity | null; - livePhotoVideoId!: string | null; - originalFileName!: string; - sidecarPath!: string | null; - exifInfo?: Exif; - tags?: Tag[]; - sharedLinks!: SharedLinkEntity[]; - faces!: AssetFace[]; - stackId?: string | null; - stack?: Stack | null; - jobStatus?: AssetJobStatus; - duplicateId!: string | null; -} - -export function withExif(qb: SelectQueryBuilder) { - return qb - .leftJoin('exif', 'assets.id', 'exif.assetId') - .select((eb) => eb.fn.toJson(eb.table('exif')).$castTo().as('exifInfo')); -} - -export function withExifInner(qb: SelectQueryBuilder) { - return qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .select((eb) => eb.fn.toJson(eb.table('exif')).$castTo().as('exifInfo')); -} - -export function withSmartSearch(qb: SelectQueryBuilder) { - return qb - .leftJoin('smart_search', 'assets.id', 'smart_search.assetId') - .select((eb) => eb.fn.toJson(eb.table('smart_search')).as('smartSearch')); -} - -export function withFaces(eb: ExpressionBuilder, withDeletedFace?: boolean) { - return jsonArrayFrom( - eb - .selectFrom('asset_faces') - .selectAll('asset_faces') - .whereRef('asset_faces.assetId', '=', 'assets.id') - .$if(!withDeletedFace, (qb) => qb.where('asset_faces.deletedAt', 'is', null)), - ).as('faces'); -} - -export function withFiles(eb: ExpressionBuilder, type?: AssetFileType) { - return jsonArrayFrom( - eb - .selectFrom('asset_files') - .select(columns.assetFiles) - .whereRef('asset_files.assetId', '=', 'assets.id') - .$if(!!type, (qb) => qb.where('asset_files.type', '=', type!)), - ).as('files'); -} - -export function withFacesAndPeople(eb: ExpressionBuilder, withDeletedFace?: boolean) { - return jsonArrayFrom( - eb - .selectFrom('asset_faces') - .leftJoinLateral( - (eb) => - eb.selectFrom('person').selectAll('person').whereRef('asset_faces.personId', '=', 'person.id').as('person'), - (join) => join.onTrue(), - ) - .selectAll('asset_faces') - .select((eb) => eb.table('person').as('person')) - .whereRef('asset_faces.assetId', '=', 'assets.id') - .$if(!withDeletedFace, (qb) => qb.where('asset_faces.deletedAt', 'is', null)), - ).as('faces'); -} - -export function hasPeople(qb: SelectQueryBuilder, personIds: string[]) { - return qb.innerJoin( - (eb) => - eb - .selectFrom('asset_faces') - .select('assetId') - .where('personId', '=', anyUuid(personIds!)) - .where('deletedAt', 'is', null) - .groupBy('assetId') - .having((eb) => eb.fn.count('personId').distinct(), '=', personIds.length) - .as('has_people'), - (join) => join.onRef('has_people.assetId', '=', 'assets.id'), - ); -} - -export function hasTags(qb: SelectQueryBuilder, tagIds: string[]) { - return qb.innerJoin( - (eb) => - eb - .selectFrom('tag_asset') - .select('assetsId') - .innerJoin('tags_closure', 'tag_asset.tagsId', 'tags_closure.id_descendant') - .where('tags_closure.id_ancestor', '=', anyUuid(tagIds)) - .groupBy('assetsId') - .having((eb) => eb.fn.count('tags_closure.id_ancestor').distinct(), '>=', tagIds.length) - .as('has_tags'), - (join) => join.onRef('has_tags.assetsId', '=', 'assets.id'), - ); -} - -export function withOwner(eb: ExpressionBuilder) { - return jsonObjectFrom(eb.selectFrom('users').selectAll().whereRef('users.id', '=', 'assets.ownerId')).as('owner'); -} - -export function withLibrary(eb: ExpressionBuilder) { - return jsonObjectFrom(eb.selectFrom('libraries').selectAll().whereRef('libraries.id', '=', 'assets.libraryId')).as( - 'library', - ); -} - -export function withTags(eb: ExpressionBuilder) { - return jsonArrayFrom( - eb - .selectFrom('tags') - .select(columns.tag) - .innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId') - .whereRef('assets.id', '=', 'tag_asset.assetsId'), - ).as('tags'); -} - -export function truncatedDate(size: TimeBucketSize) { - return sql`date_trunc(${size}, "localDateTime" at time zone 'UTC') at time zone 'UTC'`; -} - -export function withTagId(qb: SelectQueryBuilder, tagId: string) { - return qb.where((eb) => - eb.exists( - eb - .selectFrom('tags_closure') - .innerJoin('tag_asset', 'tag_asset.tagsId', 'tags_closure.id_descendant') - .whereRef('tag_asset.assetsId', '=', 'assets.id') - .where('tags_closure.id_ancestor', '=', tagId), - ), - ); -} - -const joinDeduplicationPlugin = new DeduplicateJoinsPlugin(); - -/** TODO: This should only be used for search-related queries, not as a general purpose query builder */ -export function searchAssetBuilder(kysely: Kysely, options: AssetSearchBuilderOptions) { - options.isArchived ??= options.withArchived ? undefined : false; - options.withDeleted ||= !!(options.trashedAfter || options.trashedBefore || options.isOffline); - return kysely - .withPlugin(joinDeduplicationPlugin) - .selectFrom('assets') - .selectAll('assets') - .$if(!!options.tagIds && options.tagIds.length > 0, (qb) => hasTags(qb, options.tagIds!)) - .$if(!!options.personIds && options.personIds.length > 0, (qb) => hasPeople(qb, options.personIds!)) - .$if(!!options.createdBefore, (qb) => qb.where('assets.createdAt', '<=', options.createdBefore!)) - .$if(!!options.createdAfter, (qb) => qb.where('assets.createdAt', '>=', options.createdAfter!)) - .$if(!!options.updatedBefore, (qb) => qb.where('assets.updatedAt', '<=', options.updatedBefore!)) - .$if(!!options.updatedAfter, (qb) => qb.where('assets.updatedAt', '>=', options.updatedAfter!)) - .$if(!!options.trashedBefore, (qb) => qb.where('assets.deletedAt', '<=', options.trashedBefore!)) - .$if(!!options.trashedAfter, (qb) => qb.where('assets.deletedAt', '>=', options.trashedAfter!)) - .$if(!!options.takenBefore, (qb) => qb.where('assets.fileCreatedAt', '<=', options.takenBefore!)) - .$if(!!options.takenAfter, (qb) => qb.where('assets.fileCreatedAt', '>=', options.takenAfter!)) - .$if(options.city !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.city', options.city === null ? 'is' : '=', options.city!), - ) - .$if(options.state !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.state', options.state === null ? 'is' : '=', options.state!), - ) - .$if(options.country !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.country', options.country === null ? 'is' : '=', options.country!), - ) - .$if(options.make !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.make', options.make === null ? 'is' : '=', options.make!), - ) - .$if(options.model !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.model', options.model === null ? 'is' : '=', options.model!), - ) - .$if(options.lensModel !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.lensModel', options.lensModel === null ? 'is' : '=', options.lensModel!), - ) - .$if(options.rating !== undefined, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where('exif.rating', options.rating === null ? 'is' : '=', options.rating!), - ) - .$if(!!options.checksum, (qb) => qb.where('assets.checksum', '=', options.checksum!)) - .$if(!!options.deviceAssetId, (qb) => qb.where('assets.deviceAssetId', '=', options.deviceAssetId!)) - .$if(!!options.deviceId, (qb) => qb.where('assets.deviceId', '=', options.deviceId!)) - .$if(!!options.id, (qb) => qb.where('assets.id', '=', asUuid(options.id!))) - .$if(!!options.libraryId, (qb) => qb.where('assets.libraryId', '=', asUuid(options.libraryId!))) - .$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!))) - .$if(!!options.encodedVideoPath, (qb) => qb.where('assets.encodedVideoPath', '=', options.encodedVideoPath!)) - .$if(!!options.originalPath, (qb) => - qb.where(sql`f_unaccent(assets."originalPath")`, 'ilike', sql`'%' || f_unaccent(${options.originalPath}) || '%'`), - ) - .$if(!!options.originalFileName, (qb) => - qb.where( - sql`f_unaccent(assets."originalFileName")`, - 'ilike', - sql`'%' || f_unaccent(${options.originalFileName}) || '%'`, - ), - ) - .$if(!!options.description, (qb) => - qb - .innerJoin('exif', 'assets.id', 'exif.assetId') - .where(sql`f_unaccent(exif.description)`, 'ilike', sql`'%' || f_unaccent(${options.description}) || '%'`), - ) - .$if(!!options.type, (qb) => qb.where('assets.type', '=', options.type!)) - .$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!)) - .$if(options.isOffline !== undefined, (qb) => qb.where('assets.isOffline', '=', options.isOffline!)) - .$if(options.isVisible !== undefined, (qb) => qb.where('assets.isVisible', '=', options.isVisible!)) - .$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!)) - .$if(options.isEncoded !== undefined, (qb) => - qb.where('assets.encodedVideoPath', options.isEncoded ? 'is not' : 'is', null), - ) - .$if(options.isMotion !== undefined, (qb) => - qb.where('assets.livePhotoVideoId', options.isMotion ? 'is not' : 'is', null), - ) - .$if(!!options.isNotInAlbum, (qb) => - qb.where((eb) => - eb.not(eb.exists((eb) => eb.selectFrom('albums_assets_assets').whereRef('assetsId', '=', 'assets.id'))), - ), - ) - .$if(!!options.withExif, withExifInner) - .$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople)) - .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)); -} diff --git a/server/src/entities/shared-link.entity.ts b/server/src/entities/shared-link.entity.ts deleted file mode 100644 index 720ba424d1..0000000000 --- a/server/src/entities/shared-link.entity.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AlbumEntity } from 'src/entities/album.entity'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { SharedLinkType } from 'src/enum'; - -export class SharedLinkEntity { - id!: string; - description!: string | null; - password!: string | null; - userId!: string; - key!: Buffer; // use to access the inidividual asset - type!: SharedLinkType; - createdAt!: Date; - expiresAt!: Date | null; - allowUpload!: boolean; - allowDownload!: boolean; - showExif!: boolean; - assets!: AssetEntity[]; - album?: AlbumEntity; - albumId!: string | null; -} diff --git a/server/src/enum.ts b/server/src/enum.ts index e5c6039be8..b00b013393 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -8,6 +8,8 @@ export enum ImmichCookie { AUTH_TYPE = 'immich_auth_type', IS_AUTHENTICATED = 'immich_is_authenticated', SHARED_LINK_TOKEN = 'immich_shared_link_token', + OAUTH_STATE = 'immich_oauth_state', + OAUTH_CODE_VERIFIER = 'immich_oauth_code_verifier', } export enum ImmichHeader { @@ -124,6 +126,11 @@ export enum Permission { MEMORY_UPDATE = 'memory.update', MEMORY_DELETE = 'memory.delete', + NOTIFICATION_CREATE = 'notification.create', + NOTIFICATION_READ = 'notification.read', + NOTIFICATION_UPDATE = 'notification.update', + NOTIFICATION_DELETE = 'notification.delete', + PARTNER_CREATE = 'partner.create', PARTNER_READ = 'partner.read', PARTNER_UPDATE = 'partner.update', @@ -137,9 +144,11 @@ export enum Permission { PERSON_MERGE = 'person.merge', PERSON_REASSIGN = 'person.reassign', + SESSION_CREATE = 'session.create', SESSION_READ = 'session.read', SESSION_UPDATE = 'session.update', SESSION_DELETE = 'session.delete', + SESSION_LOCK = 'session.lock', SHARED_LINK_CREATE = 'sharedLink.create', SHARED_LINK_READ = 'sharedLink.read', @@ -330,6 +339,11 @@ export enum ImageFormat { WEBP = 'webp', } +export enum RawExtractedFormat { + JPEG = 'jpeg', + JXL = 'jxl', +} + export enum LogLevel { VERBOSE = 'verbose', DEBUG = 'debug', @@ -400,11 +414,14 @@ export enum DatabaseExtension { EARTH_DISTANCE = 'earthdistance', VECTOR = 'vector', VECTORS = 'vectors', + VECTORCHORD = 'vchord', } export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Other services may need to queue jobs on bootstrap. + JobService = -190, // Initialise config after other bootstrap services, stop other services from using config on bootstrap SystemConfig = 100, } @@ -511,6 +528,7 @@ export enum JobName { NOTIFY_SIGNUP = 'notify-signup', NOTIFY_ALBUM_INVITE = 'notify-album-invite', NOTIFY_ALBUM_UPDATE = 'notify-album-update', + NOTIFICATIONS_CLEANUP = 'notifications-cleanup', SEND_EMAIL = 'notification-send-email', // Version check @@ -550,6 +568,7 @@ export enum DatabaseLock { Library = 1337, GetSystemConfig = 69, BackupDatabase = 42, + MemoryCreation = 777, } export enum SyncRequestType { @@ -559,6 +578,8 @@ export enum SyncRequestType { AssetExifsV1 = 'AssetExifsV1', PartnerAssetsV1 = 'PartnerAssetsV1', PartnerAssetExifsV1 = 'PartnerAssetExifsV1', + AlbumsV1 = 'AlbumsV1', + AlbumUsersV1 = 'AlbumUsersV1', } export enum SyncEntityType { @@ -575,4 +596,47 @@ export enum SyncEntityType { PartnerAssetV1 = 'PartnerAssetV1', PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1', PartnerAssetExifV1 = 'PartnerAssetExifV1', + + AlbumV1 = 'AlbumV1', + AlbumDeleteV1 = 'AlbumDeleteV1', + AlbumUserV1 = 'AlbumUserV1', + AlbumUserDeleteV1 = 'AlbumUserDeleteV1', +} + +export enum NotificationLevel { + Success = 'success', + Error = 'error', + Warning = 'warning', + Info = 'info', +} + +export enum NotificationType { + JobFailed = 'JobFailed', + BackupFailed = 'BackupFailed', + SystemMessage = 'SystemMessage', + Custom = 'Custom', +} + +export enum OAuthTokenEndpointAuthMethod { + CLIENT_SECRET_POST = 'client_secret_post', + CLIENT_SECRET_BASIC = 'client_secret_basic', +} + +export enum DatabaseSslMode { + Disable = 'disable', + Allow = 'allow', + Prefer = 'prefer', + Require = 'require', + VerifyFull = 'verify-full', +} + +export enum AssetVisibility { + ARCHIVE = 'archive', + TIMELINE = 'timeline', + + /** + * Video part of the LivePhotos and MotionPhotos + */ + HIDDEN = 'hidden', + LOCKED = 'locked', } diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts index e67c7275a7..4511e1001b 100644 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ b/server/src/migrations/1700713871511-UsePgVectors.ts @@ -1,15 +1,13 @@ -import { ConfigRepository } from 'src/repositories/config.repository'; +import { getVectorExtension } from 'src/repositories/database.repository'; import { getCLIPModelInfo } from 'src/utils/misc'; import { MigrationInterface, QueryRunner } from 'typeorm'; -const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; - export class UsePgVectors1700713871511 implements MigrationInterface { name = 'UsePgVectors1700713871511'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${vectorExtension}`); + await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${await getVectorExtension(queryRunner)}`); const faceDimQuery = await queryRunner.query(` SELECT CARDINALITY(embedding::real[]) as dimsize FROM asset_faces diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts index 993e12f822..43809d6364 100644 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts @@ -1,22 +1,15 @@ -import { DatabaseExtension } from 'src/enum'; -import { ConfigRepository } from 'src/repositories/config.repository'; +import { getVectorExtension } from 'src/repositories/database.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; -const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; - export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { name = 'AddCLIPEmbeddingIndex1700713994428'; public async up(queryRunner: QueryRunner): Promise { - if (vectorExtension === DatabaseExtension.VECTORS) { - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); - } + const vectorExtension = await getVectorExtension(queryRunner); await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS clip_index ON smart_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); } public async down(queryRunner: QueryRunner): Promise { diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts index 182aae4e42..5ee91afbcc 100644 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts @@ -1,22 +1,15 @@ -import { DatabaseExtension } from 'src/enum'; -import { ConfigRepository } from 'src/repositories/config.repository'; +import { getVectorExtension } from 'src/repositories/database.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; -const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; - export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { name = 'AddFaceEmbeddingIndex1700714033632'; public async up(queryRunner: QueryRunner): Promise { - if (vectorExtension === DatabaseExtension.VECTORS) { - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); - } + const vectorExtension = await getVectorExtension(queryRunner); await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS face_index ON asset_faces - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); } public async down(queryRunner: QueryRunner): Promise { diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts index e08bcb8e25..68e1618775 100644 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts @@ -1,14 +1,13 @@ import { DatabaseExtension } from 'src/enum'; -import { ConfigRepository } from 'src/repositories/config.repository'; +import { getVectorExtension } from 'src/repositories/database.repository'; +import { vectorIndexQuery } from 'src/utils/database'; import { MigrationInterface, QueryRunner } from 'typeorm'; -const vectorExtension = new ConfigRepository().getEnv().database.vectorExtension; - export class AddFaceSearchRelation1718486162779 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { + const vectorExtension = await getVectorExtension(queryRunner); if (vectorExtension === DatabaseExtension.VECTORS) { await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); } const hasEmbeddings = async (tableName: string): Promise => { @@ -47,21 +46,14 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface { await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`); await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`); - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS clip_index ON smart_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); - - await queryRunner.query(` - CREATE INDEX face_index ON face_search - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); } public async down(queryRunner: QueryRunner): Promise { + const vectorExtension = await getVectorExtension(queryRunner); if (vectorExtension === DatabaseExtension.VECTORS) { await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`SET vectors.pgvector_compatibility=on`); } await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); @@ -74,9 +66,6 @@ export class AddFaceSearchRelation1718486162779 implements MigrationInterface { WHERE id = fs."faceId"`); await queryRunner.query(`DROP TABLE face_search`); - await queryRunner.query(` - CREATE INDEX face_index ON asset_faces - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16)`); + await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); } } diff --git a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts new file mode 100644 index 0000000000..db351d5bab --- /dev/null +++ b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts @@ -0,0 +1,51 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddForeignKeyIndexes1744900200559 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`); + await queryRunner.query(`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`); + await queryRunner.query(`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`); + await queryRunner.query(`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`); + await queryRunner.query(`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`); + await queryRunner.query(`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`); + await queryRunner.query(`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`); + await queryRunner.query(`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`); + await queryRunner.query(`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`); + await queryRunner.query(`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`); + await queryRunner.query(`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`); + await queryRunner.query(`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`); + await queryRunner.query(`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`); + await queryRunner.query(`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`); + await queryRunner.query(`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`); + await queryRunner.query(`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_66fe3837414c5a9f1c33ca4934";`); + await queryRunner.query(`DROP INDEX "IDX_91704e101438fd0653f582426d";`); + await queryRunner.query(`DROP INDEX "IDX_c05079e542fd74de3b5ecb5c1c";`); + await queryRunner.query(`DROP INDEX "IDX_5527cc99f530a547093f9e577b";`); + await queryRunner.query(`DROP INDEX "IDX_2bbabe31656b6778c6b87b6102";`); + await queryRunner.query(`DROP INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c";`); + await queryRunner.query(`DROP INDEX "IDX_9f9590cc11561f1f48ff034ef9";`); + await queryRunner.query(`DROP INDEX "IDX_2c5ac0d6fb58b238fd2068de67";`); + await queryRunner.query(`DROP INDEX "IDX_16294b83fa8c0149719a1f631e";`); + await queryRunner.query(`DROP INDEX "IDX_9977c3c1de01c3d848039a6b90";`); + await queryRunner.query(`DROP INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20";`); + await queryRunner.query(`DROP INDEX "IDX_b22c53f35ef20c28c21637c85f";`); + await queryRunner.query(`DROP INDEX "IDX_05895aa505a670300d4816debc";`); + await queryRunner.query(`DROP INDEX "IDX_57de40bc620f456c7311aa3a1e";`); + await queryRunner.query(`DROP INDEX "IDX_d8ddd9d687816cc490432b3d4b";`); + await queryRunner.query(`DROP INDEX "IDX_d7e875c6c60e661723dbf372fd";`); + await queryRunner.query(`DROP INDEX "IDX_575842846f0c28fa5da46c99b1";`); + await queryRunner.query(`DROP INDEX "IDX_6c2e267ae764a9413b863a2934";`); + await queryRunner.query(`DROP INDEX "IDX_1af8519996fbfb3684b58df280";`); + await queryRunner.query(`DROP INDEX "IDX_3571467bcbe021f66e2bdce96e";`); + await queryRunner.query(`DROP INDEX "IDX_8091ea76b12338cb4428d33d78";`); + } +} diff --git a/server/src/migrations/1744910873956-AddMissingIndex.ts b/server/src/migrations/1744910873956-AddMissingIndex.ts new file mode 100644 index 0000000000..38dd6f4958 --- /dev/null +++ b/server/src/migrations/1744910873956-AddMissingIndex.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMissingIndex1744910873956 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE INDEX IF NOT EXISTS "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord";`); + } +} diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index dd58aebcb2..402bbdcfaf 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -98,6 +98,7 @@ from where "assets"."id" in ($1) and "assets"."ownerId" = $2 + and "assets"."visibility" != $3 -- AccessRepository.asset.checkPartnerAccess select @@ -110,8 +111,11 @@ from and "assets"."deletedAt" is null where "partner"."sharedWithId" = $1 - and "assets"."isArchived" = $2 - and "assets"."id" in ($3) + and ( + "assets"."visibility" = 'timeline' + or "assets"."visibility" = 'hidden' + ) + and "assets"."id" in ($2) -- AccessRepository.asset.checkSharedLinkAccess select @@ -157,6 +161,15 @@ where and "memories"."ownerId" = $2 and "memories"."deletedAt" is null +-- AccessRepository.notification.checkOwnerAccess +select + "notifications"."id" +from + "notifications" +where + "notifications"."id" in ($1) + and "notifications"."userId" = $2 + -- AccessRepository.person.checkOwnerAccess select "person"."id" @@ -186,6 +199,15 @@ where "partners"."sharedById" in ($1) and "partners"."sharedWithId" = $2 +-- AccessRepository.session.checkOwnerAccess +select + "sessions"."id" +from + "sessions" +where + "sessions"."id" in ($1) + and "sessions"."userId" = $2 + -- AccessRepository.stack.checkOwnerAccess select "stacks"."id" diff --git a/server/src/queries/activity.repository.sql b/server/src/queries/activity.repository.sql index c6e4c60a19..3040de8e03 100644 --- a/server/src/queries/activity.repository.sql +++ b/server/src/queries/activity.repository.sql @@ -13,6 +13,7 @@ from "users"."id", "users"."name", "users"."email", + "users"."avatarColor", "users"."profileImagePath", "users"."profileChangedAt" from @@ -44,6 +45,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql index b89cbfb0b9..2b351368ef 100644 --- a/server/src/queries/album.repository.sql +++ b/server/src/queries/album.repository.sql @@ -12,6 +12,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -36,6 +37,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -100,6 +102,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -124,6 +127,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -191,6 +195,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -215,6 +220,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -269,6 +275,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -292,6 +299,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -353,6 +361,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -383,6 +392,11 @@ where order by "albums"."createdAt" desc +-- AlbumRepository.removeAssetsFromAll +delete from "albums_assets_assets" +where + "albums_assets_assets"."assetsId" in ($1) + -- AlbumRepository.getAssetIds select * diff --git a/server/src/queries/album.user.repository.sql b/server/src/queries/album.user.repository.sql index d628e4980a..08f337c150 100644 --- a/server/src/queries/album.user.repository.sql +++ b/server/src/queries/album.user.repository.sql @@ -6,7 +6,9 @@ insert into values ($1, $2) returning - * + "usersId", + "albumsId", + "role" -- AlbumUserRepository.update update "albums_shared_users_users" diff --git a/server/src/queries/asset.job.repository.sql b/server/src/queries/asset.job.repository.sql index bf96ae80d6..3d47b7517e 100644 --- a/server/src/queries/asset.job.repository.sql +++ b/server/src/queries/asset.job.repository.sql @@ -7,31 +7,15 @@ select "ownerId", "duplicateId", "stackId", - "isVisible", - "smart_search"."embedding", - ( - select - coalesce(json_agg(agg), '[]') - from - ( - select - "asset_files"."id", - "asset_files"."path", - "asset_files"."type" - from - "asset_files" - where - "asset_files"."assetId" = "assets"."id" - and "asset_files"."type" = $1 - ) as agg - ) as "files" + "visibility", + "smart_search"."embedding" from "assets" left join "smart_search" on "assets"."id" = "smart_search"."assetId" where - "assets"."id" = $2::uuid + "assets"."id" = $1::uuid limit - $3 + $2 -- AssetJobRepository.getForSidecarWriteJob select @@ -83,7 +67,7 @@ from inner join "asset_job_status" on "asset_job_status"."assetId" = "assets"."id" where "assets"."deletedAt" is null - and "assets"."isVisible" = $1 + and "assets"."visibility" != $1 and ( "asset_job_status"."previewAt" is null or "asset_job_status"."thumbnailAt" is null @@ -118,7 +102,7 @@ where -- AssetJobRepository.getForGenerateThumbnailJob select "assets"."id", - "assets"."isVisible", + "assets"."visibility", "assets"."originalFileName", "assets"."originalPath", "assets"."ownerId", @@ -155,7 +139,7 @@ select "assets"."fileCreatedAt", "assets"."fileModifiedAt", "assets"."isExternal", - "assets"."isVisible", + "assets"."visibility", "assets"."libraryId", "assets"."livePhotoVideoId", "assets"."localDateTime", @@ -194,10 +178,40 @@ where "asset_files"."assetId" = $1 and "asset_files"."type" = $2 +-- AssetJobRepository.streamForSearchDuplicates +select + "assets"."id" +from + "assets" + inner join "smart_search" on "assets"."id" = "smart_search"."assetId" + inner join "asset_job_status" as "job_status" on "job_status"."assetId" = "assets"."id" +where + "assets"."visibility" != $1 + and "assets"."deletedAt" is null + and "job_status"."duplicatesDetectedAt" is null + +-- AssetJobRepository.streamForEncodeClip +select + "assets"."id" +from + "assets" + inner join "asset_job_status" as "job_status" on "assetId" = "assets"."id" +where + "assets"."visibility" != $1 + and "assets"."deletedAt" is null + and "job_status"."previewAt" is not null + and not exists ( + select + from + "smart_search" + where + "assetId" = "assets"."id" + ) + -- AssetJobRepository.getForClipEncoding select "assets"."id", - "assets"."isVisible", + "assets"."visibility", ( select coalesce(json_agg(agg), '[]') @@ -222,7 +236,7 @@ where -- AssetJobRepository.getForDetectFacesJob select "assets"."id", - "assets"."isVisible", + "assets"."visibility", to_json("exif") as "exifInfo", ( select @@ -259,6 +273,130 @@ from where "assets"."id" = $2 +-- AssetJobRepository.getForSyncAssets +select + "assets"."id", + "assets"."isOffline", + "assets"."libraryId", + "assets"."originalPath", + "assets"."status", + "assets"."fileModifiedAt" +from + "assets" +where + "assets"."id" = any ($1::uuid[]) + +-- AssetJobRepository.getForAssetDeletion +select + "assets"."id", + "assets"."visibility", + "assets"."libraryId", + "assets"."ownerId", + "assets"."livePhotoVideoId", + "assets"."sidecarPath", + "assets"."encodedVideoPath", + "assets"."originalPath", + to_json("exif") as "exifInfo", + ( + select + coalesce(json_agg(agg), '[]') + from + ( + select + "asset_faces".*, + "person" as "person" + from + "asset_faces" + left join lateral ( + select + "person".* + from + "person" + where + "asset_faces"."personId" = "person"."id" + ) as "person" on true + where + "asset_faces"."assetId" = "assets"."id" + and "asset_faces"."deletedAt" is null + ) as agg + ) as "faces", + ( + select + coalesce(json_agg(agg), '[]') + from + ( + select + "asset_files"."id", + "asset_files"."path", + "asset_files"."type" + from + "asset_files" + where + "asset_files"."assetId" = "assets"."id" + ) as agg + ) as "files", + to_json("stacked_assets") as "stack" +from + "assets" + left join "exif" on "assets"."id" = "exif"."assetId" + left join "asset_stack" on "asset_stack"."id" = "assets"."stackId" + left join lateral ( + select + "asset_stack"."id", + "asset_stack"."primaryAssetId", + array_agg("stacked") as "assets" + from + "assets" as "stacked" + where + "stacked"."deletedAt" is not null + and "stacked"."visibility" != $1 + and "stacked"."stackId" = "asset_stack"."id" + group by + "asset_stack"."id" + ) as "stacked_assets" on "asset_stack"."id" is not null +where + "assets"."id" = $2 + +-- AssetJobRepository.streamForVideoConversion +select + "assets"."id" +from + "assets" +where + "assets"."type" = $1 + and ( + "assets"."encodedVideoPath" is null + or "assets"."encodedVideoPath" = $2 + ) + and "assets"."visibility" != $3 + and "assets"."deletedAt" is null + +-- AssetJobRepository.getForVideoConversion +select + "assets"."id", + "assets"."ownerId", + "assets"."originalPath", + "assets"."encodedVideoPath" +from + "assets" +where + "assets"."id" = $1 + and "assets"."type" = $2 + +-- AssetJobRepository.streamForMetadataExtraction +select + "assets"."id" +from + "assets" + left join "asset_job_status" on "asset_job_status"."assetId" = "assets"."id" +where + ( + "asset_job_status"."metadataExtractedAt" is null + or "asset_job_status"."assetId" is null + ) + and "assets"."visibility" != $1 + and "assets"."deletedAt" is null + -- AssetJobRepository.getForStorageTemplateJob select "assets"."id", @@ -308,3 +446,37 @@ from "assets" where "assets"."deletedAt" <= $1 + +-- AssetJobRepository.streamForSidecar +select + "assets"."id" +from + "assets" +where + ( + "assets"."sidecarPath" = $1 + or "assets"."sidecarPath" is null + ) + and "assets"."visibility" != $2 + +-- AssetJobRepository.streamForDetectFacesJob +select + "assets"."id" +from + "assets" + inner join "asset_job_status" as "job_status" on "assetId" = "assets"."id" +where + "assets"."visibility" != $1 + and "assets"."deletedAt" is null + and "job_status"."previewAt" is not null + and "job_status"."facesRecognizedAt" is null +order by + "assets"."createdAt" desc + +-- AssetJobRepository.streamForMigrationJob +select + "id" +from + "assets" +where + "assets"."deletedAt" is null diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index cf17bb0276..8efaa6a17b 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -43,21 +43,20 @@ with "asset_job_status"."previewAt" is not null and (assets."localDateTime" at time zone 'UTC')::date = today.date and "assets"."ownerId" = any ($3::uuid[]) - and "assets"."isVisible" = $4 - and "assets"."isArchived" = $5 + and "assets"."visibility" = $4 and exists ( select from "asset_files" where "assetId" = "assets"."id" - and "asset_files"."type" = $6 + and "asset_files"."type" = $5 ) and "assets"."deletedAt" is null order by (assets."localDateTime" at time zone 'UTC')::date desc limit - $7 + $6 ) as "a" on true inner join "exif" on "a"."id" = "exif"."assetId" ) @@ -82,7 +81,7 @@ from where "assets"."id" = any ($1::uuid[]) --- AssetRepository.getByIdsWithAllRelations +-- AssetRepository.getByIdsWithAllRelationsButStacks select "assets".*, ( @@ -127,28 +126,13 @@ select "assets"."id" = "tag_asset"."assetsId" ) as agg ) as "tags", - to_json("exif") as "exifInfo", - to_json("stacked_assets") as "stack" + to_json("exif") as "exifInfo" from "assets" left join "exif" on "assets"."id" = "exif"."assetId" left join "asset_stack" on "asset_stack"."id" = "assets"."stackId" - left join lateral ( - select - "asset_stack".*, - array_agg("stacked") as "assets" - from - "assets" as "stacked" - where - "stacked"."stackId" = "asset_stack"."id" - and "stacked"."id" != "asset_stack"."primaryAssetId" - and "stacked"."deletedAt" is null - and "stacked"."isArchived" = $1 - group by - "asset_stack"."id" - ) as "stacked_assets" on "asset_stack"."id" is not null where - "assets"."id" = any ($2::uuid[]) + "assets"."id" = any ($1::uuid[]) -- AssetRepository.deleteAll delete from "assets" @@ -174,7 +158,7 @@ from where "ownerId" = $1::uuid and "deviceId" = $2 - and "isVisible" = $3 + and "visibility" != $3 and "deletedAt" is null -- AssetRepository.getLivePhotoCount @@ -247,35 +231,19 @@ where limit $3 --- AssetRepository.getWithout (sidecar) -select - "assets".* -from - "assets" -where - ( - "assets"."sidecarPath" = $1 - or "assets"."sidecarPath" is null - ) - and "assets"."isVisible" = $2 - and "deletedAt" is null -order by - "createdAt" -limit - $3 -offset - $4 - -- AssetRepository.getTimeBuckets with "assets" as ( select - date_trunc($1, "localDateTime" at time zone 'UTC') at time zone 'UTC' as "timeBucket" + date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC' as "timeBucket" from "assets" where "assets"."deletedAt" is null - and "assets"."isVisible" = $2 + and ( + "assets"."visibility" = $1 + or "assets"."visibility" = $2 + ) ) select "timeBucket", @@ -288,44 +256,112 @@ order by "timeBucket" desc -- AssetRepository.getTimeBucket -select - "assets".*, - to_json("exif") as "exifInfo", - to_json("stacked_assets") as "stack" -from - "assets" - left join "exif" on "assets"."id" = "exif"."assetId" - left join "asset_stack" on "asset_stack"."id" = "assets"."stackId" - left join lateral ( +with + "cte" as ( select - "asset_stack".*, - count("stacked") as "assetCount" + "assets"."duration", + "assets"."id", + "assets"."visibility", + "assets"."isFavorite", + assets.type = 'IMAGE' as "isImage", + assets."deletedAt" is not null as "isTrashed", + "assets"."livePhotoVideoId", + "assets"."localDateTime", + "assets"."ownerId", + "assets"."status", + encode("assets"."thumbhash", 'base64') as "thumbhash", + "exif"."city", + "exif"."country", + "exif"."projectionType", + coalesce( + case + when exif."exifImageHeight" = 0 + or exif."exifImageWidth" = 0 then 1 + when "exif"."orientation" in ('5', '6', '7', '8', '-90', '90') then round( + exif."exifImageHeight"::numeric / exif."exifImageWidth"::numeric, + 3 + ) + else round( + exif."exifImageWidth"::numeric / exif."exifImageHeight"::numeric, + 3 + ) + end, + 1 + ) as "ratio", + "stack" from - "assets" as "stacked" + "assets" + inner join "exif" on "assets"."id" = "exif"."assetId" + left join lateral ( + select + array[stacked."stackId"::text, count('stacked')::text] as "stack" + from + "assets" as "stacked" + where + "stacked"."stackId" = "assets"."stackId" + and "stacked"."deletedAt" is null + and "stacked"."visibility" != $1 + group by + "stacked"."stackId" + ) as "stacked_assets" on true where - "stacked"."stackId" = "asset_stack"."id" - and "stacked"."deletedAt" is null - and "stacked"."isArchived" = $1 - group by - "asset_stack"."id" - ) as "stacked_assets" on "asset_stack"."id" is not null -where - ( - "asset_stack"."primaryAssetId" = "assets"."id" - or "assets"."stackId" is null + "assets"."deletedAt" is null + and ( + "assets"."visibility" = $2 + or "assets"."visibility" = $3 + ) + and date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC' = $4 + and ( + "assets"."visibility" = $5 + or "assets"."visibility" = $6 + ) + and not exists ( + select + from + "asset_stack" + where + "asset_stack"."id" = "assets"."stackId" + and "asset_stack"."primaryAssetId" != "assets"."id" + ) + order by + "assets"."localDateTime" desc + ), + "agg" as ( + select + coalesce(array_agg("city"), '{}') as "city", + coalesce(array_agg("country"), '{}') as "country", + coalesce(array_agg("duration"), '{}') as "duration", + coalesce(array_agg("id"), '{}') as "id", + coalesce(array_agg("visibility"), '{}') as "visibility", + coalesce(array_agg("isFavorite"), '{}') as "isFavorite", + coalesce(array_agg("isImage"), '{}') as "isImage", + coalesce(array_agg("isTrashed"), '{}') as "isTrashed", + coalesce(array_agg("livePhotoVideoId"), '{}') as "livePhotoVideoId", + coalesce(array_agg("localDateTime"), '{}') as "localDateTime", + coalesce(array_agg("ownerId"), '{}') as "ownerId", + coalesce(array_agg("projectionType"), '{}') as "projectionType", + coalesce(array_agg("ratio"), '{}') as "ratio", + coalesce(array_agg("status"), '{}') as "status", + coalesce(array_agg("thumbhash"), '{}') as "thumbhash", + coalesce(json_agg("stack"), '[]') as "stack" + from + "cte" ) - and "assets"."deletedAt" is null - and "assets"."isVisible" = $2 - and date_trunc($3, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $4 -order by - "assets"."localDateTime" desc +select + to_json(agg)::text as "assets" +from + "agg" -- AssetRepository.getDuplicates with "duplicates" as ( select "assets"."duplicateId", - jsonb_agg("asset") as "assets" + json_agg( + "asset" + order by + "assets"."localDateTime" asc + ) as "assets" from "assets" left join lateral ( @@ -341,7 +377,7 @@ with "assets"."ownerId" = $1::uuid and "assets"."duplicateId" is not null and "assets"."deletedAt" is null - and "assets"."isVisible" = $2 + and "assets"."visibility" != $2 and "assets"."stackId" is null group by "assets"."duplicateId" @@ -352,7 +388,7 @@ with from "duplicates" where - jsonb_array_length("assets") = $3 + json_array_length("assets") = $3 ), "removed_unique" as ( update "assets" @@ -399,12 +435,11 @@ from inner join "cities" on "exif"."city" = "cities"."city" where "ownerId" = $2::uuid - and "isVisible" = $3 - and "isArchived" = $4 - and "type" = $5 + and "visibility" = $3 + and "type" = $4 and "deletedAt" is null limit - $6 + $5 -- AssetRepository.getAllForUserFullSync select @@ -428,7 +463,7 @@ from ) as "stacked_assets" on "asset_stack"."id" is not null where "assets"."ownerId" = $1::uuid - and "assets"."isVisible" = $2 + and "assets"."visibility" != $2 and "assets"."updatedAt" <= $3 and "assets"."id" > $4 order by @@ -458,7 +493,38 @@ from ) as "stacked_assets" on "asset_stack"."id" is not null where "assets"."ownerId" = any ($1::uuid[]) - and "assets"."isVisible" = $2 + and "assets"."visibility" != $2 and "assets"."updatedAt" > $3 limit $4 + +-- AssetRepository.detectOfflineExternalAssets +update "assets" +set + "isOffline" = $1, + "deletedAt" = $2 +where + "isOffline" = $3 + and "isExternal" = $4 + and "libraryId" = $5::uuid + and ( + not "originalPath" like $6 + or "originalPath" like $7 + ) + +-- AssetRepository.filterNewExternalAssetPaths +select + "path" +from + unnest(array[$1]::text[]) as "path" +where + not exists ( + select + "originalPath" + from + "assets" + where + "assets"."originalPath" = "path" + and "libraryId" = $2::uuid + and "isExternal" = $3 + ) diff --git a/server/src/queries/audit.repository.sql b/server/src/queries/audit.repository.sql index 3c83d2d3e8..b1a10abf48 100644 --- a/server/src/queries/audit.repository.sql +++ b/server/src/queries/audit.repository.sql @@ -14,8 +14,3 @@ order by "audit"."entityId" desc, "audit"."entityType" desc, "audit"."createdAt" desc - --- AuditRepository.removeBefore -delete from "audit" -where - "createdAt" < $1 diff --git a/server/src/queries/database.repository.sql b/server/src/queries/database.repository.sql index 8c87a7470f..be27f1846c 100644 --- a/server/src/queries/database.repository.sql +++ b/server/src/queries/database.repository.sql @@ -1,21 +1,14 @@ -- NOTE: This file is auto generated by ./sql-generator --- DatabaseRepository.getExtensionVersion +-- DatabaseRepository.getExtensionVersions SELECT + name, default_version as "availableVersion", installed_version as "installedVersion" FROM pg_available_extensions WHERE - name = $1 + name in ($1) -- DatabaseRepository.getPostgresVersion SHOW server_version - --- DatabaseRepository.shouldReindex -SELECT - idx_status -FROM - pg_vector_index_stat -WHERE - indexname = $1 diff --git a/server/src/queries/library.repository.sql b/server/src/queries/library.repository.sql index 43500a8748..f17d9663d6 100644 --- a/server/src/queries/library.repository.sql +++ b/server/src/queries/library.repository.sql @@ -35,14 +35,14 @@ select where ( "assets"."type" = $1 - and "assets"."isVisible" = $2 + and "assets"."visibility" != $2 ) ) as "photos", count(*) filter ( where ( "assets"."type" = $3 - and "assets"."isVisible" = $4 + and "assets"."visibility" != $4 ) ) as "videos", coalesce(sum("exif"."fileSizeInByte"), $5) as "usage" diff --git a/server/src/queries/map.repository.sql b/server/src/queries/map.repository.sql index b3bb207946..edfdec13d2 100644 --- a/server/src/queries/map.repository.sql +++ b/server/src/queries/map.repository.sql @@ -14,7 +14,7 @@ from and "exif"."latitude" is not null and "exif"."longitude" is not null where - "isVisible" = $1 + "assets"."visibility" = $1 and "deletedAt" is null and ( "ownerId" in ($2) diff --git a/server/src/queries/memory.repository.sql b/server/src/queries/memory.repository.sql index d44d017045..a3243025b4 100644 --- a/server/src/queries/memory.repository.sql +++ b/server/src/queries/memory.repository.sql @@ -1,11 +1,5 @@ -- NOTE: This file is auto generated by ./sql-generator --- MemoryRepository.cleanup -delete from "memories" -where - "createdAt" < $1 - and "isSaved" = $2 - -- MemoryRepository.search select "memories".*, @@ -21,6 +15,7 @@ select inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId" where "memories_assets_assets"."memoriesId" = "memories"."id" + and "assets"."visibility" = 'timeline' and "assets"."deletedAt" is null order by "assets"."fileCreatedAt" asc @@ -49,6 +44,7 @@ select inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId" where "memories_assets_assets"."memoriesId" = "memories"."id" + and "assets"."visibility" = 'timeline' and "assets"."deletedAt" is null order by "assets"."fileCreatedAt" asc @@ -85,6 +81,7 @@ select inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId" where "memories_assets_assets"."memoriesId" = "memories"."id" + and "assets"."visibility" = 'timeline' and "assets"."deletedAt" is null order by "assets"."fileCreatedAt" asc @@ -117,6 +114,7 @@ select inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId" where "memories_assets_assets"."memoriesId" = "memories"."id" + and "assets"."visibility" = 'timeline' and "assets"."deletedAt" is null order by "assets"."fileCreatedAt" asc diff --git a/server/src/queries/move.repository.sql b/server/src/queries/move.repository.sql index a65c7a8b85..50c9ad7dd9 100644 --- a/server/src/queries/move.repository.sql +++ b/server/src/queries/move.repository.sql @@ -16,19 +16,6 @@ where returning * --- MoveRepository.cleanMoveHistory -delete from "move_history" -where - "move_history"."entityId" not in ( - select - "id" - from - "assets" - where - "assets"."id" = "move_history"."entityId" - ) - and "move_history"."pathType" = 'original' - -- MoveRepository.cleanMoveHistorySingle delete from "move_history" where diff --git a/server/src/queries/notification.repository.sql b/server/src/queries/notification.repository.sql new file mode 100644 index 0000000000..f7e211d80a --- /dev/null +++ b/server/src/queries/notification.repository.sql @@ -0,0 +1,40 @@ +-- NOTE: This file is auto generated by ./sql-generator + +-- NotificationRepository.search +select + "id", + "createdAt", + "level", + "type", + "title", + "description", + "data", + "readAt" +from + "notifications" +where + "userId" = $1 + and "deletedAt" is null +order by + "createdAt" desc + +-- NotificationRepository.search (unread) +select + "id", + "createdAt", + "level", + "type", + "title", + "description", + "data", + "readAt" +from + "notifications" +where + ( + "userId" = $1 + and "readAt" is null + ) + and "deletedAt" is null +order by + "createdAt" desc diff --git a/server/src/queries/partner.repository.sql b/server/src/queries/partner.repository.sql index e115dc34b9..100f1bc638 100644 --- a/server/src/queries/partner.repository.sql +++ b/server/src/queries/partner.repository.sql @@ -12,6 +12,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -29,6 +30,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -61,6 +63,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -78,6 +81,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -96,48 +100,6 @@ where "sharedWithId" = $1 and "sharedById" = $2 --- PartnerRepository.create -insert into - "partners" ("sharedWithId", "sharedById") -values - ($1, $2) -returning - *, - ( - select - to_json(obj) - from - ( - select - "id", - "name", - "email", - "profileImagePath", - "profileChangedAt" - from - "users" as "sharedBy" - where - "sharedBy"."id" = "partners"."sharedById" - ) as obj - ) as "sharedBy", - ( - select - to_json(obj) - from - ( - select - "id", - "name", - "email", - "profileImagePath", - "profileChangedAt" - from - "users" as "sharedWith" - where - "sharedWith"."id" = "partners"."sharedWithId" - ) as obj - ) as "sharedWith" - -- PartnerRepository.update update "partners" set @@ -156,6 +118,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from @@ -173,6 +136,7 @@ returning "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt" from diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index f9ba32262d..48854f4872 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -7,34 +7,10 @@ set where "asset_faces"."personId" = $2 --- PersonRepository.unassignFaces -update "asset_faces" -set - "personId" = $1 -where - "asset_faces"."sourceType" = $2 -VACUUM -ANALYZE asset_faces, -face_search, -person -REINDEX TABLE asset_faces -REINDEX TABLE person - -- PersonRepository.delete delete from "person" where - "person"."id" in $1 - --- PersonRepository.deleteFaces -delete from "asset_faces" -where - "asset_faces"."sourceType" = $1 -VACUUM -ANALYZE asset_faces, -face_search, -person -REINDEX TABLE asset_faces -REINDEX TABLE person + "person"."id" in ($1) -- PersonRepository.getAllWithoutFaces select @@ -107,7 +83,7 @@ select ( select "assets"."ownerId", - "assets"."isArchived", + "assets"."visibility", "assets"."fileCreatedAt" from "assets" @@ -143,23 +119,26 @@ select "asset_faces"."boundingBoxY2" as "y2", "asset_faces"."imageWidth" as "oldWidth", "asset_faces"."imageHeight" as "oldHeight", - "exif"."exifImageWidth", - "exif"."exifImageHeight", "assets"."type", "assets"."originalPath", - "asset_files"."path" as "previewPath" + "exif"."orientation" as "exifOrientation", + ( + select + "asset_files"."path" + from + "asset_files" + where + "asset_files"."assetId" = "assets"."id" + and "asset_files"."type" = 'preview' + ) as "previewPath" from "person" inner join "asset_faces" on "asset_faces"."id" = "person"."faceAssetId" inner join "assets" on "asset_faces"."assetId" = "assets"."id" - inner join "exif" on "exif"."assetId" = "assets"."id" - inner join "asset_files" on "asset_files"."assetId" = "assets"."id" + left join "exif" on "exif"."assetId" = "assets"."id" where "person"."id" = $1 and "asset_faces"."deletedAt" is null - and "asset_files"."type" = $2 - and "exif"."exifImageWidth" > $3 - and "exif"."exifImageHeight" > $4 -- PersonRepository.reassignFace update "asset_faces" @@ -203,7 +182,7 @@ from "asset_faces" left join "assets" on "assets"."id" = "asset_faces"."assetId" and "asset_faces"."personId" = $1 - and "assets"."isArchived" = $2 + and "assets"."visibility" != $2 and "assets"."deletedAt" is null where "asset_faces"."deletedAt" is null @@ -220,7 +199,7 @@ from inner join "asset_faces" on "asset_faces"."personId" = "person"."id" inner join "assets" on "assets"."id" = "asset_faces"."assetId" and "assets"."deletedAt" is null - and "assets"."isArchived" = $2 + and "assets"."visibility" != $2 where "person"."ownerId" = $3 and "asset_faces"."deletedAt" is null diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index 4fce272365..c100089179 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -7,11 +7,11 @@ from "assets" inner join "exif" on "assets"."id" = "exif"."assetId" where - "assets"."fileCreatedAt" >= $1 - and "exif"."lensModel" = $2 - and "assets"."ownerId" = any ($3::uuid[]) - and "assets"."isFavorite" = $4 - and "assets"."isArchived" = $5 + "assets"."visibility" = $1 + and "assets"."fileCreatedAt" >= $2 + and "exif"."lensModel" = $3 + and "assets"."ownerId" = any ($4::uuid[]) + and "assets"."isFavorite" = $5 and "assets"."deletedAt" is null order by "assets"."fileCreatedAt" desc @@ -28,11 +28,11 @@ offset "assets" inner join "exif" on "assets"."id" = "exif"."assetId" where - "assets"."fileCreatedAt" >= $1 - and "exif"."lensModel" = $2 - and "assets"."ownerId" = any ($3::uuid[]) - and "assets"."isFavorite" = $4 - and "assets"."isArchived" = $5 + "assets"."visibility" = $1 + and "assets"."fileCreatedAt" >= $2 + and "exif"."lensModel" = $3 + and "assets"."ownerId" = any ($4::uuid[]) + and "assets"."isFavorite" = $5 and "assets"."deletedAt" is null and "assets"."id" < $6 order by @@ -48,11 +48,11 @@ union all "assets" inner join "exif" on "assets"."id" = "exif"."assetId" where - "assets"."fileCreatedAt" >= $8 - and "exif"."lensModel" = $9 - and "assets"."ownerId" = any ($10::uuid[]) - and "assets"."isFavorite" = $11 - and "assets"."isArchived" = $12 + "assets"."visibility" = $8 + and "assets"."fileCreatedAt" >= $9 + and "exif"."lensModel" = $10 + and "assets"."ownerId" = any ($11::uuid[]) + and "assets"."isFavorite" = $12 and "assets"."deletedAt" is null and "assets"."id" > $13 order by @@ -64,6 +64,9 @@ limit $15 -- SearchRepository.searchSmart +begin +set + local vchordrq.probes = 1 select "assets".* from @@ -71,11 +74,11 @@ from inner join "exif" on "assets"."id" = "exif"."assetId" inner join "smart_search" on "assets"."id" = "smart_search"."assetId" where - "assets"."fileCreatedAt" >= $1 - and "exif"."lensModel" = $2 - and "assets"."ownerId" = any ($3::uuid[]) - and "assets"."isFavorite" = $4 - and "assets"."isArchived" = $5 + "assets"."visibility" = $1 + and "assets"."fileCreatedAt" >= $2 + and "exif"."lensModel" = $3 + and "assets"."ownerId" = any ($4::uuid[]) + and "assets"."isFavorite" = $5 and "assets"."deletedAt" is null order by smart_search.embedding <=> $6 @@ -83,8 +86,12 @@ limit $7 offset $8 +commit -- SearchRepository.searchDuplicates +begin +set + local vchordrq.probes = 1 with "cte" as ( select @@ -97,23 +104,27 @@ with where "assets"."ownerId" = any ($2::uuid[]) and "assets"."deletedAt" is null - and "assets"."isVisible" = $3 + and "assets"."visibility" != $3 and "assets"."type" = $4 and "assets"."id" != $5::uuid and "assets"."stackId" is null order by - smart_search.embedding <=> $6 + "distance" limit - $7 + $6 ) select * from "cte" where - "cte"."distance" <= $8 + "cte"."distance" <= $7 +commit -- SearchRepository.searchFaces +begin +set + local vchordrq.probes = 1 with "cte" as ( select @@ -129,16 +140,17 @@ with "assets"."ownerId" = any ($2::uuid[]) and "assets"."deletedAt" is null order by - face_search.embedding <=> $3 + "distance" limit - $4 + $3 ) select * from "cte" where - "cte"."distance" <= $5 + "cte"."distance" <= $4 +commit -- SearchRepository.searchPlaces select @@ -176,14 +188,13 @@ with recursive inner join "assets" on "assets"."id" = "exif"."assetId" where "assets"."ownerId" = any ($1::uuid[]) - and "assets"."isVisible" = $2 - and "assets"."isArchived" = $3 - and "assets"."type" = $4 + and "assets"."visibility" = $2 + and "assets"."type" = $3 and "assets"."deletedAt" is null order by "city" limit - $5 + $4 ) union all ( @@ -200,16 +211,15 @@ with recursive "exif" inner join "assets" on "assets"."id" = "exif"."assetId" where - "assets"."ownerId" = any ($6::uuid[]) - and "assets"."isVisible" = $7 - and "assets"."isArchived" = $8 - and "assets"."type" = $9 + "assets"."ownerId" = any ($5::uuid[]) + and "assets"."visibility" = $6 + and "assets"."type" = $7 and "assets"."deletedAt" is null and "exif"."city" > "cte"."city" order by "city" limit - $10 + $8 ) as "l" on true ) ) @@ -231,7 +241,7 @@ from inner join "assets" on "assets"."id" = "exif"."assetId" where "ownerId" = any ($1::uuid[]) - and "isVisible" = $2 + and "visibility" != $2 and "deletedAt" is null and "state" is not null @@ -243,7 +253,7 @@ from inner join "assets" on "assets"."id" = "exif"."assetId" where "ownerId" = any ($1::uuid[]) - and "isVisible" = $2 + and "visibility" != $2 and "deletedAt" is null and "city" is not null @@ -255,7 +265,7 @@ from inner join "assets" on "assets"."id" = "exif"."assetId" where "ownerId" = any ($1::uuid[]) - and "isVisible" = $2 + and "visibility" != $2 and "deletedAt" is null and "make" is not null @@ -267,6 +277,6 @@ from inner join "assets" on "assets"."id" = "exif"."assetId" where "ownerId" = any ($1::uuid[]) - and "isVisible" = $2 + and "visibility" != $2 and "deletedAt" is null and "model" is not null diff --git a/server/src/queries/session.repository.sql b/server/src/queries/session.repository.sql index eea2356897..6a9b69c2e3 100644 --- a/server/src/queries/session.repository.sql +++ b/server/src/queries/session.repository.sql @@ -1,17 +1,20 @@ -- NOTE: This file is auto generated by ./sql-generator --- SessionRepository.search +-- SessionRepository.get select - * + "id", + "expiresAt", + "pinExpiresAt" from "sessions" where - "sessions"."updatedAt" <= $1 + "id" = $1 -- SessionRepository.getByToken select "sessions"."id", "sessions"."updatedAt", + "sessions"."pinExpiresAt", ( select to_json(obj) @@ -35,6 +38,10 @@ from "sessions" where "sessions"."token" = $1 + and ( + "sessions"."expiresAt" is null + or "sessions"."expiresAt" > $2 + ) -- SessionRepository.getByUserId select @@ -45,6 +52,10 @@ from and "users"."deletedAt" is null where "sessions"."userId" = $1 + and ( + "sessions"."expiresAt" is null + or "sessions"."expiresAt" > $2 + ) order by "sessions"."updatedAt" desc, "sessions"."createdAt" desc @@ -53,3 +64,10 @@ order by delete from "sessions" where "id" = $1::uuid + +-- SessionRepository.lockAll +update "sessions" +set + "pinExpiresAt" = $1 +where + "userId" = $2 diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index e08335d9f1..f797f5c0b5 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -84,7 +84,7 @@ select "type", "deletedAt", "isFavorite", - "isVisible", + "visibility", "updateId" from "assets" @@ -106,7 +106,7 @@ select "type", "deletedAt", "isFavorite", - "isVisible", + "visibility", "updateId" from "assets" @@ -246,3 +246,98 @@ where and "updatedAt" < now() - interval '1 millisecond' order by "updateId" asc + +-- SyncRepository.getAlbumDeletes +select + "id", + "albumId" +from + "albums_audit" +where + "userId" = $1 + and "deletedAt" < now() - interval '1 millisecond' +order by + "id" asc + +-- SyncRepository.getAlbumUpserts +select distinct + on ("albums"."id", "albums"."updateId") "albums"."id", + "albums"."ownerId", + "albums"."albumName" as "name", + "albums"."description", + "albums"."createdAt", + "albums"."updatedAt", + "albums"."albumThumbnailAssetId" as "thumbnailAssetId", + "albums"."isActivityEnabled", + "albums"."order", + "albums"."updateId" +from + "albums" + left join "albums_shared_users_users" as "album_users" on "albums"."id" = "album_users"."albumsId" +where + "albums"."updatedAt" < now() - interval '1 millisecond' + and ( + "albums"."ownerId" = $1 + or "album_users"."usersId" = $2 + ) +order by + "albums"."updateId" asc + +-- SyncRepository.getAlbumUserDeletes +select + "id", + "userId", + "albumId" +from + "album_users_audit" +where + "albumId" in ( + select + "id" + from + "albums" + where + "ownerId" = $1 + union + ( + select + "albumUsers"."albumsId" as "id" + from + "albums_shared_users_users" as "albumUsers" + where + "albumUsers"."usersId" = $2 + ) + ) + and "deletedAt" < now() - interval '1 millisecond' +order by + "id" asc + +-- SyncRepository.getAlbumUserUpserts +select + "albums_shared_users_users"."albumsId" as "albumId", + "albums_shared_users_users"."usersId" as "userId", + "albums_shared_users_users"."role", + "albums_shared_users_users"."updateId" +from + "albums_shared_users_users" +where + "albums_shared_users_users"."updatedAt" < now() - interval '1 millisecond' + and "albums_shared_users_users"."albumsId" in ( + select + "id" + from + "albums" + where + "ownerId" = $1 + union + ( + select + "albumUsers"."albumsId" as "id" + from + "albums_shared_users_users" as "albumUsers" + where + "albumUsers"."usersId" = $2 + ) + ) +order by + "albums_shared_users_users"."updateId" asc diff --git a/server/src/queries/system.metadata.repository.sql b/server/src/queries/system.metadata.repository.sql index c4fd7b96f8..8bdf1b3ad7 100644 --- a/server/src/queries/system.metadata.repository.sql +++ b/server/src/queries/system.metadata.repository.sql @@ -8,15 +8,6 @@ from where "key" = $1 --- SystemMetadataRepository.set -insert into - "system_metadata" ("key", "value") -values - ($1, $2) -on conflict ("key") do update -set - "value" = $3 - -- SystemMetadataRepository.delete delete from "system_metadata" where diff --git a/server/src/queries/tag.repository.sql b/server/src/queries/tag.repository.sql index d728d3af88..af757d96b7 100644 --- a/server/src/queries/tag.repository.sql +++ b/server/src/queries/tag.repository.sql @@ -58,7 +58,7 @@ from where "userId" = $1 order by - "value" asc + "value" -- TagRepository.create insert into @@ -94,6 +94,15 @@ where "tagsId" = $1 and "assetsId" in ($2) +-- TagRepository.upsertAssetIds +insert into + "tag_asset" ("assetId", "tagsIds") +values + ($1, $2) +on conflict do nothing +returning + * + -- TagRepository.replaceAssetTags begin delete from "tag_asset" @@ -107,17 +116,3 @@ on conflict do nothing returning * rollback - --- TagRepository.deleteEmptyTags -begin -select - "tags"."id", - count("assets"."id") as "count" -from - "assets" - inner join "tag_asset" on "tag_asset"."assetsId" = "assets"."id" - inner join "tags_closure" on "tags_closure"."id_descendant" = "tag_asset"."tagsId" - inner join "tags" on "tags"."id" = "tags_closure"."id_descendant" -group by - "tags"."id" -commit diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index 1212d0f2bd..33f2960266 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -5,6 +5,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -43,6 +44,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -85,11 +87,22 @@ where "users"."isAdmin" = $1 and "users"."deletedAt" is null +-- UserRepository.getForPinCode +select + "users"."pinCode", + "users"."password" +from + "users" +where + "users"."id" = $1 + and "users"."deletedAt" is null + -- UserRepository.getByEmail select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -128,6 +141,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -152,6 +166,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -198,6 +213,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -235,6 +251,7 @@ select "id", "name", "email", + "avatarColor", "profileImagePath", "profileChangedAt", "createdAt", @@ -278,14 +295,14 @@ select where ( "assets"."type" = 'IMAGE' - and "assets"."isVisible" = true + and "assets"."visibility" != 'hidden' ) ) as "photos", count(*) filter ( where ( "assets"."type" = 'VIDEO' - and "assets"."isVisible" = true + and "assets"."visibility" != 'hidden' ) ) as "videos", coalesce( diff --git a/server/src/queries/version.history.repository.sql b/server/src/queries/version.history.repository.sql index a9805e8c25..2e898cac31 100644 --- a/server/src/queries/version.history.repository.sql +++ b/server/src/queries/version.history.repository.sql @@ -15,11 +15,3 @@ from "version_history" order by "createdAt" desc - --- VersionHistoryRepository.create -insert into - "version_history" ("version") -values - ($1) -returning - * diff --git a/server/src/queries/view.repository.sql b/server/src/queries/view.repository.sql index 1510521526..a2260ce5f6 100644 --- a/server/src/queries/view.repository.sql +++ b/server/src/queries/view.repository.sql @@ -7,8 +7,7 @@ from "assets" where "ownerId" = $2::uuid - and "isVisible" = $3 - and "isArchived" = $4 + and "visibility" = $3 and "deletedAt" is null and "fileCreatedAt" is not null and "fileModifiedAt" is not null @@ -23,13 +22,12 @@ from left join "exif" on "assets"."id" = "exif"."assetId" where "ownerId" = $1::uuid - and "isVisible" = $2 - and "isArchived" = $3 + and "visibility" = $2 and "deletedAt" is null and "fileCreatedAt" is not null and "fileModifiedAt" is not null and "localDateTime" is not null - and "originalPath" like $4 - and "originalPath" not like $5 + and "originalPath" like $3 + and "originalPath" not like $4 order by - regexp_replace("assets"."originalPath", $6, $7) asc + regexp_replace("assets"."originalPath", $5, $6) asc diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 961cccbf3e..17f69c0e52 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -3,7 +3,7 @@ import { Kysely, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DB } from 'src/db'; import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; -import { AlbumUserRole } from 'src/enum'; +import { AlbumUserRole, AssetVisibility } from 'src/enum'; import { asUuid } from 'src/utils/database'; class ActivityAccess { @@ -168,7 +168,7 @@ class AssetAccess { @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) @ChunkedSet({ paramIndex: 1 }) - async checkOwnerAccess(userId: string, assetIds: Set) { + async checkOwnerAccess(userId: string, assetIds: Set, hasElevatedPermission: boolean | undefined) { if (assetIds.size === 0) { return new Set(); } @@ -178,6 +178,7 @@ class AssetAccess { .select('assets.id') .where('assets.id', 'in', [...assetIds]) .where('assets.ownerId', '=', userId) + .$if(!hasElevatedPermission, (eb) => eb.where('assets.visibility', '!=', AssetVisibility.LOCKED)) .execute() .then((assets) => new Set(assets.map((asset) => asset.id))); } @@ -199,7 +200,13 @@ class AssetAccess { ) .select('assets.id') .where('partner.sharedWithId', '=', userId) - .where('assets.isArchived', '=', false) + .where((eb) => + eb.or([ + eb('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE)), + eb('assets.visibility', '=', sql.lit(AssetVisibility.HIDDEN)), + ]), + ) + .where('assets.id', 'in', [...assetIds]) .execute() .then((assets) => new Set(assets.map((asset) => asset.id))); @@ -279,6 +286,45 @@ class AuthDeviceAccess { } } +class NotificationAccess { + constructor(private db: Kysely) {} + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) + @ChunkedSet({ paramIndex: 1 }) + async checkOwnerAccess(userId: string, notificationIds: Set) { + if (notificationIds.size === 0) { + return new Set(); + } + + return this.db + .selectFrom('notifications') + .select('notifications.id') + .where('notifications.id', 'in', [...notificationIds]) + .where('notifications.userId', '=', userId) + .execute() + .then((stacks) => new Set(stacks.map((stack) => stack.id))); + } +} + +class SessionAccess { + constructor(private db: Kysely) {} + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] }) + @ChunkedSet({ paramIndex: 1 }) + async checkOwnerAccess(userId: string, sessionIds: Set) { + if (sessionIds.size === 0) { + return new Set(); + } + + return this.db + .selectFrom('sessions') + .select('sessions.id') + .where('sessions.id', 'in', [...sessionIds]) + .where('sessions.userId', '=', userId) + .execute() + .then((sessions) => new Set(sessions.map((session) => session.id))); + } +} class StackAccess { constructor(private db: Kysely) {} @@ -426,8 +472,10 @@ export class AccessRepository { asset: AssetAccess; authDevice: AuthDeviceAccess; memory: MemoryAccess; + notification: NotificationAccess; person: PersonAccess; partner: PartnerAccess; + session: SessionAccess; stack: StackAccess; tag: TagAccess; timeline: TimelineAccess; @@ -438,8 +486,10 @@ export class AccessRepository { this.asset = new AssetAccess(db); this.authDevice = new AuthDeviceAccess(db); this.memory = new MemoryAccess(db); + this.notification = new NotificationAccess(db); this.person = new PersonAccess(db); this.partner = new PartnerAccess(db); + this.session = new SessionAccess(db); this.stack = new StackAccess(db); this.tag = new TagAccess(db); this.timeline = new TimelineAccess(db); diff --git a/server/src/repositories/album-user.repository.ts b/server/src/repositories/album-user.repository.ts index f363f2e91a..ad7ba8d6cd 100644 --- a/server/src/repositories/album-user.repository.ts +++ b/server/src/repositories/album-user.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, Selectable, Updateable } from 'kysely'; +import { Insertable, Kysely, Updateable } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { AlbumsSharedUsersUsers, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; @@ -15,8 +15,12 @@ export class AlbumUserRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] }) - create(albumUser: Insertable): Promise> { - return this.db.insertInto('albums_shared_users_users').values(albumUser).returningAll().executeTakeFirstOrThrow(); + create(albumUser: Insertable) { + return this.db + .insertInto('albums_shared_users_users') + .values(albumUser) + .returning(['usersId', 'albumsId', 'role']) + .executeTakeFirstOrThrow(); } @GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] }) diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index e21d5d73cd..c8bdae6d31 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -1,12 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { ExpressionBuilder, Insertable, Kysely, sql, Updateable } from 'kysely'; +import { ExpressionBuilder, Insertable, Kysely, NotNull, sql, Updateable } from 'kysely'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; -import { columns } from 'src/database'; +import { columns, Exif } from 'src/database'; import { Albums, DB } from 'src/db'; import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { AlbumUserCreateDto } from 'src/dtos/album.dto'; -import { AlbumEntity } from 'src/entities/album.entity'; export interface AlbumAssetCount { albumId: string; @@ -21,9 +20,9 @@ export interface AlbumInfoOptions { } const withOwner = (eb: ExpressionBuilder) => { - return jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'albums.ownerId')).as( - 'owner', - ); + return jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'albums.ownerId')) + .$notNull() + .as('owner'); }; const withAlbumUsers = (eb: ExpressionBuilder) => { @@ -32,12 +31,14 @@ const withAlbumUsers = (eb: ExpressionBuilder) => { .selectFrom('albums_shared_users_users as album_users') .select('album_users.role') .select((eb) => - jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'album_users.usersId')).as( - 'user', - ), + jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'album_users.usersId')) + .$notNull() + .as('user'), ) .whereRef('album_users.albumsId', '=', 'albums.id'), - ).as('albumUsers'); + ) + .$notNull() + .as('albumUsers'); }; const withSharedLink = (eb: ExpressionBuilder) => { @@ -53,7 +54,7 @@ const withAssets = (eb: ExpressionBuilder) => { .selectFrom('assets') .selectAll('assets') .leftJoin('exif', 'assets.id', 'exif.assetId') - .select((eb) => eb.table('exif').as('exifInfo')) + .select((eb) => eb.table('exif').$castTo().as('exifInfo')) .innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id') .whereRef('albums_assets_assets.albumsId', '=', 'albums.id') .where('assets.deletedAt', 'is', null) @@ -69,7 +70,7 @@ export class AlbumRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql({ params: [DummyValue.UUID, { withAssets: true }] }) - async getById(id: string, options: AlbumInfoOptions): Promise { + async getById(id: string, options: AlbumInfoOptions) { return this.db .selectFrom('albums') .selectAll('albums') @@ -79,11 +80,12 @@ export class AlbumRepository { .select(withAlbumUsers) .select(withSharedLink) .$if(options.withAssets, (eb) => eb.select(withAssets)) - .executeTakeFirst() as Promise; + .$narrowType<{ assets: NotNull }>() + .executeTakeFirst(); } @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - async getByAssetId(ownerId: string, assetId: string): Promise { + async getByAssetId(ownerId: string, assetId: string) { return this.db .selectFrom('albums') .selectAll('albums') @@ -105,7 +107,7 @@ export class AlbumRepository { .select(withOwner) .select(withAlbumUsers) .orderBy('albums.createdAt', 'desc') - .execute() as unknown as Promise; + .execute(); } @GenerateSql({ params: [[DummyValue.UUID]] }) @@ -134,7 +136,7 @@ export class AlbumRepository { } @GenerateSql({ params: [DummyValue.UUID] }) - async getOwned(ownerId: string): Promise { + async getOwned(ownerId: string) { return this.db .selectFrom('albums') .selectAll('albums') @@ -144,14 +146,14 @@ export class AlbumRepository { .where('albums.ownerId', '=', ownerId) .where('albums.deletedAt', 'is', null) .orderBy('albums.createdAt', 'desc') - .execute() as unknown as Promise; + .execute(); } /** * Get albums shared with and shared by owner. */ @GenerateSql({ params: [DummyValue.UUID] }) - async getShared(ownerId: string): Promise { + async getShared(ownerId: string) { return this.db .selectFrom('albums') .selectAll('albums') @@ -176,14 +178,14 @@ export class AlbumRepository { .select(withOwner) .select(withSharedLink) .orderBy('albums.createdAt', 'desc') - .execute() as unknown as Promise; + .execute(); } /** * Get albums of owner that are _not_ shared */ @GenerateSql({ params: [DummyValue.UUID] }) - async getNotShared(ownerId: string): Promise { + async getNotShared(ownerId: string) { return this.db .selectFrom('albums') .selectAll('albums') @@ -203,7 +205,7 @@ export class AlbumRepository { ) .select(withOwner) .orderBy('albums.createdAt', 'desc') - .execute() as unknown as Promise; + .execute(); } async restoreAll(userId: string): Promise { @@ -218,8 +220,10 @@ export class AlbumRepository { await this.db.deleteFrom('albums').where('ownerId', '=', userId).execute(); } - async removeAsset(assetId: string): Promise { - await this.db.deleteFrom('albums_assets_assets').where('albums_assets_assets.assetsId', '=', assetId).execute(); + @GenerateSql({ params: [[DummyValue.UUID]] }) + @Chunked() + async removeAssetsFromAll(assetIds: string[]): Promise { + await this.db.deleteFrom('albums_assets_assets').where('albums_assets_assets.assetsId', 'in', assetIds).execute(); } @Chunked({ paramIndex: 1 }) @@ -262,7 +266,7 @@ export class AlbumRepository { await this.addAssets(this.db, albumId, assetIds); } - create(album: Insertable, assetIds: string[], albumUsers: AlbumUserCreateDto[]): Promise { + create(album: Insertable, assetIds: string[], albumUsers: AlbumUserCreateDto[]) { return this.db.transaction().execute(async (tx) => { const newAlbum = await tx.insertInto('albums').values(album).returning('albums.id').executeTakeFirst(); @@ -290,11 +294,12 @@ export class AlbumRepository { .select(withOwner) .select(withAssets) .select(withAlbumUsers) - .executeTakeFirst() as unknown as Promise; + .$narrowType<{ assets: NotNull }>() + .executeTakeFirstOrThrow(); }); } - update(id: string, album: Updateable): Promise { + update(id: string, album: Updateable) { return this.db .updateTable('albums') .set(album) @@ -303,7 +308,7 @@ export class AlbumRepository { .returning(withOwner) .returning(withSharedLink) .returning(withAlbumUsers) - .executeTakeFirst() as unknown as Promise; + .executeTakeFirstOrThrow(); } async delete(id: string): Promise { diff --git a/server/src/repositories/asset-job.repository.ts b/server/src/repositories/asset-job.repository.ts index b507fa5445..6f86edaaa1 100644 --- a/server/src/repositories/asset-job.repository.ts +++ b/server/src/repositories/asset-job.repository.ts @@ -2,13 +2,21 @@ import { Injectable } from '@nestjs/common'; import { Kysely } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; -import { columns } from 'src/database'; +import { Asset, columns } from 'src/database'; import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { withExifInner, withFaces, withFiles } from 'src/entities/asset.entity'; -import { AssetFileType } from 'src/enum'; +import { AssetFileType, AssetType, AssetVisibility } from 'src/enum'; import { StorageAsset } from 'src/types'; -import { asUuid } from 'src/utils/database'; +import { + anyUuid, + asUuid, + toJson, + withExif, + withExifInner, + withFaces, + withFacesAndPeople, + withFiles, +} from 'src/utils/database'; @Injectable() export class AssetJobRepository { @@ -20,16 +28,7 @@ export class AssetJobRepository { .selectFrom('assets') .where('assets.id', '=', asUuid(id)) .leftJoin('smart_search', 'assets.id', 'smart_search.assetId') - .select((eb) => [ - 'id', - 'type', - 'ownerId', - 'duplicateId', - 'stackId', - 'isVisible', - 'smart_search.embedding', - withFiles(eb, AssetFileType.PREVIEW), - ]) + .select(['id', 'type', 'ownerId', 'duplicateId', 'stackId', 'visibility', 'smart_search.embedding']) .limit(1) .executeTakeFirst(); } @@ -62,7 +61,7 @@ export class AssetJobRepository { .select(['assets.id', 'assets.thumbhash']) .select(withFiles) .where('assets.deletedAt', 'is', null) - .where('assets.isVisible', '=', true) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) .$if(!force, (qb) => qb // If there aren't any entries, metadata extraction hasn't run yet which is required for thumbnails @@ -94,7 +93,7 @@ export class AssetJobRepository { .selectFrom('assets') .select([ 'assets.id', - 'assets.isVisible', + 'assets.visibility', 'assets.originalFileName', 'assets.originalPath', 'assets.ownerId', @@ -127,11 +126,48 @@ export class AssetJobRepository { .execute(); } + private assetsWithPreviews() { + return this.db + .selectFrom('assets') + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) + .where('assets.deletedAt', 'is', null) + .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') + .where('job_status.previewAt', 'is not', null); + } + + @GenerateSql({ params: [], stream: true }) + streamForSearchDuplicates(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) + .where('assets.deletedAt', 'is', null) + .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') + .$if(!force, (qb) => + qb + .innerJoin('asset_job_status as job_status', 'job_status.assetId', 'assets.id') + .where('job_status.duplicatesDetectedAt', 'is', null), + ) + .stream(); + } + + @GenerateSql({ params: [], stream: true }) + streamForEncodeClip(force?: boolean) { + return this.assetsWithPreviews() + .select(['assets.id']) + .$if(!force, (qb) => + qb.where((eb) => + eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id'))), + ), + ) + .stream(); + } + @GenerateSql({ params: [DummyValue.UUID] }) getForClipEncoding(id: string) { return this.db .selectFrom('assets') - .select(['assets.id', 'assets.isVisible']) + .select(['assets.id', 'assets.visibility']) .select((eb) => withFiles(eb, AssetFileType.PREVIEW)) .where('assets.id', '=', id) .executeTakeFirst(); @@ -141,7 +177,7 @@ export class AssetJobRepository { getForDetectFacesJob(id: string) { return this.db .selectFrom('assets') - .select(['assets.id', 'assets.isVisible']) + .select(['assets.id', 'assets.visibility']) .$call(withExifInner) .select((eb) => withFaces(eb, true)) .select((eb) => withFiles(eb, AssetFileType.PREVIEW)) @@ -149,6 +185,100 @@ export class AssetJobRepository { .executeTakeFirst(); } + @GenerateSql({ params: [[DummyValue.UUID]] }) + getForSyncAssets(ids: string[]) { + return this.db + .selectFrom('assets') + .select([ + 'assets.id', + 'assets.isOffline', + 'assets.libraryId', + 'assets.originalPath', + 'assets.status', + 'assets.fileModifiedAt', + ]) + .where('assets.id', '=', anyUuid(ids)) + .execute(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + getForAssetDeletion(id: string) { + return this.db + .selectFrom('assets') + .select([ + 'assets.id', + 'assets.visibility', + 'assets.libraryId', + 'assets.ownerId', + 'assets.livePhotoVideoId', + 'assets.sidecarPath', + 'assets.encodedVideoPath', + 'assets.originalPath', + ]) + .$call(withExif) + .select(withFacesAndPeople) + .select(withFiles) + .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') + .leftJoinLateral( + (eb) => + eb + .selectFrom('assets as stacked') + .select(['asset_stack.id', 'asset_stack.primaryAssetId']) + .select((eb) => eb.fn('array_agg', [eb.table('stacked')]).as('assets')) + .where('stacked.deletedAt', 'is not', null) + .where('stacked.visibility', '!=', AssetVisibility.ARCHIVE) + .whereRef('stacked.stackId', '=', 'asset_stack.id') + .groupBy('asset_stack.id') + .as('stacked_assets'), + (join) => join.on('asset_stack.id', 'is not', null), + ) + .select((eb) => toJson(eb, 'stacked_assets').as('stack')) + .where('assets.id', '=', id) + .executeTakeFirst(); + } + + @GenerateSql({ params: [], stream: true }) + streamForVideoConversion(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .where('assets.type', '=', AssetType.VIDEO) + .$if(!force, (qb) => + qb + .where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')])) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN), + ) + .where('assets.deletedAt', 'is', null) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + getForVideoConversion(id: string) { + return this.db + .selectFrom('assets') + .select(['assets.id', 'assets.ownerId', 'assets.originalPath', 'assets.encodedVideoPath']) + .where('assets.id', '=', id) + .where('assets.type', '=', AssetType.VIDEO) + .executeTakeFirst(); + } + + @GenerateSql({ params: [], stream: true }) + streamForMetadataExtraction(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .$if(!force, (qb) => + qb + .leftJoin('asset_job_status', 'asset_job_status.assetId', 'assets.id') + .where((eb) => + eb.or([eb('asset_job_status.metadataExtractedAt', 'is', null), eb('asset_job_status.assetId', 'is', null)]), + ) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN), + ) + .where('assets.deletedAt', 'is', null) + .stream(); + } + private storageTemplateAssetQuery() { return this.db .selectFrom('assets') @@ -190,4 +320,30 @@ export class AssetJobRepository { .where('assets.deletedAt', '<=', trashedBefore) .stream(); } + + @GenerateSql({ params: [], stream: true }) + streamForSidecar(force?: boolean) { + return this.db + .selectFrom('assets') + .select(['assets.id']) + .$if(!force, (qb) => + qb.where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])), + ) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) + .stream(); + } + + @GenerateSql({ params: [], stream: true }) + streamForDetectFacesJob(force?: boolean) { + return this.assetsWithPreviews() + .$if(!force, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) + .select(['assets.id']) + .orderBy('assets.createdAt', 'desc') + .stream(); + } + + @GenerateSql({ params: [DummyValue.DATE], stream: true }) + streamForMigrationJob() { + return this.db.selectFrom('assets').select(['id']).where('assets.deletedAt', 'is', null).stream(); + } } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 52c390c162..60744ddc5f 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,14 +1,20 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, Selectable, UpdateResult, Updateable, sql } from 'kysely'; +import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; +import { Stack } from 'src/database'; import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; +import { MapAsset } from 'src/dtos/asset-response.dto'; +import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; import { - AssetEntity, + anyUuid, + asUuid, hasPeople, - searchAssetBuilder, + removeUndefinedKeys, truncatedDate, + unnest, + withDefaultVisibility, withExif, withFaces, withFacesAndPeople, @@ -18,19 +24,15 @@ import { withSmartSearch, withTagId, withTags, -} from 'src/entities/asset.entity'; -import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum'; -import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository'; -import { anyUuid, asUuid, removeUndefinedKeys, unnest } from 'src/utils/database'; +} from 'src/utils/database'; import { globToSqlPattern } from 'src/utils/misc'; -import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination'; export type AssetStats = Record; export interface AssetStatsOptions { isFavorite?: boolean; - isArchived?: boolean; isTrashed?: boolean; + visibility?: AssetVisibility; } export interface LivePhotoSearchOptions { @@ -41,16 +43,6 @@ export interface LivePhotoSearchOptions { type: AssetType; } -export enum WithoutProperty { - THUMBNAIL = 'thumbnail', - ENCODED_VIDEO = 'encoded-video', - EXIF = 'exif', - SMART_SEARCH = 'smart-search', - DUPLICATE = 'duplicate', - FACES = 'faces', - SIDECAR = 'sidecar', -} - export enum WithProperty { SIDECAR = 'sidecar', } @@ -61,7 +53,6 @@ export enum TimeBucketSize { } export interface AssetBuilderOptions { - isArchived?: boolean; isFavorite?: boolean; isTrashed?: boolean; isDuplicate?: boolean; @@ -73,10 +64,10 @@ export interface AssetBuilderOptions { exifInfo?: boolean; status?: AssetStatus; assetType?: AssetType; + visibility?: AssetVisibility; } export interface TimeBucketOptions extends AssetBuilderOptions { - size: TimeBucketSize; order?: AssetOrder; } @@ -126,8 +117,6 @@ export interface AssetGetByChecksumOptions { libraryId?: string; } -export type AssetPathEntity = Pick; - export interface GetByIdsRelations { exifInfo?: boolean; faces?: { person?: boolean; withDeleted?: boolean }; @@ -141,12 +130,12 @@ export interface GetByIdsRelations { export interface DuplicateGroup { duplicateId: string; - assets: AssetEntity[]; + assets: MapAsset[]; } export interface DayOfYearAssets { yearsAgo: number; - assets: AssetEntity[]; + assets: MapAsset[]; } @Injectable() @@ -234,12 +223,12 @@ export class AssetRepository { .execute(); } - create(asset: Insertable): Promise { - return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirst() as any as Promise; + create(asset: Insertable) { + return this.db.insertInto('assets').values(asset).returningAll().executeTakeFirstOrThrow(); } - createAll(assets: Insertable[]): Promise { - return this.db.insertInto('assets').values(assets).returningAll().execute() as any as Promise; + createAll(assets: Insertable[]) { + return this.db.insertInto('assets').values(assets).returningAll().execute(); } @GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] }) @@ -269,8 +258,7 @@ export class AssetRepository { .where('asset_job_status.previewAt', 'is not', null) .where(sql`(assets."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`) .where('assets.ownerId', '=', anyUuid(ownerIds)) - .where('assets.isVisible', '=', true) - .where('assets.isArchived', '=', false) + .where('assets.visibility', '=', AssetVisibility.TIMELINE) .where((eb) => eb.exists((qb) => qb @@ -299,56 +287,13 @@ export class AssetRepository { @GenerateSql({ params: [[DummyValue.UUID]] }) @ChunkedArray() - async getByIds( - ids: string[], - { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}, - ): Promise { - const res = await this.db - .selectFrom('assets') - .selectAll('assets') - .where('assets.id', '=', anyUuid(ids)) - .$if(!!exifInfo, withExif) - .$if(!!faces, (qb) => - qb.select((eb) => - faces?.person ? withFacesAndPeople(eb, faces.withDeleted) : withFaces(eb, faces?.withDeleted), - ), - ) - .$if(!!files, (qb) => qb.select(withFiles)) - .$if(!!library, (qb) => qb.select(withLibrary)) - .$if(!!owner, (qb) => qb.select(withOwner)) - .$if(!!smartSearch, withSmartSearch) - .$if(!!stack, (qb) => - qb - .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') - .$if(!stack!.assets, (qb) => qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).as('stack'))) - .$if(!!stack!.assets, (qb) => - qb - .leftJoinLateral( - (eb) => - eb - .selectFrom('assets as stacked') - .selectAll('asset_stack') - .select((eb) => eb.fn('array_agg', [eb.table('stacked')]).as('assets')) - .whereRef('stacked.stackId', '=', 'asset_stack.id') - .whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId') - .where('stacked.deletedAt', 'is', null) - .where('stacked.isArchived', '=', false) - .groupBy('asset_stack.id') - .as('stacked_assets'), - (join) => join.on('asset_stack.id', 'is not', null), - ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')), - ), - ) - .$if(!!tags, (qb) => qb.select(withTags)) - .execute(); - - return res as any as AssetEntity[]; + getByIds(ids: string[]) { + return this.db.selectFrom('assets').selectAll('assets').where('assets.id', '=', anyUuid(ids)).execute(); } @GenerateSql({ params: [[DummyValue.UUID]] }) @ChunkedArray() - getByIdsWithAllRelations(ids: string[]): Promise { + getByIdsWithAllRelationsButStacks(ids: string[]) { return this.db .selectFrom('assets') .selectAll('assets') @@ -356,23 +301,8 @@ export class AssetRepository { .select(withTags) .$call(withExif) .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') - .leftJoinLateral( - (eb) => - eb - .selectFrom('assets as stacked') - .selectAll('asset_stack') - .select((eb) => eb.fn('array_agg', [eb.table('stacked')]).as('assets')) - .whereRef('stacked.stackId', '=', 'asset_stack.id') - .whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId') - .where('stacked.deletedAt', 'is', null) - .where('stacked.isArchived', '=', false) - .groupBy('asset_stack.id') - .as('stacked_assets'), - (join) => join.on('asset_stack.id', 'is not', null), - ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')) .where('assets.id', '=', anyUuid(ids)) - .execute() as any as Promise; + .execute(); } @GenerateSql({ params: [DummyValue.UUID] }) @@ -392,36 +322,15 @@ export class AssetRepository { return assets.map((asset) => asset.deviceAssetId); } - getByUserId( - pagination: PaginationOptions, - userId: string, - options: Omit = {}, - ): Paginated { - return this.getAll(pagination, { ...options, userIds: [userId] }); - } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) - getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise { + getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string) { return this.db .selectFrom('assets') .selectAll('assets') .where('libraryId', '=', asUuid(libraryId)) .where('originalPath', '=', originalPath) .limit(1) - .executeTakeFirst() as any as Promise; - } - - async getAll( - pagination: PaginationOptions, - { orderDirection, ...options }: AssetSearchOptions = {}, - ): Paginated { - const builder = searchAssetBuilder(this.db, options) - .select(withFiles) - .orderBy('assets.createdAt', orderDirection ?? 'asc') - .limit(pagination.take + 1) - .offset(pagination.skip ?? 0); - const items = await builder.execute(); - return paginationHelper(items as any as AssetEntity[], pagination.take); + .executeTakeFirst(); } /** @@ -438,7 +347,7 @@ export class AssetRepository { .select(['deviceAssetId']) .where('ownerId', '=', asUuid(ownerId)) .where('deviceId', '=', deviceId) - .where('isVisible', '=', true) + .where('visibility', '!=', AssetVisibility.HIDDEN) .where('deletedAt', 'is', null) .execute(); @@ -456,23 +365,22 @@ export class AssetRepository { } @GenerateSql({ params: [DummyValue.UUID] }) - getById( - id: string, - { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}, - ): Promise { + getById(id: string, { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}) { return this.db .selectFrom('assets') .selectAll('assets') .where('assets.id', '=', asUuid(id)) .$if(!!exifInfo, withExif) - .$if(!!faces, (qb) => qb.select(faces?.person ? withFacesAndPeople : withFaces)) + .$if(!!faces, (qb) => qb.select(faces?.person ? withFacesAndPeople : withFaces).$narrowType<{ faces: NotNull }>()) .$if(!!library, (qb) => qb.select(withLibrary)) .$if(!!owner, (qb) => qb.select(withOwner)) .$if(!!smartSearch, withSmartSearch) .$if(!!stack, (qb) => qb .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') - .$if(!stack!.assets, (qb) => qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).as('stack'))) + .$if(!stack!.assets, (qb) => + qb.select((eb) => eb.fn.toJson(eb.table('asset_stack')).$castTo().as('stack')), + ) .$if(!!stack!.assets, (qb) => qb .leftJoinLateral( @@ -484,18 +392,18 @@ export class AssetRepository { .whereRef('stacked.stackId', '=', 'asset_stack.id') .whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId') .where('stacked.deletedAt', 'is', null) - .where('stacked.isArchived', '=', false) + .where('stacked.visibility', '=', AssetVisibility.TIMELINE) .groupBy('asset_stack.id') .as('stacked_assets'), (join) => join.on('asset_stack.id', 'is not', null), ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')), + .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo().as('stack')), ), ) .$if(!!files, (qb) => qb.select(withFiles)) .$if(!!tags, (qb) => qb.select(withTags)) .limit(1) - .executeTakeFirst() as any as Promise; + .executeTakeFirst(); } @GenerateSql({ params: [[DummyValue.UUID], { deviceId: DummyValue.STRING }] }) @@ -524,7 +432,7 @@ export class AssetRepository { .execute(); } - async update(asset: Updateable & { id: string }): Promise { + async update(asset: Updateable & { id: string }) { const value = omitBy(asset, isUndefined); delete value.id; if (!isEmpty(value)) { @@ -534,10 +442,10 @@ export class AssetRepository { .selectAll('assets') .$call(withExif) .$call((qb) => qb.select(withFacesAndPeople)) - .executeTakeFirst() as Promise; + .executeTakeFirst(); } - return this.getById(asset.id, { exifInfo: true, faces: { person: true } }) as Promise; + return this.getById(asset.id, { exifInfo: true, faces: { person: true } }); } async remove(asset: { id: string }): Promise { @@ -545,7 +453,7 @@ export class AssetRepository { } @GenerateSql({ params: [{ ownerId: DummyValue.UUID, libraryId: DummyValue.UUID, checksum: DummyValue.BUFFER }] }) - getByChecksum({ ownerId, libraryId, checksum }: AssetGetByChecksumOptions): Promise { + getByChecksum({ ownerId, libraryId, checksum }: AssetGetByChecksumOptions) { return this.db .selectFrom('assets') .selectAll('assets') @@ -553,17 +461,17 @@ export class AssetRepository { .where('checksum', '=', checksum) .$call((qb) => (libraryId ? qb.where('libraryId', '=', asUuid(libraryId)) : qb.where('libraryId', 'is', null))) .limit(1) - .executeTakeFirst() as Promise; + .executeTakeFirst(); } @GenerateSql({ params: [DummyValue.UUID, [DummyValue.BUFFER]] }) - getByChecksums(userId: string, checksums: Buffer[]): Promise { + getByChecksums(userId: string, checksums: Buffer[]) { return this.db .selectFrom('assets') .select(['id', 'checksum', 'deletedAt']) .where('ownerId', '=', asUuid(userId)) .where('checksum', 'in', checksums) - .execute() as any as Promise; + .execute(); } @GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] }) @@ -580,7 +488,7 @@ export class AssetRepository { return asset?.id; } - findLivePhotoMatch(options: LivePhotoSearchOptions): Promise { + findLivePhotoMatch(options: LivePhotoSearchOptions) { const { ownerId, otherAssetId, livePhotoCID, type } = options; return this.db .selectFrom('assets') @@ -591,81 +499,10 @@ export class AssetRepository { .where('type', '=', type) .where('exif.livePhotoCID', '=', livePhotoCID) .limit(1) - .executeTakeFirst() as Promise; + .executeTakeFirst(); } - @GenerateSql( - ...Object.values(WithProperty).map((property) => ({ - name: property, - params: [DummyValue.PAGINATION, property], - })), - ) - async getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated { - const items = await this.db - .selectFrom('assets') - .selectAll('assets') - .$if(property === WithoutProperty.DUPLICATE, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId') - .where('job_status.duplicatesDetectedAt', 'is', null) - .where('job_status.previewAt', 'is not', null) - .where((eb) => eb.exists(eb.selectFrom('smart_search').where('assetId', '=', eb.ref('assets.id')))) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.ENCODED_VIDEO, (qb) => - qb - .where('assets.type', '=', AssetType.VIDEO) - .where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')])), - ) - .$if(property === WithoutProperty.EXIF, (qb) => - qb - .leftJoin('asset_job_status as job_status', 'assets.id', 'job_status.assetId') - .where((eb) => eb.or([eb('job_status.metadataExtractedAt', 'is', null), eb('assetId', 'is', null)])) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.FACES, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('job_status.previewAt', 'is not', null) - .where('job_status.facesRecognizedAt', 'is', null) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.SIDECAR, (qb) => - qb - .where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])) - .where('assets.isVisible', '=', true), - ) - .$if(property === WithoutProperty.SMART_SEARCH, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('job_status.previewAt', 'is not', null) - .where('assets.isVisible', '=', true) - .where((eb) => - eb.not((eb) => eb.exists(eb.selectFrom('smart_search').whereRef('assetId', '=', 'assets.id'))), - ), - ) - .$if(property === WithoutProperty.THUMBNAIL, (qb) => - qb - .innerJoin('asset_job_status as job_status', 'assetId', 'assets.id') - .where('assets.isVisible', '=', true) - .where((eb) => - eb.or([ - eb('job_status.previewAt', 'is', null), - eb('job_status.thumbnailAt', 'is', null), - eb('assets.thumbhash', 'is', null), - ]), - ), - ) - .where('deletedAt', 'is', null) - .limit(pagination.take + 1) - .offset(pagination.skip ?? 0) - .orderBy('createdAt') - .execute(); - - return paginationHelper(items as any as AssetEntity[], pagination.take); - } - - getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise { + getStatistics(ownerId: string, { visibility, isFavorite, isTrashed }: AssetStatsOptions): Promise { return this.db .selectFrom('assets') .select((eb) => eb.fn.countAll().filterWhere('type', '=', AssetType.AUDIO).as(AssetType.AUDIO)) @@ -673,25 +510,25 @@ export class AssetRepository { .select((eb) => eb.fn.countAll().filterWhere('type', '=', AssetType.VIDEO).as(AssetType.VIDEO)) .select((eb) => eb.fn.countAll().filterWhere('type', '=', AssetType.OTHER).as(AssetType.OTHER)) .where('ownerId', '=', asUuid(ownerId)) - .where('isVisible', '=', true) - .$if(isArchived !== undefined, (qb) => qb.where('isArchived', '=', isArchived!)) + .$if(visibility === undefined, withDefaultVisibility) + .$if(!!visibility, (qb) => qb.where('assets.visibility', '=', visibility!)) .$if(isFavorite !== undefined, (qb) => qb.where('isFavorite', '=', isFavorite!)) .$if(!!isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED)) .where('deletedAt', isTrashed ? 'is not' : 'is', null) .executeTakeFirstOrThrow(); } - getRandom(userIds: string[], take: number): Promise { + getRandom(userIds: string[], take: number) { return this.db .selectFrom('assets') .selectAll('assets') .$call(withExif) .where('ownerId', '=', anyUuid(userIds)) - .where('isVisible', '=', true) + .where('visibility', '!=', AssetVisibility.HIDDEN) .where('deletedAt', 'is', null) .orderBy((eb) => eb.fn('random')) .limit(take) - .execute() as any as Promise; + .execute(); } @GenerateSql({ params: [{ size: TimeBucketSize.MONTH }] }) @@ -701,10 +538,11 @@ export class AssetRepository { .with('assets', (qb) => qb .selectFrom('assets') - .select(truncatedDate(options.size).as('timeBucket')) + .select(truncatedDate(TimeBucketSize.MONTH).as('timeBucket')) .$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED)) .where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null) - .where('assets.isVisible', '=', true) + .$if(options.visibility === undefined, withDefaultVisibility) + .$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!)) .$if(!!options.albumId, (qb) => qb .innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId') @@ -721,7 +559,6 @@ export class AssetRepository { .where((eb) => eb.or([eb('assets.stackId', 'is', null), eb(eb.table('asset_stack'), 'is not', null)])), ) .$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!))) - .$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!)) .$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!)) .$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!)) .$if(options.isDuplicate !== undefined, (qb) => @@ -743,57 +580,130 @@ export class AssetRepository { ); } - @GenerateSql({ params: [DummyValue.TIME_BUCKET, { size: TimeBucketSize.MONTH, withStacked: true }] }) - async getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise { - return this.db - .selectFrom('assets') - .selectAll('assets') - .$call(withExif) - .$if(!!options.albumId, (qb) => + @GenerateSql({ + params: [DummyValue.TIME_BUCKET, { withStacked: true }], + }) + getTimeBucket(timeBucket: string, options: TimeBucketOptions) { + const query = this.db + .with('cte', (qb) => qb - .innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id') - .where('albums_assets_assets.albumsId', '=', options.albumId!), + .selectFrom('assets') + .innerJoin('exif', 'assets.id', 'exif.assetId') + .select((eb) => [ + 'assets.duration', + 'assets.id', + 'assets.visibility', + 'assets.isFavorite', + sql`assets.type = 'IMAGE'`.as('isImage'), + sql`assets."deletedAt" is not null`.as('isTrashed'), + 'assets.livePhotoVideoId', + 'assets.localDateTime', + 'assets.ownerId', + 'assets.status', + eb.fn('encode', ['assets.thumbhash', sql.lit('base64')]).as('thumbhash'), + 'exif.city', + 'exif.country', + 'exif.projectionType', + eb.fn + .coalesce( + eb + .case() + .when(sql`exif."exifImageHeight" = 0 or exif."exifImageWidth" = 0`) + .then(eb.lit(1)) + .when('exif.orientation', 'in', sql`('5', '6', '7', '8', '-90', '90')`) + .then(sql`round(exif."exifImageHeight"::numeric / exif."exifImageWidth"::numeric, 3)`) + .else(sql`round(exif."exifImageWidth"::numeric / exif."exifImageHeight"::numeric, 3)`) + .end(), + eb.lit(1), + ) + .as('ratio'), + ]) + .where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null) + .$if(options.visibility == undefined, withDefaultVisibility) + .$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!)) + .where(truncatedDate(TimeBucketSize.MONTH), '=', timeBucket.replace(/^[+-]/, '')) + .$if(!!options.albumId, (qb) => + qb.where((eb) => + eb.exists( + eb + .selectFrom('albums_assets_assets') + .whereRef('albums_assets_assets.assetsId', '=', 'assets.id') + .where('albums_assets_assets.albumsId', '=', asUuid(options.albumId!)), + ), + ), + ) + .$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!])) + .$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!))) + .$if(options.visibility == undefined, withDefaultVisibility) + .$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!)) + .$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!)) + .$if(!!options.withStacked, (qb) => + qb + .where((eb) => + eb.not( + eb.exists( + eb + .selectFrom('asset_stack') + .whereRef('asset_stack.id', '=', 'assets.stackId') + .whereRef('asset_stack.primaryAssetId', '!=', 'assets.id'), + ), + ), + ) + .leftJoinLateral( + (eb) => + eb + .selectFrom('assets as stacked') + .select(sql`array[stacked."stackId"::text, count('stacked')::text]`.as('stack')) + .whereRef('stacked.stackId', '=', 'assets.stackId') + .where('stacked.deletedAt', 'is', null) + .where('stacked.visibility', '!=', AssetVisibility.ARCHIVE) + .groupBy('stacked.stackId') + .as('stacked_assets'), + (join) => join.onTrue(), + ) + .select('stack'), + ) + .$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!)) + .$if(options.isDuplicate !== undefined, (qb) => + qb.where('assets.duplicateId', options.isDuplicate ? 'is not' : 'is', null), + ) + .$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED)) + .$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)) + .orderBy('assets.localDateTime', options.order ?? 'desc'), ) - .$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!])) - .$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!))) - .$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!)) - .$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!)) - .$if(!!options.withStacked, (qb) => + .with('agg', (qb) => qb - .leftJoin('asset_stack', 'asset_stack.id', 'assets.stackId') - .where((eb) => - eb.or([eb('asset_stack.primaryAssetId', '=', eb.ref('assets.id')), eb('assets.stackId', 'is', null)]), - ) - .leftJoinLateral( - (eb) => - eb - .selectFrom('assets as stacked') - .selectAll('asset_stack') - .select((eb) => eb.fn.count(eb.table('stacked')).as('assetCount')) - .whereRef('stacked.stackId', '=', 'asset_stack.id') - .where('stacked.deletedAt', 'is', null) - .where('stacked.isArchived', '=', false) - .groupBy('asset_stack.id') - .as('stacked_assets'), - (join) => join.on('asset_stack.id', 'is not', null), - ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')), + .selectFrom('cte') + .select((eb) => [ + eb.fn.coalesce(eb.fn('array_agg', ['city']), sql.lit('{}')).as('city'), + eb.fn.coalesce(eb.fn('array_agg', ['country']), sql.lit('{}')).as('country'), + eb.fn.coalesce(eb.fn('array_agg', ['duration']), sql.lit('{}')).as('duration'), + eb.fn.coalesce(eb.fn('array_agg', ['id']), sql.lit('{}')).as('id'), + eb.fn.coalesce(eb.fn('array_agg', ['visibility']), sql.lit('{}')).as('visibility'), + eb.fn.coalesce(eb.fn('array_agg', ['isFavorite']), sql.lit('{}')).as('isFavorite'), + eb.fn.coalesce(eb.fn('array_agg', ['isImage']), sql.lit('{}')).as('isImage'), + // TODO: isTrashed is redundant as it will always be all true or false depending on the options + eb.fn.coalesce(eb.fn('array_agg', ['isTrashed']), sql.lit('{}')).as('isTrashed'), + eb.fn.coalesce(eb.fn('array_agg', ['livePhotoVideoId']), sql.lit('{}')).as('livePhotoVideoId'), + eb.fn.coalesce(eb.fn('array_agg', ['localDateTime']), sql.lit('{}')).as('localDateTime'), + eb.fn.coalesce(eb.fn('array_agg', ['ownerId']), sql.lit('{}')).as('ownerId'), + eb.fn.coalesce(eb.fn('array_agg', ['projectionType']), sql.lit('{}')).as('projectionType'), + eb.fn.coalesce(eb.fn('array_agg', ['ratio']), sql.lit('{}')).as('ratio'), + eb.fn.coalesce(eb.fn('array_agg', ['status']), sql.lit('{}')).as('status'), + eb.fn.coalesce(eb.fn('array_agg', ['thumbhash']), sql.lit('{}')).as('thumbhash'), + ]) + .$if(!!options.withStacked, (qb) => + qb.select((eb) => eb.fn.coalesce(eb.fn('json_agg', ['stack']), sql.lit('[]')).as('stack')), + ), ) - .$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!)) - .$if(options.isDuplicate !== undefined, (qb) => - qb.where('assets.duplicateId', options.isDuplicate ? 'is not' : 'is', null), - ) - .$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED)) - .$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)) - .where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null) - .where('assets.isVisible', '=', true) - .where(truncatedDate(options.size), '=', timeBucket.replace(/^[+-]/, '')) - .orderBy('assets.localDateTime', options.order ?? 'desc') - .execute() as any as Promise; + .selectFrom('agg') + .select(sql`to_json(agg)::text`.as('assets')); + + return query.executeTakeFirstOrThrow(); } @GenerateSql({ params: [DummyValue.UUID] }) - getDuplicates(userId: string): Promise { + getDuplicates(userId: string) { return ( this.db .with('duplicates', (qb) => @@ -810,11 +720,14 @@ export class AssetRepository { (join) => join.onTrue(), ) .select('assets.duplicateId') - .select((eb) => eb.fn('jsonb_agg', [eb.table('asset')]).as('assets')) + .select((eb) => + eb.fn.jsonAgg('asset').orderBy('assets.localDateTime', 'asc').$castTo().as('assets'), + ) .where('assets.ownerId', '=', asUuid(userId)) .where('assets.duplicateId', 'is not', null) + .$narrowType<{ duplicateId: NotNull }>() .where('assets.deletedAt', 'is', null) - .where('assets.isVisible', '=', true) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) .where('assets.stackId', 'is', null) .groupBy('assets.duplicateId'), ) @@ -822,7 +735,7 @@ export class AssetRepository { qb .selectFrom('duplicates') .select('duplicateId') - .where((eb) => eb(eb.fn('jsonb_array_length', ['assets']), '=', 1)), + .where((eb) => eb(eb.fn('json_array_length', ['assets']), '=', 1)), ) .with('removed_unique', (qb) => qb @@ -833,19 +746,16 @@ export class AssetRepository { ) .selectFrom('duplicates') .selectAll() - // TODO: compare with filtering by jsonb_array_length > 1 + // TODO: compare with filtering by json_array_length > 1 .where(({ not, exists }) => not(exists((eb) => eb.selectFrom('unique').whereRef('unique.duplicateId', '=', 'duplicates.duplicateId'))), ) - .execute() as any as Promise + .execute() ); } @GenerateSql({ params: [DummyValue.UUID, { minAssetsPerField: 5, maxFields: 12 }] }) - async getAssetIdByCity( - ownerId: string, - { minAssetsPerField, maxFields }: AssetExploreFieldOptions, - ): Promise> { + async getAssetIdByCity(ownerId: string, { minAssetsPerField, maxFields }: AssetExploreFieldOptions) { const items = await this.db .with('cities', (qb) => qb @@ -860,15 +770,15 @@ export class AssetRepository { .innerJoin('cities', 'exif.city', 'cities.city') .distinctOn('exif.city') .select(['assetId as data', 'exif.city as value']) + .$narrowType<{ value: NotNull }>() .where('ownerId', '=', asUuid(ownerId)) - .where('isVisible', '=', true) - .where('isArchived', '=', false) + .where('visibility', '=', AssetVisibility.TIMELINE) .where('type', '=', AssetType.IMAGE) .where('deletedAt', 'is', null) .limit(maxFields) .execute(); - return { fieldName: 'exifInfo.city', items: items as SearchExploreItemSet }; + return { fieldName: 'exifInfo.city', items }; } @GenerateSql({ @@ -881,7 +791,7 @@ export class AssetRepository { }, ], }) - getAllForUserFullSync(options: AssetFullSyncOptions): Promise { + getAllForUserFullSync(options: AssetFullSyncOptions) { const { ownerId, lastId, updatedUntil, limit } = options; return this.db .selectFrom('assets') @@ -899,18 +809,18 @@ export class AssetRepository { .as('stacked_assets'), (join) => join.on('asset_stack.id', 'is not', null), ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')) + .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo().as('stack')) .where('assets.ownerId', '=', asUuid(ownerId)) - .where('assets.isVisible', '=', true) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) .where('assets.updatedAt', '<=', updatedUntil) .$if(!!lastId, (qb) => qb.where('assets.id', '>', lastId!)) .orderBy('assets.id') .limit(limit) - .execute() as any as Promise; + .execute(); } @GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE, limit: 100 }] }) - async getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise { + async getChangedDeltaSync(options: AssetDeltaSyncOptions) { return this.db .selectFrom('assets') .selectAll('assets') @@ -927,12 +837,12 @@ export class AssetRepository { .as('stacked_assets'), (join) => join.on('asset_stack.id', 'is not', null), ) - .select((eb) => eb.fn.toJson(eb.table('stacked_assets')).as('stack')) + .select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo()).as('stack')) .where('assets.ownerId', '=', anyUuid(options.userIds)) - .where('assets.isVisible', '=', true) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) .where('assets.updatedAt', '>', options.updatedAfter) .limit(options.limit) - .execute() as any as Promise; + .execute(); } async upsertFile(file: Pick, 'assetId' | 'path' | 'type'>): Promise { @@ -976,9 +886,7 @@ export class AssetRepository { .execute(); } - @GenerateSql({ - params: [{ libraryId: DummyValue.UUID, importPaths: [DummyValue.STRING], exclusionPatterns: [DummyValue.STRING] }], - }) + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.STRING], [DummyValue.STRING]] }) async detectOfflineExternalAssets( libraryId: string, importPaths: string[], @@ -1005,9 +913,7 @@ export class AssetRepository { .executeTakeFirstOrThrow(); } - @GenerateSql({ - params: [{ libraryId: DummyValue.UUID, paths: [DummyValue.STRING] }], - }) + @GenerateSql({ params: [DummyValue.UUID, [DummyValue.STRING]] }) async filterNewExternalAssetPaths(libraryId: string, paths: string[]): Promise { const result = await this.db .selectFrom(unnest(paths).as('path')) diff --git a/server/src/repositories/audit.repository.ts b/server/src/repositories/audit.repository.ts index 5961e4f25d..1193e26ebe 100644 --- a/server/src/repositories/audit.repository.ts +++ b/server/src/repositories/audit.repository.ts @@ -29,14 +29,15 @@ export class AuditRepository { .$if(!!options.entityType, (qb) => qb.where('audit.entityType', '=', options.entityType!)) .where('audit.ownerId', 'in', options.userIds) .distinctOn(['audit.entityId', 'audit.entityType']) - .orderBy(['audit.entityId desc', 'audit.entityType desc', 'audit.createdAt desc']) + .orderBy('audit.entityId', 'desc') + .orderBy('audit.entityType', 'desc') + .orderBy('audit.createdAt', 'desc') .select('audit.entityId') .execute(); return records.map(({ entityId }) => entityId); } - @GenerateSql({ params: [DummyValue.DATE] }) async removeBefore(before: Date): Promise { await this.db.deleteFrom('audit').where('createdAt', '<', before).execute(); } diff --git a/server/src/repositories/config.repository.spec.ts b/server/src/repositories/config.repository.spec.ts index 888d5c33ec..238b48bcef 100644 --- a/server/src/repositories/config.repository.spec.ts +++ b/server/src/repositories/config.repository.spec.ts @@ -23,6 +23,7 @@ const resetEnv = () => { 'DB_USERNAME', 'DB_PASSWORD', 'DB_DATABASE_NAME', + 'DB_SSL_MODE', 'DB_SKIP_MIGRATIONS', 'DB_VECTOR_EXTENSION', @@ -80,27 +81,29 @@ describe('getEnv', () => { const { database } = getEnv(); expect(database).toEqual({ config: { - kysely: expect.objectContaining({ - host: 'database', - port: 5432, - database: 'immich', - username: 'postgres', - password: 'postgres', - }), - typeorm: expect.objectContaining({ - type: 'postgres', - host: 'database', - port: 5432, - database: 'immich', - username: 'postgres', - password: 'postgres', - }), + connectionType: 'parts', + host: 'database', + port: 5432, + database: 'immich', + username: 'postgres', + password: 'postgres', }, skipMigrations: false, - vectorExtension: 'vectors', + vectorExtension: undefined, }); }); + it('should validate DB_SSL_MODE', () => { + process.env.DB_SSL_MODE = 'invalid'; + expect(() => getEnv()).toThrowError('Invalid environment variables: DB_SSL_MODE'); + }); + + it('should accept a valid DB_SSL_MODE', () => { + process.env.DB_SSL_MODE = 'prefer'; + const { database } = getEnv(); + expect(database.config).toMatchObject(expect.objectContaining({ ssl: 'prefer' })); + }); + it('should allow skipping migrations', () => { process.env.DB_SKIP_MIGRATIONS = 'true'; const { database } = getEnv(); @@ -110,88 +113,9 @@ describe('getEnv', () => { it('should use DB_URL', () => { process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich'; const { database } = getEnv(); - expect(database.config.kysely).toMatchObject({ - host: 'database1', - password: 'postgres2', - user: 'postgres1', - port: 54_320, - database: 'immich', - }); - }); - - it('should handle sslmode=require', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=require'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=prefer', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=prefer'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=verify-ca', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-ca'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=verify-full', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-full'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: {} }); - }); - - it('should handle sslmode=no-verify', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=no-verify'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: { rejectUnauthorized: false } }); - }); - - it('should handle ssl=true', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?ssl=true'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ ssl: true }); - }); - - it('should reject invalid ssl', () => { - process.env.DB_URL = 'postgres://postgres1:postgres2@database1:54320/immich?ssl=invalid'; - - expect(() => getEnv()).toThrowError('Invalid ssl option: invalid'); - }); - - it('should handle socket: URLs', () => { - process.env.DB_URL = 'socket:/run/postgresql?db=database1'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ - host: '/run/postgresql', - database: 'database1', - }); - }); - - it('should handle sockets in postgres: URLs', () => { - process.env.DB_URL = 'postgres:///database2?host=/path/to/socket'; - - const { database } = getEnv(); - - expect(database.config.kysely).toMatchObject({ - host: '/path/to/socket', - database: 'database2', + expect(database.config).toMatchObject({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich', }); }); }); diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index f689641d4f..9a0a24f70f 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -7,8 +7,7 @@ import { Request, Response } from 'express'; import { RedisOptions } from 'ioredis'; import { CLS_ID, ClsModuleOptions } from 'nestjs-cls'; import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces'; -import { join, resolve } from 'node:path'; -import { parse } from 'pg-connection-string'; +import { join } from 'node:path'; import { citiesFile, excludePaths, IWorker } from 'src/constants'; import { Telemetry } from 'src/decorators'; import { EnvDto } from 'src/dtos/env.dto'; @@ -22,9 +21,7 @@ import { QueueName, } from 'src/enum'; import { DatabaseConnectionParams, VectorExtension } from 'src/types'; -import { isValidSsl, PostgresConnectionConfig } from 'src/utils/database'; import { setDifference } from 'src/utils/set'; -import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js'; export interface EnvData { host?: string; @@ -59,9 +56,9 @@ export interface EnvData { }; database: { - config: { typeorm: PostgresConnectionOptions & DatabaseConnectionParams; kysely: PostgresConnectionConfig }; + config: DatabaseConnectionParams; skipMigrations: boolean; - vectorExtension: VectorExtension; + vectorExtension?: VectorExtension; }; licensePublicKey: { @@ -152,14 +149,10 @@ const getEnv = (): EnvData => { const isProd = environment === ImmichEnvironment.PRODUCTION; const buildFolder = dto.IMMICH_BUILD_DATA || '/build'; const folders = { - // eslint-disable-next-line unicorn/prefer-module - dist: resolve(`${__dirname}/..`), geodata: join(buildFolder, 'geodata'), web: join(buildFolder, 'www'), }; - const databaseUrl = dto.DB_URL; - let redisConfig = { host: dto.REDIS_HOSTNAME || 'redis', port: dto.REDIS_PORT || 6379, @@ -191,29 +184,32 @@ const getEnv = (): EnvData => { } } - const parts = { - connectionType: 'parts', - host: dto.DB_HOSTNAME || 'database', - port: dto.DB_PORT || 5432, - username: dto.DB_USERNAME || 'postgres', - password: dto.DB_PASSWORD || 'postgres', - database: dto.DB_DATABASE_NAME || 'immich', - } as const; + const databaseConnection: DatabaseConnectionParams = dto.DB_URL + ? { connectionType: 'url', url: dto.DB_URL } + : { + connectionType: 'parts', + host: dto.DB_HOSTNAME || 'database', + port: dto.DB_PORT || 5432, + username: dto.DB_USERNAME || 'postgres', + password: dto.DB_PASSWORD || 'postgres', + database: dto.DB_DATABASE_NAME || 'immich', + ssl: dto.DB_SSL_MODE || undefined, + }; - let parsedOptions: PostgresConnectionConfig = parts; - if (dto.DB_URL) { - const parsed = parse(dto.DB_URL); - if (!isValidSsl(parsed.ssl)) { - throw new Error(`Invalid ssl option: ${parsed.ssl}`); + let vectorExtension: VectorExtension | undefined; + switch (dto.DB_VECTOR_EXTENSION) { + case 'pgvector': { + vectorExtension = DatabaseExtension.VECTOR; + break; + } + case 'pgvecto.rs': { + vectorExtension = DatabaseExtension.VECTORS; + break; + } + case 'vectorchord': { + vectorExtension = DatabaseExtension.VECTORCHORD; + break; } - - parsedOptions = { - ...parsed, - ssl: parsed.ssl, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }; } return { @@ -269,23 +265,9 @@ const getEnv = (): EnvData => { }, database: { - config: { - typeorm: { - type: 'postgres', - entities: [], - migrations: [`${folders.dist}/migrations` + '/*.{js,ts}'], - subscribers: [], - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - ...(databaseUrl ? { connectionType: 'url', url: databaseUrl } : parts), - }, - kysely: parsedOptions, - }, - + config: databaseConnection, skipMigrations: dto.DB_SKIP_MIGRATIONS ?? false, - vectorExtension: dto.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS, + vectorExtension, }, licensePublicKey: isProd ? productionKeys : stagingKeys, diff --git a/server/src/repositories/crypto.repository.ts b/server/src/repositories/crypto.repository.ts index e471ccb031..c3136db456 100644 --- a/server/src/repositories/crypto.repository.ts +++ b/server/src/repositories/crypto.repository.ts @@ -54,7 +54,7 @@ export class CryptoRepository { }); } - newPassword(bytes: number) { + randomBytesAsText(bytes: number) { return randomBytes(bytes).toString('base64').replaceAll(/\W/g, ''); } } diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index ec0b263408..1e8e147c43 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -2,23 +2,63 @@ import { Injectable } from '@nestjs/common'; import AsyncLock from 'async-lock'; import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; -import { existsSync } from 'node:fs'; import { readdir } from 'node:fs/promises'; -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; import semver from 'semver'; -import { EXTENSION_NAMES, POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants'; +import { + EXTENSION_NAMES, + POSTGRES_VERSION_RANGE, + VECTOR_EXTENSIONS, + VECTOR_INDEX_TABLES, + VECTOR_VERSION_RANGE, + VECTORCHORD_LIST_SLACK_FACTOR, + VECTORCHORD_VERSION_RANGE, + VECTORS_VERSION_RANGE, +} from 'src/constants'; import { DB } from 'src/db'; import { GenerateSql } from 'src/decorators'; import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; +import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; -import { DataSource } from 'typeorm'; +import { DataSource, QueryRunner } from 'typeorm'; + +export let cachedVectorExtension: VectorExtension | undefined; +export async function getVectorExtension(runner: Kysely | QueryRunner): Promise { + if (cachedVectorExtension) { + return cachedVectorExtension; + } + + cachedVectorExtension = new ConfigRepository().getEnv().database.vectorExtension; + if (cachedVectorExtension) { + return cachedVectorExtension; + } + + let availableExtensions: { name: VectorExtension }[]; + const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`; + if (runner instanceof Kysely) { + const { rows } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); + availableExtensions = rows; + } else { + availableExtensions = (await runner.query(query)) as { name: VectorExtension }[]; + } + const extensionNames = new Set(availableExtensions.map((row) => row.name)); + cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext)); + if (!cachedVectorExtension) { + throw new Error(`No vector extension found. Available extensions: ${VECTOR_EXTENSIONS.join(', ')}`); + } + return cachedVectorExtension; +} + +export const probes: Record = { + [VectorIndex.CLIP]: 1, + [VectorIndex.FACE]: 1, +}; @Injectable() export class DatabaseRepository { - private vectorExtension: VectorExtension; private readonly asyncLock = new AsyncLock(); constructor( @@ -26,7 +66,6 @@ export class DatabaseRepository { private logger: LoggingRepository, private configRepository: ConfigRepository, ) { - this.vectorExtension = configRepository.getEnv().database.vectorExtension; this.logger.setContext(DatabaseRepository.name); } @@ -34,18 +73,35 @@ export class DatabaseRepository { await this.db.destroy(); } - @GenerateSql({ params: [DatabaseExtension.VECTORS] }) - async getExtensionVersion(extension: DatabaseExtension): Promise { + getVectorExtension(): Promise { + return getVectorExtension(this.db); + } + + @GenerateSql({ params: [[DatabaseExtension.VECTORS]] }) + async getExtensionVersions(extensions: readonly DatabaseExtension[]): Promise { const { rows } = await sql` - SELECT default_version as "availableVersion", installed_version as "installedVersion" + SELECT name, default_version as "availableVersion", installed_version as "installedVersion" FROM pg_available_extensions - WHERE name = ${extension} + WHERE name in (${sql.join(extensions)}) `.execute(this.db); - return rows[0] ?? { availableVersion: null, installedVersion: null }; + return rows; } getExtensionVersionRange(extension: VectorExtension): string { - return extension === DatabaseExtension.VECTORS ? VECTORS_VERSION_RANGE : VECTOR_VERSION_RANGE; + switch (extension) { + case DatabaseExtension.VECTORCHORD: { + return VECTORCHORD_VERSION_RANGE; + } + case DatabaseExtension.VECTORS: { + return VECTORS_VERSION_RANGE; + } + case DatabaseExtension.VECTOR: { + return VECTOR_VERSION_RANGE; + } + default: { + throw new Error(`Unsupported vector extension: '${extension}'`); + } + } } @GenerateSql() @@ -59,11 +115,24 @@ export class DatabaseRepository { } async createExtension(extension: DatabaseExtension): Promise { - await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(extension)}`.execute(this.db); + this.logger.log(`Creating ${EXTENSION_NAMES[extension]} extension`); + await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(extension)} CASCADE`.execute(this.db); + if (extension === DatabaseExtension.VECTORCHORD) { + const dbName = sql.id(await this.getDatabaseName()); + await sql`ALTER DATABASE ${dbName} SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db); + await sql`SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db); + await sql`ALTER DATABASE ${dbName} SET vchordrq.probes = 1`.execute(this.db); + await sql`SET vchordrq.probes = 1`.execute(this.db); + } + } + + async dropExtension(extension: DatabaseExtension): Promise { + this.logger.log(`Dropping ${EXTENSION_NAMES[extension]} extension`); + await sql`DROP EXTENSION IF EXISTS ${sql.raw(extension)}`.execute(this.db); } async updateVectorExtension(extension: VectorExtension, targetVersion?: string): Promise { - const { availableVersion, installedVersion } = await this.getExtensionVersion(extension); + const [{ availableVersion, installedVersion }] = await this.getExtensionVersions([extension]); if (!installedVersion) { throw new Error(`${EXTENSION_NAMES[extension]} extension is not installed`); } @@ -75,184 +144,289 @@ export class DatabaseRepository { const isVectors = extension === DatabaseExtension.VECTORS; let restartRequired = false; + const diff = semver.diff(installedVersion, targetVersion); await this.db.transaction().execute(async (tx) => { await this.setSearchPath(tx); - if (isVectors && installedVersion === '0.1.1') { - await this.setExtVersion(tx, DatabaseExtension.VECTORS, '0.1.11'); - } - - const isSchemaUpgrade = semver.satisfies(installedVersion, '0.1.1 || 0.1.11'); - if (isSchemaUpgrade && isVectors) { - await this.updateVectorsSchema(tx); - } - await sql`ALTER EXTENSION ${sql.raw(extension)} UPDATE TO ${sql.lit(targetVersion)}`.execute(tx); - const diff = semver.diff(installedVersion, targetVersion); - if (isVectors && diff && ['minor', 'major'].includes(diff)) { + if (isVectors && (diff === 'major' || diff === 'minor')) { await sql`SELECT pgvectors_upgrade()`.execute(tx); restartRequired = true; - } else { - await this.reindex(VectorIndex.CLIP); - await this.reindex(VectorIndex.FACE); } }); + if (diff && !restartRequired) { + await Promise.all([this.reindexVectors(VectorIndex.CLIP), this.reindexVectors(VectorIndex.FACE)]); + } + return { restartRequired }; } - async reindex(index: VectorIndex): Promise { - try { - await sql`REINDEX INDEX ${sql.raw(index)}`.execute(this.db); - } catch (error) { - if (this.vectorExtension !== DatabaseExtension.VECTORS) { - throw error; - } - this.logger.warn(`Could not reindex index ${index}. Attempting to auto-fix.`); + async prewarm(index: VectorIndex): Promise { + const vectorExtension = await getVectorExtension(this.db); + if (vectorExtension !== DatabaseExtension.VECTORCHORD) { + return; + } + this.logger.debug(`Prewarming ${index}`); + await sql`SELECT vchordrq_prewarm(${index})`.execute(this.db); + } - const table = await this.getIndexTable(index); - const dimSize = await this.getDimSize(table); - await this.db.transaction().execute(async (tx) => { - await this.setSearchPath(tx); - await sql`DROP INDEX IF EXISTS ${sql.raw(index)}`.execute(tx); - await sql`ALTER TABLE ${sql.raw(table)} ALTER COLUMN embedding SET DATA TYPE real[]`.execute(tx); - await sql`ALTER TABLE ${sql.raw(table)} ALTER COLUMN embedding SET DATA TYPE vector(${sql.raw(String(dimSize))})`.execute( - tx, - ); - await sql`SET vectors.pgvector_compatibility=on`.execute(tx); - await sql` - CREATE INDEX IF NOT EXISTS ${sql.raw(index)} ON ${sql.raw(table)} - USING hnsw (embedding vector_cosine_ops) - WITH (ef_construction = 300, m = 16) - `.execute(tx); - }); + async reindexVectorsIfNeeded(names: VectorIndex[]): Promise { + const { rows } = await sql<{ + indexdef: string; + indexname: string; + }>`SELECT indexdef, indexname FROM pg_indexes WHERE indexname = ANY(ARRAY[${sql.join(names)}])`.execute(this.db); + + const vectorExtension = await getVectorExtension(this.db); + + const promises = []; + for (const indexName of names) { + const row = rows.find((index) => index.indexname === indexName); + const table = VECTOR_INDEX_TABLES[indexName]; + if (!row) { + promises.push(this.reindexVectors(indexName)); + continue; + } + + switch (vectorExtension) { + case DatabaseExtension.VECTOR: { + if (!row.indexdef.toLowerCase().includes('using hnsw')) { + promises.push(this.reindexVectors(indexName)); + } + break; + } + case DatabaseExtension.VECTORS: { + if (!row.indexdef.toLowerCase().includes('using vectors')) { + promises.push(this.reindexVectors(indexName)); + } + break; + } + case DatabaseExtension.VECTORCHORD: { + const matches = row.indexdef.match(/(?<=lists = \[)\d+/g); + const lists = matches && matches.length > 0 ? Number(matches[0]) : 1; + promises.push( + this.getRowCount(table).then((count) => { + const targetLists = this.targetListCount(count); + this.logger.log(`targetLists=${targetLists}, current=${lists} for ${indexName} of ${count} rows`); + if ( + !row.indexdef.toLowerCase().includes('using vchordrq') || + // slack factor is to avoid frequent reindexing if the count is borderline + (lists !== targetLists && lists !== this.targetListCount(count * VECTORCHORD_LIST_SLACK_FACTOR)) + ) { + probes[indexName] = this.targetProbeCount(targetLists); + return this.reindexVectors(indexName, { lists: targetLists }); + } else { + probes[indexName] = this.targetProbeCount(lists); + } + }), + ); + break; + } + } + } + + if (promises.length > 0) { + await Promise.all(promises); } } - @GenerateSql({ params: [VectorIndex.CLIP] }) - async shouldReindex(name: VectorIndex): Promise { - if (this.vectorExtension !== DatabaseExtension.VECTORS) { - return false; - } + private async reindexVectors(indexName: VectorIndex, { lists }: { lists?: number } = {}): Promise { + this.logger.log(`Reindexing ${indexName}`); + const table = VECTOR_INDEX_TABLES[indexName]; + const vectorExtension = await getVectorExtension(this.db); - try { - const { rows } = await sql<{ - idx_status: string; - }>`SELECT idx_status FROM pg_vector_index_stat WHERE indexname = ${name}`.execute(this.db); - return rows[0]?.idx_status === 'UPGRADE'; - } catch (error) { - const message: string = (error as any).message; - if (message.includes('index is not existing')) { - return true; - } else if (message.includes('relation "pg_vector_index_stat" does not exist')) { - return false; - } - throw error; + const { rows } = await sql<{ + columnName: string; + }>`SELECT column_name as "columnName" FROM information_schema.columns WHERE table_name = ${table}`.execute(this.db); + if (rows.length === 0) { + this.logger.warn( + `Table ${table} does not exist, skipping reindexing. This is only normal if this is a new Immich instance.`, + ); + return; } + const dimSize = await this.getDimensionSize(table); + await this.db.schema.dropIndex(indexName).ifExists().execute(); + if (table === 'smart_search') { + await this.db.schema.alterTable(table).dropConstraint('dim_size_constraint').ifExists().execute(); + } + await this.db.transaction().execute(async (tx) => { + if (!rows.some((row) => row.columnName === 'embedding')) { + this.logger.warn(`Column 'embedding' does not exist in table '${table}', truncating and adding column.`); + await sql`TRUNCATE TABLE ${sql.raw(table)}`.execute(tx); + await sql`ALTER TABLE ${sql.raw(table)} ADD COLUMN embedding real[] NOT NULL`.execute(tx); + } + await sql`ALTER TABLE ${sql.raw(table)} ALTER COLUMN embedding SET DATA TYPE real[]`.execute(tx); + const schema = vectorExtension === DatabaseExtension.VECTORS ? 'vectors.' : ''; + await sql` + ALTER TABLE ${sql.raw(table)} + ALTER COLUMN embedding + SET DATA TYPE ${sql.raw(schema)}vector(${sql.raw(String(dimSize))})`.execute(tx); + lists ||= this.targetListCount(await this.getRowCount(table)); + await sql.raw(vectorIndexQuery({ vectorExtension, table, indexName, lists })).execute(tx); + }); + try { + await sql`VACUUM ANALYZE ${sql.raw(table)}`.execute(this.db); + } catch (error: any) { + this.logger.warn(`Failed to vacuum table '${table}'. The DB will temporarily use more disk space: ${error}`); + } + this.logger.log(`Reindexed ${indexName}`); } private async setSearchPath(tx: Transaction): Promise { await sql`SET search_path TO "$user", public, vectors`.execute(tx); } - private async setExtVersion(tx: Transaction, extName: DatabaseExtension, version: string): Promise { - await sql`UPDATE pg_catalog.pg_extension SET extversion = ${version} WHERE extname = ${extName}`.execute(tx); + private async getDatabaseName(): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db`.execute(this.db); + return rows[0].db; } - private async getIndexTable(index: VectorIndex): Promise { - const { rows } = await sql<{ - relname: string | null; - }>`SELECT relname FROM pg_stat_all_indexes WHERE indexrelname = ${index}`.execute(this.db); - const table = rows[0]?.relname; - if (!table) { - throw new Error(`Could not find table for index ${index}`); - } - return table; - } - - private async updateVectorsSchema(tx: Transaction): Promise { - const extension = DatabaseExtension.VECTORS; - await sql`CREATE SCHEMA IF NOT EXISTS ${extension}`.execute(tx); - await sql`UPDATE pg_catalog.pg_extension SET extrelocatable = true WHERE extname = ${extension}`.execute(tx); - await sql`ALTER EXTENSION vectors SET SCHEMA vectors`.execute(tx); - await sql`UPDATE pg_catalog.pg_extension SET extrelocatable = false WHERE extname = ${extension}`.execute(tx); - } - - private async getDimSize(table: string, column = 'embedding'): Promise { + async getDimensionSize(table: string, column = 'embedding'): Promise { const { rows } = await sql<{ dimsize: number }>` SELECT atttypmod as dimsize FROM pg_attribute f JOIN pg_class c ON c.oid = f.attrelid WHERE c.relkind = 'r'::char AND f.attnum > 0 - AND c.relname = ${table} - AND f.attname = '${column}' + AND c.relname = ${table}::text + AND f.attname = ${column}::text `.execute(this.db); const dimSize = rows[0]?.dimsize; if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { - throw new Error(`Could not retrieve dimension size`); + this.logger.warn(`Could not retrieve dimension size of column '${column}' in table '${table}', assuming 512`); + return 512; } return dimSize; } - async runMigrations(options?: { transaction?: 'all' | 'none' | 'each'; only?: 'kysely' | 'typeorm' }): Promise { - const { database } = this.configRepository.getEnv(); - if (options?.only !== 'kysely') { - const dataSource = new DataSource(database.config.typeorm); + async setDimensionSize(dimSize: number): Promise { + if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { + throw new Error(`Invalid CLIP dimension size: ${dimSize}`); + } - this.logger.log('Running migrations, this may take a while'); + // this is done in two transactions to handle concurrent writes + await this.db.transaction().execute(async (trx) => { + await sql`delete from ${sql.table('smart_search')}`.execute(trx); + await trx.schema.alterTable('smart_search').dropConstraint('dim_size_constraint').ifExists().execute(); + await sql`alter table ${sql.table('smart_search')} add constraint dim_size_constraint check (array_length(embedding::real[], 1) = ${sql.lit(dimSize)})`.execute( + trx, + ); + }); + + const vectorExtension = await this.getVectorExtension(); + await this.db.transaction().execute(async (trx) => { + await sql`drop index if exists clip_index`.execute(trx); + await trx.schema + .alterTable('smart_search') + .alterColumn('embedding', (col) => col.setDataType(sql.raw(`vector(${dimSize})`))) + .execute(); + await sql + .raw(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: VectorIndex.CLIP })) + .execute(trx); + await trx.schema.alterTable('smart_search').dropConstraint('dim_size_constraint').ifExists().execute(); + }); + probes[VectorIndex.CLIP] = 1; + + await sql`vacuum analyze ${sql.table('smart_search')}`.execute(this.db); + } + + async deleteAllSearchEmbeddings(): Promise { + await sql`truncate ${sql.table('smart_search')}`.execute(this.db); + } + + private targetListCount(count: number) { + if (count < 128_000) { + return 1; + } else if (count < 2_048_000) { + return 1 << (32 - Math.clz32(count / 1000)); + } else { + return 1 << (33 - Math.clz32(Math.sqrt(count))); + } + } + + private targetProbeCount(lists: number) { + return Math.ceil(lists / 8); + } + + private async getRowCount(table: keyof DB): Promise { + const { count } = await this.db + .selectFrom(this.db.dynamic.table(table).as('t')) + .select((eb) => eb.fn.countAll().as('count')) + .executeTakeFirstOrThrow(); + return count; + } + + async runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise { + const { database } = this.configRepository.getEnv(); + + this.logger.log('Running migrations, this may take a while'); + + const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; + const { rows } = await tableExists.execute(this.db); + const hasTypeOrmMigrations = !!rows[0]?.result; + if (hasTypeOrmMigrations) { + // eslint-disable-next-line unicorn/prefer-module + const dist = resolve(`${__dirname}/..`); this.logger.debug('Running typeorm migrations'); - + const dataSource = new DataSource({ + type: 'postgres', + entities: [], + subscribers: [], + migrations: [`${dist}/migrations` + '/*.{js,ts}'], + migrationsRun: false, + synchronize: false, + connectTimeoutMS: 10_000, // 10 seconds + parseInt8: true, + ...(database.config.connectionType === 'url' + ? { url: database.config.url } + : { + host: database.config.host, + port: database.config.port, + username: database.config.username, + password: database.config.password, + database: database.config.database, + }), + }); await dataSource.initialize(); await dataSource.runMigrations(options); await dataSource.destroy(); - this.logger.debug('Finished running typeorm migrations'); } - if (options?.only !== 'typeorm') { - // eslint-disable-next-line unicorn/prefer-module - const migrationFolder = join(__dirname, '..', 'schema/migrations'); + this.logger.debug('Running kysely migrations'); + const migrator = new Migrator({ + db: this.db, + migrationLockTableName: 'kysely_migrations_lock', + migrationTableName: 'kysely_migrations', + provider: new FileMigrationProvider({ + fs: { readdir }, + path: { join }, + // eslint-disable-next-line unicorn/prefer-module + migrationFolder: join(__dirname, '..', 'schema/migrations'), + }), + }); - // TODO remove after we have at least one kysely migration - if (!existsSync(migrationFolder)) { - return; + const { error, results } = await migrator.migrateToLatest(); + + for (const result of results ?? []) { + if (result.status === 'Success') { + this.logger.log(`Migration "${result.migrationName}" succeeded`); } - this.logger.debug('Running kysely migrations'); - const migrator = new Migrator({ - db: this.db, - migrationLockTableName: 'kysely_migrations_lock', - migrationTableName: 'kysely_migrations', - provider: new FileMigrationProvider({ - fs: { readdir }, - path: { join }, - migrationFolder, - }), - }); - - const { error, results } = await migrator.migrateToLatest(); - - for (const result of results ?? []) { - if (result.status === 'Success') { - this.logger.log(`Migration "${result.migrationName}" succeeded`); - } - - if (result.status === 'Error') { - this.logger.warn(`Migration "${result.migrationName}" failed`); - } + if (result.status === 'Error') { + this.logger.warn(`Migration "${result.migrationName}" failed`); } - - if (error) { - this.logger.error(`Kysely migrations failed: ${error}`); - throw error; - } - - this.logger.debug('Finished running kysely migrations'); } + + if (error) { + this.logger.error(`Kysely migrations failed: ${error}`); + throw error; + } + + this.logger.debug('Finished running kysely migrations'); } async withLock(lock: DatabaseLock, callback: () => Promise): Promise { diff --git a/server/src/repositories/download.repository.ts b/server/src/repositories/download.repository.ts index c9c62c90ce..4c4bed07ff 100644 --- a/server/src/repositories/download.repository.ts +++ b/server/src/repositories/download.repository.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DB } from 'src/db'; +import { AssetVisibility } from 'src/enum'; import { anyUuid } from 'src/utils/database'; const builder = (db: Kysely) => @@ -31,6 +32,9 @@ export class DownloadRepository { } downloadUserId(userId: string) { - return builder(this.db).where('assets.ownerId', '=', userId).where('assets.isVisible', '=', true).stream(); + return builder(this.db) + .where('assets.ownerId', '=', userId) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) + .stream(); } } diff --git a/server/src/repositories/notification.repository.spec.ts b/server/src/repositories/email.repository.spec.ts similarity index 87% rename from server/src/repositories/notification.repository.spec.ts rename to server/src/repositories/email.repository.spec.ts index 1d0770af6b..5640b26bf6 100644 --- a/server/src/repositories/notification.repository.spec.ts +++ b/server/src/repositories/email.repository.spec.ts @@ -1,13 +1,13 @@ +import { EmailRenderRequest, EmailRepository, EmailTemplate } from 'src/repositories/email.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { EmailRenderRequest, EmailTemplate, NotificationRepository } from 'src/repositories/notification.repository'; import { automock } from 'test/utils'; -describe(NotificationRepository.name, () => { - let sut: NotificationRepository; +describe(EmailRepository.name, () => { + let sut: EmailRepository; beforeEach(() => { // eslint-disable-next-line no-sparse-arrays - sut = new NotificationRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false })); + sut = new EmailRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false })); }); describe('renderEmail', () => { diff --git a/server/src/repositories/email.repository.ts b/server/src/repositories/email.repository.ts new file mode 100644 index 0000000000..78c89b4a9d --- /dev/null +++ b/server/src/repositories/email.repository.ts @@ -0,0 +1,174 @@ +import { Injectable } from '@nestjs/common'; +import { render } from '@react-email/render'; +import { createTransport } from 'nodemailer'; +import React from 'react'; +import { AlbumInviteEmail } from 'src/emails/album-invite.email'; +import { AlbumUpdateEmail } from 'src/emails/album-update.email'; +import { TestEmail } from 'src/emails/test.email'; +import { WelcomeEmail } from 'src/emails/welcome.email'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { EmailImageAttachment } from 'src/types'; + +export type SendEmailOptions = { + from: string; + to: string; + replyTo?: string; + subject: string; + html: string; + text: string; + imageAttachments?: EmailImageAttachment[]; + smtp: SmtpOptions; +}; + +export type SmtpOptions = { + host: string; + port?: number; + username?: string; + password?: string; + ignoreCert?: boolean; +}; + +export enum EmailTemplate { + TEST_EMAIL = 'test', + + // AUTH + WELCOME = 'welcome', + RESET_PASSWORD = 'reset-password', + + // ALBUM + ALBUM_INVITE = 'album-invite', + ALBUM_UPDATE = 'album-update', +} + +interface BaseEmailProps { + baseUrl: string; + customTemplate?: string; +} + +export interface TestEmailProps extends BaseEmailProps { + displayName: string; +} + +export interface WelcomeEmailProps extends BaseEmailProps { + displayName: string; + username: string; + password?: string; +} + +export interface AlbumInviteEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + senderName: string; + recipientName: string; + cid?: string; +} + +export interface AlbumUpdateEmailProps extends BaseEmailProps { + albumName: string; + albumId: string; + recipientName: string; + cid?: string; +} + +export type EmailRenderRequest = + | { + template: EmailTemplate.TEST_EMAIL; + data: TestEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.WELCOME; + data: WelcomeEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_INVITE; + data: AlbumInviteEmailProps; + customTemplate: string; + } + | { + template: EmailTemplate.ALBUM_UPDATE; + data: AlbumUpdateEmailProps; + customTemplate: string; + }; + +export type SendEmailResponse = { + messageId: string; + response: any; +}; + +@Injectable() +export class EmailRepository { + constructor(private logger: LoggingRepository) { + this.logger.setContext(EmailRepository.name); + } + + verifySmtp(options: SmtpOptions): Promise { + const transport = this.createTransport(options); + try { + return transport.verify(); + } finally { + transport.close(); + } + } + + async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> { + const component = this.render(request); + const html = await render(component, { pretty: false }); + const text = await render(component, { plainText: true }); + return { html, text }; + } + + sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise { + this.logger.debug(`Sending email to ${to} with subject: ${subject}`); + const transport = this.createTransport(smtp); + + const attachments = imageAttachments?.map((attachment) => ({ + filename: attachment.filename, + path: attachment.path, + cid: attachment.cid, + })); + + try { + return transport.sendMail({ to, from, subject, html, text, attachments }); + } finally { + transport.close(); + } + } + + private render({ template, data, customTemplate }: EmailRenderRequest): React.FunctionComponentElement { + switch (template) { + case EmailTemplate.TEST_EMAIL: { + return React.createElement(TestEmail, { ...data, customTemplate }); + } + + case EmailTemplate.WELCOME: { + return React.createElement(WelcomeEmail, { ...data, customTemplate }); + } + + case EmailTemplate.ALBUM_INVITE: { + return React.createElement(AlbumInviteEmail, { ...data, customTemplate }); + } + + case EmailTemplate.ALBUM_UPDATE: { + return React.createElement(AlbumUpdateEmail, { ...data, customTemplate }); + } + } + } + + private createTransport(options: SmtpOptions) { + return createTransport({ + host: options.host, + port: options.port, + tls: { rejectUnauthorized: !options.ignoreCert }, + auth: + options.username || options.password + ? { + user: options.username, + pass: options.password, + } + : undefined, + connectionTimeout: 5000, + }); + } +} diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index 3156804d09..307b8b0ef4 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -14,11 +14,12 @@ import { SystemConfig } from 'src/config'; import { EventConfig } from 'src/decorators'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { NotificationDto } from 'src/dtos/notification.dto'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { ImmichWorker, MetadataKey, QueueName } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { JobItem } from 'src/types'; +import { JobItem, JobSource } from 'src/types'; import { handlePromiseError } from 'src/utils/misc'; type EmitHandlers = Partial<{ [T in EmitEvent]: Array> }>; @@ -47,7 +48,7 @@ type EventMap = { 'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }]; // album events - 'album.update': [{ id: string; recipientIds: string[] }]; + 'album.update': [{ id: string; recipientId: string }]; 'album.invite': [{ id: string; userId: string }]; // asset events @@ -57,6 +58,7 @@ type EventMap = { 'asset.show': [{ assetId: string; userId: string }]; 'asset.trash': [{ assetId: string; userId: string }]; 'asset.delete': [{ assetId: string; userId: string }]; + 'asset.metadataExtracted': [{ assetId: string; userId: string; source?: JobSource }]; // asset bulk events 'assets.trash': [{ assetIds: string[]; userId: string }]; @@ -64,6 +66,7 @@ type EventMap = { 'assets.restore': [{ assetIds: string[]; userId: string }]; 'job.start': [QueueName, JobItem]; + 'job.failed': [{ job: JobItem; error: Error | any }]; // session events 'session.delete': [{ sessionId: string }]; @@ -104,6 +107,7 @@ export interface ClientEventMap { on_server_version: [ServerVersionResponseDto]; on_config_update: []; on_new_release: [ReleaseNotification]; + on_notification: [NotificationDto]; on_session_delete: [string]; } diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index ef36a2b3f8..453e515fe0 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -11,6 +11,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -55,6 +56,7 @@ export const repositories = [ CryptoRepository, DatabaseRepository, DownloadRepository, + EmailRepository, EventRepository, JobRepository, LibraryRepository, diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index fd9f4c5363..32a4f75d67 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -9,7 +9,7 @@ import { JobName, JobStatus, MetadataKey, QueueCleanType, QueueName } from 'src/ import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; -import { IEntityJob, JobCounts, JobItem, JobOf, QueueStatus } from 'src/types'; +import { JobCounts, JobItem, JobOf, QueueStatus } from 'src/types'; import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc'; type JobMapItem = { @@ -33,7 +33,7 @@ export class JobRepository { this.logger.setContext(JobRepository.name); } - setup({ services }: { services: ClassConstructor[] }) { + setup(services: ClassConstructor[]) { const reflector = this.moduleRef.get(Reflector, { strict: false }); // discovery @@ -206,7 +206,10 @@ export class JobRepository { private getJobOptions(item: JobItem): JobsOptions | null { switch (item.name) { case JobName.NOTIFY_ALBUM_UPDATE: { - return { jobId: item.data.id, delay: item.data?.delay }; + return { + jobId: `${item.data.id}/${item.data.recipientId}`, + delay: item.data?.delay, + }; } case JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE: { return { jobId: item.data.id }; @@ -227,19 +230,12 @@ export class JobRepository { return this.moduleRef.get(getQueueToken(queue), { strict: false }); } - public async removeJob(jobId: string, name: JobName): Promise { - const existingJob = await this.getQueue(this.getQueueName(name)).getJob(jobId); - if (!existingJob) { - return; - } - try { + /** @deprecated */ + // todo: remove this when asset notifications no longer need it. + public async removeJob(name: JobName, jobID: string): Promise { + const existingJob = await this.getQueue(this.getQueueName(name)).getJob(jobID); + if (existingJob) { await existingJob.remove(); - } catch (error: any) { - if (error.message?.includes('Missing key for job')) { - return; - } - throw error; } - return existingJob.data; } } diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index fd9dd81b7b..b6c5ebbe08 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -4,7 +4,7 @@ import { InjectKysely } from 'nestjs-kysely'; import { DB, Libraries } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; -import { AssetType } from 'src/enum'; +import { AssetType, AssetVisibility } from 'src/enum'; export enum AssetSyncResult { DO_NOTHING, @@ -77,13 +77,17 @@ export class LibraryRepository { .select((eb) => eb.fn .countAll() - .filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.isVisible', '=', true)])) + .filterWhere((eb) => + eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]), + ) .as('photos'), ) .select((eb) => eb.fn .countAll() - .filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.isVisible', '=', true)])) + .filterWhere((eb) => + eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]), + ) .as('videos'), ) .select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('exif.fileSizeInByte'), eb.val(0)).as('usage')) diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 3f809db41e..2ac3715a50 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -5,7 +5,7 @@ import { Telemetry } from 'src/decorators'; import { LogLevel } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; -type LogDetails = any[]; +type LogDetails = any; type LogFunction = () => string; const LOG_LEVELS = [LogLevel.VERBOSE, LogLevel.DEBUG, LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; @@ -74,11 +74,21 @@ export class MyConsoleLogger extends ConsoleLogger { export class LoggingRepository { private logger: MyConsoleLogger; - constructor(@Inject(ClsService) cls: ClsService | undefined, configRepository: ConfigRepository) { - const { noColor } = configRepository.getEnv(); + constructor( + @Inject(ClsService) cls: ClsService | undefined, + @Inject(ConfigRepository) configRepository: ConfigRepository | undefined, + ) { + let noColor = false; + if (configRepository) { + noColor = configRepository.getEnv().noColor; + } this.logger = new MyConsoleLogger(cls, { context: LoggingRepository.name, color: !noColor }); } + static create() { + return new LoggingRepository(undefined, undefined); + } + setAppName(name: string): void { appName = name.charAt(0).toUpperCase() + name.slice(1); } diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index f9998ad179..3f559442aa 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -8,7 +8,7 @@ import readLine from 'node:readline'; import { citiesFile } from 'src/constants'; import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { SystemMetadataKey } from 'src/enum'; +import { AssetVisibility, SystemMetadataKey } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; @@ -75,9 +75,11 @@ export class MapRepository { } @GenerateSql({ params: [[DummyValue.UUID], [DummyValue.UUID]] }) - getMapMarkers(ownerIds: string[], albumIds: string[], options: MapMarkerSearchOptions = {}) { - const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options; - + getMapMarkers( + ownerIds: string[], + albumIds: string[], + { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore }: MapMarkerSearchOptions = {}, + ) { return this.db .selectFrom('assets') .innerJoin('exif', (builder) => @@ -88,8 +90,17 @@ export class MapRepository { ) .select(['id', 'exif.latitude as lat', 'exif.longitude as lon', 'exif.city', 'exif.state', 'exif.country']) .$narrowType<{ lat: NotNull; lon: NotNull }>() - .where('isVisible', '=', true) - .$if(isArchived !== undefined, (q) => q.where('isArchived', '=', isArchived!)) + .$if(isArchived === true, (qb) => + qb.where((eb) => + eb.or([ + eb('assets.visibility', '=', AssetVisibility.TIMELINE), + eb('assets.visibility', '=', AssetVisibility.ARCHIVE), + ]), + ), + ) + .$if(isArchived === false || isArchived === undefined, (qb) => + qb.where('assets.visibility', '=', AssetVisibility.TIMELINE), + ) .$if(isFavorite !== undefined, (q) => q.where('isFavorite', '=', isFavorite!)) .$if(fileCreatedAfter !== undefined, (q) => q.where('fileCreatedAt', '>=', fileCreatedAfter!)) .$if(fileCreatedBefore !== undefined, (q) => q.where('fileCreatedAt', '<=', fileCreatedBefore!)) diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 1e41dd6bb2..33cf4e3e03 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -7,7 +7,7 @@ import { Writable } from 'node:stream'; import sharp from 'sharp'; import { ORIENTATION_TO_SHARP_ROTATION } from 'src/constants'; import { Exif } from 'src/database'; -import { Colorspace, LogLevel } from 'src/enum'; +import { Colorspace, LogLevel, RawExtractedFormat } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { DecodeToBufferOptions, @@ -36,34 +36,51 @@ type ProgressEvent = { percent?: number; }; +export type ExtractResult = { + buffer: Buffer; + format: RawExtractedFormat; +}; + @Injectable() export class MediaRepository { constructor(private logger: LoggingRepository) { this.logger.setContext(MediaRepository.name); } - async extract(input: string, output: string): Promise { + /** + * + * @param input file path to the input image + * @returns ExtractResult if succeeded, or null if failed + */ + async extract(input: string): Promise { try { - // remove existing output file if it exists - // as exiftool-vendored does not support overwriting via "-w!" flag - // and throws "1 files could not be read" error when the output file exists - await fs.unlink(output).catch(() => null); - await exiftool.extractBinaryTag('JpgFromRaw2', input, output); - } catch { - try { - this.logger.debug('Extracting JPEG from RAW image:', input); - await exiftool.extractJpgFromRaw(input, output); - } catch (error: any) { - this.logger.debug('Could not extract JPEG from image, trying preview', error.message); - try { - await exiftool.extractPreview(input, output); - } catch (error: any) { - this.logger.debug('Could not extract preview from image', error.message); - return false; - } - } + const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw2', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('JpgFromRaw', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract JPEG buffer from image, trying PreviewJXL next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('PreviewJXL', input); + return { buffer, format: RawExtractedFormat.JXL }; + } catch (error: any) { + this.logger.debug('Could not extract PreviewJXL buffer from image, trying PreviewImage next', error.message); + } + + try { + const buffer = await exiftool.extractBinaryTagToBuffer('PreviewImage', input); + return { buffer, format: RawExtractedFormat.JPEG }; + } catch (error: any) { + this.logger.debug('Could not extract preview buffer from image', error.message); + return null; } - return true; } async writeExif(tags: Partial, output: string): Promise { @@ -104,7 +121,7 @@ export class MediaRepository { } } - decodeImage(input: string, options: DecodeToBufferOptions) { + decodeImage(input: string | Buffer, options: DecodeToBufferOptions) { return this.getImageDecodingPipeline(input, options).raw().toBuffer({ resolveWithObject: true }); } @@ -192,7 +209,7 @@ export class MediaRepository { index: stream.index, codecType: stream.codec_type, codecName: stream.codec_name, - frameCount: this.parseInt(options?.countFrames ? stream.nb_read_packets : stream.nb_frames), + bitrate: this.parseInt(stream.bit_rate), })), }; } @@ -235,7 +252,7 @@ export class MediaRepository { }); } - async getImageDimensions(input: string): Promise { + async getImageDimensions(input: string | Buffer): Promise { const { width = 0, height = 0 } = await sharp(input).metadata(); return { width, height }; } diff --git a/server/src/repositories/memory.repository.ts b/server/src/repositories/memory.repository.ts index 44c7c30857..96eb78e6d6 100644 --- a/server/src/repositories/memory.repository.ts +++ b/server/src/repositories/memory.repository.ts @@ -1,19 +1,26 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, Updateable } from 'kysely'; +import { Insertable, Kysely, sql, Updateable } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; import { DB, Memories } from 'src/db'; import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { MemorySearchDto } from 'src/dtos/memory.dto'; +import { AssetVisibility } from 'src/enum'; import { IBulkAsset } from 'src/types'; @Injectable() export class MemoryRepository implements IBulkAsset { constructor(@InjectKysely() private db: Kysely) {} - @GenerateSql({ params: [DummyValue.UUID] }) - cleanup() { + async cleanup() { + await this.db + .deleteFrom('memories_assets_assets') + .using('assets') + .whereRef('memories_assets_assets.assetsId', '=', 'assets.id') + .where('assets.visibility', '!=', AssetVisibility.TIMELINE) + .execute(); + return this.db .deleteFrom('memories') .where('createdAt', '<', DateTime.now().minus({ days: 30 }).toJSDate()) @@ -37,6 +44,7 @@ export class MemoryRepository implements IBulkAsset { .innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId') .whereRef('memories_assets_assets.memoriesId', '=', 'memories.id') .orderBy('assets.fileCreatedAt', 'asc') + .where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE)) .where('assets.deletedAt', 'is', null), ).as('assets'), ) @@ -139,6 +147,7 @@ export class MemoryRepository implements IBulkAsset { .innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId') .whereRef('memories_assets_assets.memoriesId', '=', 'memories.id') .orderBy('assets.fileCreatedAt', 'asc') + .where('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE)) .where('assets.deletedAt', 'is', null), ).as('assets'), ) diff --git a/server/src/repositories/move.repository.ts b/server/src/repositories/move.repository.ts index 21c52aec65..a21167fffd 100644 --- a/server/src/repositories/move.repository.ts +++ b/server/src/repositories/move.repository.ts @@ -37,7 +37,6 @@ export class MoveRepository { return this.db.deleteFrom('move_history').where('id', '=', id).returningAll().executeTakeFirstOrThrow(); } - @GenerateSql() async cleanMoveHistory(): Promise { await this.db .deleteFrom('move_history') @@ -52,7 +51,7 @@ export class MoveRepository { .execute(); } - @GenerateSql() + @GenerateSql({ params: [DummyValue.UUID] }) async cleanMoveHistorySingle(assetId: string): Promise { await this.db .deleteFrom('move_history') diff --git a/server/src/repositories/notification.repository.ts b/server/src/repositories/notification.repository.ts index 91f03b928b..b35f532094 100644 --- a/server/src/repositories/notification.repository.ts +++ b/server/src/repositories/notification.repository.ts @@ -1,174 +1,102 @@ -import { Injectable } from '@nestjs/common'; -import { render } from '@react-email/render'; -import { createTransport } from 'nodemailer'; -import React from 'react'; -import { AlbumInviteEmail } from 'src/emails/album-invite.email'; -import { AlbumUpdateEmail } from 'src/emails/album-update.email'; -import { TestEmail } from 'src/emails/test.email'; -import { WelcomeEmail } from 'src/emails/welcome.email'; -import { LoggingRepository } from 'src/repositories/logging.repository'; -import { EmailImageAttachment } from 'src/types'; +import { Insertable, Kysely, Updateable } from 'kysely'; +import { DateTime } from 'luxon'; +import { InjectKysely } from 'nestjs-kysely'; +import { columns } from 'src/database'; +import { DB, Notifications } from 'src/db'; +import { DummyValue, GenerateSql } from 'src/decorators'; +import { NotificationSearchDto } from 'src/dtos/notification.dto'; -export type SendEmailOptions = { - from: string; - to: string; - replyTo?: string; - subject: string; - html: string; - text: string; - imageAttachments?: EmailImageAttachment[]; - smtp: SmtpOptions; -}; - -export type SmtpOptions = { - host: string; - port?: number; - username?: string; - password?: string; - ignoreCert?: boolean; -}; - -export enum EmailTemplate { - TEST_EMAIL = 'test', - - // AUTH - WELCOME = 'welcome', - RESET_PASSWORD = 'reset-password', - - // ALBUM - ALBUM_INVITE = 'album-invite', - ALBUM_UPDATE = 'album-update', -} - -interface BaseEmailProps { - baseUrl: string; - customTemplate?: string; -} - -export interface TestEmailProps extends BaseEmailProps { - displayName: string; -} - -export interface WelcomeEmailProps extends BaseEmailProps { - displayName: string; - username: string; - password?: string; -} - -export interface AlbumInviteEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - senderName: string; - recipientName: string; - cid?: string; -} - -export interface AlbumUpdateEmailProps extends BaseEmailProps { - albumName: string; - albumId: string; - recipientName: string; - cid?: string; -} - -export type EmailRenderRequest = - | { - template: EmailTemplate.TEST_EMAIL; - data: TestEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.WELCOME; - data: WelcomeEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_INVITE; - data: AlbumInviteEmailProps; - customTemplate: string; - } - | { - template: EmailTemplate.ALBUM_UPDATE; - data: AlbumUpdateEmailProps; - customTemplate: string; - }; - -export type SendEmailResponse = { - messageId: string; - response: any; -}; - -@Injectable() export class NotificationRepository { - constructor(private logger: LoggingRepository) { - this.logger.setContext(NotificationRepository.name); + constructor(@InjectKysely() private db: Kysely) {} + + cleanup() { + return this.db + .deleteFrom('notifications') + .where((eb) => + eb.or([ + // remove soft-deleted notifications + eb.and([eb('deletedAt', 'is not', null), eb('deletedAt', '<', DateTime.now().minus({ days: 3 }).toJSDate())]), + + // remove old, read notifications + eb.and([ + // keep recently read messages around for a few days + eb('readAt', '>', DateTime.now().minus({ days: 2 }).toJSDate()), + eb('createdAt', '<', DateTime.now().minus({ days: 15 }).toJSDate()), + ]), + + eb.and([ + // remove super old, unread notifications + eb('readAt', '=', null), + eb('createdAt', '<', DateTime.now().minus({ days: 30 }).toJSDate()), + ]), + ]), + ) + .execute(); } - verifySmtp(options: SmtpOptions): Promise { - const transport = this.createTransport(options); - try { - return transport.verify(); - } finally { - transport.close(); - } + @GenerateSql({ params: [DummyValue.UUID, {}] }, { name: 'unread', params: [DummyValue.UUID, { unread: true }] }) + search(userId: string, dto: NotificationSearchDto) { + return this.db + .selectFrom('notifications') + .select(columns.notification) + .where((qb) => + qb.and({ + userId, + id: dto.id, + level: dto.level, + type: dto.type, + readAt: dto.unread ? null : undefined, + }), + ) + .where('deletedAt', 'is', null) + .orderBy('createdAt', 'desc') + .execute(); } - async renderEmail(request: EmailRenderRequest): Promise<{ html: string; text: string }> { - const component = this.render(request); - const html = await render(component, { pretty: false }); - const text = await render(component, { plainText: true }); - return { html, text }; + create(notification: Insertable) { + return this.db + .insertInto('notifications') + .values(notification) + .returning(columns.notification) + .executeTakeFirstOrThrow(); } - sendEmail({ to, from, subject, html, text, smtp, imageAttachments }: SendEmailOptions): Promise { - this.logger.debug(`Sending email to ${to} with subject: ${subject}`); - const transport = this.createTransport(smtp); - - const attachments = imageAttachments?.map((attachment) => ({ - filename: attachment.filename, - path: attachment.path, - cid: attachment.cid, - })); - - try { - return transport.sendMail({ to, from, subject, html, text, attachments }); - } finally { - transport.close(); - } + get(id: string) { + return this.db + .selectFrom('notifications') + .select(columns.notification) + .where('id', '=', id) + .where('deletedAt', 'is not', null) + .executeTakeFirst(); } - private render({ template, data, customTemplate }: EmailRenderRequest): React.FunctionComponentElement { - switch (template) { - case EmailTemplate.TEST_EMAIL: { - return React.createElement(TestEmail, { ...data, customTemplate }); - } - - case EmailTemplate.WELCOME: { - return React.createElement(WelcomeEmail, { ...data, customTemplate }); - } - - case EmailTemplate.ALBUM_INVITE: { - return React.createElement(AlbumInviteEmail, { ...data, customTemplate }); - } - - case EmailTemplate.ALBUM_UPDATE: { - return React.createElement(AlbumUpdateEmail, { ...data, customTemplate }); - } - } + update(id: string, notification: Updateable) { + return this.db + .updateTable('notifications') + .set(notification) + .where('deletedAt', 'is', null) + .where('id', '=', id) + .returning(columns.notification) + .executeTakeFirstOrThrow(); } - private createTransport(options: SmtpOptions) { - return createTransport({ - host: options.host, - port: options.port, - tls: { rejectUnauthorized: !options.ignoreCert }, - auth: - options.username || options.password - ? { - user: options.username, - pass: options.password, - } - : undefined, - connectionTimeout: 5000, - }); + async updateAll(ids: string[], notification: Updateable) { + await this.db.updateTable('notifications').set(notification).where('id', 'in', ids).execute(); + } + + async delete(id: string) { + await this.db + .updateTable('notifications') + .set({ deletedAt: DateTime.now().toJSDate() }) + .where('id', '=', id) + .execute(); + } + + async deleteAll(ids: string[]) { + await this.db + .updateTable('notifications') + .set({ deletedAt: DateTime.now().toJSDate() }) + .where('id', 'in', ids) + .execute(); } } diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index dc19a1fe01..ea9f0b1901 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -1,18 +1,21 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { custom, generators, Issuer, UserinfoResponse } from 'openid-client'; +import type { UserInfoResponse } from 'openid-client' with { 'resolution-mode': 'import' }; +import { OAuthTokenEndpointAuthMethod } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export type OAuthConfig = { clientId: string; - clientSecret: string; + clientSecret?: string; issuerUrl: string; mobileOverrideEnabled: boolean; mobileRedirectUri: string; profileSigningAlgorithm: string; scope: string; signingAlgorithm: string; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; + timeout: number; }; -export type OAuthProfile = UserinfoResponse; +export type OAuthProfile = UserInfoResponse; @Injectable() export class OAuthRepository { @@ -20,30 +23,47 @@ export class OAuthRepository { this.logger.setContext(OAuthRepository.name); } - init() { - custom.setHttpOptionsDefaults({ timeout: 30_000 }); - } - - async authorize(config: OAuthConfig, redirectUrl: string) { + async authorize(config: OAuthConfig, redirectUrl: string, state?: string, codeChallenge?: string) { + const { buildAuthorizationUrl, randomState, randomPKCECodeVerifier, calculatePKCECodeChallenge } = await import( + 'openid-client' + ); const client = await this.getClient(config); - return client.authorizationUrl({ + state ??= randomState(); + let codeVerifier: string | null; + if (codeChallenge) { + codeVerifier = null; + } else { + codeVerifier = randomPKCECodeVerifier(); + codeChallenge = await calculatePKCECodeChallenge(codeVerifier); + } + const url = buildAuthorizationUrl(client, { redirect_uri: redirectUrl, scope: config.scope, - state: generators.state(), - }); + state, + code_challenge: codeChallenge, + code_challenge_method: 'S256', + }).toString(); + return { url, state, codeVerifier }; } async getLogoutEndpoint(config: OAuthConfig) { const client = await this.getClient(config); - return client.issuer.metadata.end_session_endpoint; + return client.serverMetadata().end_session_endpoint; } - async getProfile(config: OAuthConfig, url: string, redirectUrl: string): Promise { + async getProfile( + config: OAuthConfig, + url: string, + expectedState: string, + codeVerifier: string, + ): Promise { + const { authorizationCodeGrant, fetchUserInfo, ...oidc } = await import('openid-client'); const client = await this.getClient(config); - const params = client.callbackParams(url); + const pkceCodeVerifier = client.serverMetadata().supportsPKCE() ? codeVerifier : undefined; + try { - const tokens = await client.callback(redirectUrl, params, { state: params.state }); - const profile = await client.userinfo(tokens.access_token || ''); + const tokens = await authorizationCodeGrant(client, new URL(url), { expectedState, pkceCodeVerifier }); + const profile = await fetchUserInfo(client, tokens.access_token, oidc.skipSubjectCheck); if (!profile.sub) { throw new Error('Unexpected profile response, no `sub`'); } @@ -59,7 +79,10 @@ export class OAuthRepository { ); } - throw error; + this.logger.error(`OAuth login failed: ${error.message}`); + this.logger.error(error); + + throw new Error('OAuth login failed', { cause: error }); } } @@ -81,19 +104,51 @@ export class OAuthRepository { clientSecret, profileSigningAlgorithm, signingAlgorithm, + tokenEndpointAuthMethod, + timeout, }: OAuthConfig) { try { - const issuer = await Issuer.discover(issuerUrl); - return new issuer.Client({ - client_id: clientId, - client_secret: clientSecret, - response_types: ['code'], - userinfo_signed_response_alg: profileSigningAlgorithm === 'none' ? undefined : profileSigningAlgorithm, - id_token_signed_response_alg: signingAlgorithm, - }); + const { allowInsecureRequests, discovery } = await import('openid-client'); + return await discovery( + new URL(issuerUrl), + clientId, + { + client_secret: clientSecret, + response_types: ['code'], + userinfo_signed_response_alg: profileSigningAlgorithm === 'none' ? undefined : profileSigningAlgorithm, + id_token_signed_response_alg: signingAlgorithm, + }, + await this.getTokenAuthMethod(tokenEndpointAuthMethod, clientSecret), + { + execute: [allowInsecureRequests], + timeout, + }, + ); } catch (error: any | AggregateError) { this.logger.error(`Error in OAuth discovery: ${error}`, error?.stack, error?.errors); throw new InternalServerErrorException(`Error in OAuth discovery: ${error}`, { cause: error }); } } + + private async getTokenAuthMethod(tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod, clientSecret?: string) { + const { None, ClientSecretPost, ClientSecretBasic } = await import('openid-client'); + + if (!clientSecret) { + return None(); + } + + switch (tokenEndpointAuthMethod) { + case OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST: { + return ClientSecretPost(clientSecret); + } + + case OAuthTokenEndpointAuthMethod.CLIENT_SECRET_BASIC: { + return ClientSecretBasic(clientSecret); + } + + default: { + return None(); + } + } + } } diff --git a/server/src/repositories/partner.repository.ts b/server/src/repositories/partner.repository.ts index ea762d0aaf..31350541ca 100644 --- a/server/src/repositories/partner.repository.ts +++ b/server/src/repositories/partner.repository.ts @@ -47,7 +47,6 @@ export class PartnerRepository { .executeTakeFirst(); } - @GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }] }) create(values: Insertable) { return this.db .insertInto('partners') diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index d55d863ea7..70a9980201 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; -import { ExpressionBuilder, Insertable, Kysely, NotNull, Selectable, sql, Updateable } from 'kysely'; +import { ExpressionBuilder, Insertable, Kysely, Selectable, sql, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { AssetFaces, DB, FaceSearch, Person } from 'src/db'; import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AssetFileType, SourceType } from 'src/enum'; +import { AssetFileType, AssetVisibility, SourceType } from 'src/enum'; import { removeUndefinedKeys } from 'src/utils/database'; -import { PaginationOptions } from 'src/utils/pagination'; +import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; export interface PersonSearchOptions { minimumFaceCount: number; @@ -98,18 +98,15 @@ export class PersonRepository { return Number(result.numChangedRows ?? 0); } - @GenerateSql({ params: [{ sourceType: SourceType.EXIF }] }) async unassignFaces({ sourceType }: UnassignFacesOptions): Promise { await this.db .updateTable('asset_faces') .set({ personId: null }) .where('asset_faces.sourceType', '=', sourceType) .execute(); - - await this.vacuum({ reindexVectors: false }); } - @GenerateSql({ params: [DummyValue.UUID] }) + @GenerateSql({ params: [[DummyValue.UUID]] }) async delete(ids: string[]): Promise { if (ids.length === 0) { return; @@ -118,11 +115,8 @@ export class PersonRepository { await this.db.deleteFrom('person').where('person.id', 'in', ids).execute(); } - @GenerateSql({ params: [{ sourceType: SourceType.EXIF }] }) async deleteFaces({ sourceType }: DeleteFacesOptions): Promise { await this.db.deleteFrom('asset_faces').where('asset_faces.sourceType', '=', sourceType).execute(); - - await this.vacuum({ reindexVectors: sourceType === SourceType.MACHINE_LEARNING }); } getAllFaces(options: GetAllFacesOptions = {}) { @@ -157,7 +151,7 @@ export class PersonRepository { .innerJoin('assets', (join) => join .onRef('asset_faces.assetId', '=', 'assets.id') - .on('assets.isArchived', '=', false) + .on('assets.visibility', '!=', AssetVisibility.ARCHIVE) .on('assets.deletedAt', 'is', null), ) .where('person.ownerId', '=', userId) @@ -200,11 +194,7 @@ export class PersonRepository { .limit(pagination.take + 1) .execute(); - if (items.length > pagination.take) { - return { items: items.slice(0, -1), hasNextPage: true }; - } - - return { items, hasNextPage: false }; + return paginationHelper(items, pagination.take); } @GenerateSql() @@ -252,7 +242,7 @@ export class PersonRepository { jsonObjectFrom( eb .selectFrom('assets') - .select(['assets.ownerId', 'assets.isArchived', 'assets.fileCreatedAt']) + .select(['assets.ownerId', 'assets.visibility', 'assets.fileCreatedAt']) .whereRef('assets.id', '=', 'asset_faces.assetId'), ).as('asset'), ) @@ -268,8 +258,7 @@ export class PersonRepository { .selectFrom('person') .innerJoin('asset_faces', 'asset_faces.id', 'person.faceAssetId') .innerJoin('assets', 'asset_faces.assetId', 'assets.id') - .innerJoin('exif', 'exif.assetId', 'assets.id') - .innerJoin('asset_files', 'asset_files.assetId', 'assets.id') + .leftJoin('exif', 'exif.assetId', 'assets.id') .select([ 'person.ownerId', 'asset_faces.boundingBoxX1 as x1', @@ -278,18 +267,20 @@ export class PersonRepository { 'asset_faces.boundingBoxY2 as y2', 'asset_faces.imageWidth as oldWidth', 'asset_faces.imageHeight as oldHeight', - 'exif.exifImageWidth', - 'exif.exifImageHeight', 'assets.type', 'assets.originalPath', - 'asset_files.path as previewPath', + 'exif.orientation as exifOrientation', ]) + .select((eb) => + eb + .selectFrom('asset_files') + .select('asset_files.path') + .whereRef('asset_files.assetId', '=', 'assets.id') + .where('asset_files.type', '=', sql.lit(AssetFileType.PREVIEW)) + .as('previewPath'), + ) .where('person.id', '=', id) .where('asset_faces.deletedAt', 'is', null) - .where('asset_files.type', '=', AssetFileType.PREVIEW) - .where('exif.exifImageWidth', '>', 0) - .where('exif.exifImageHeight', '>', 0) - .$narrowType<{ exifImageWidth: NotNull; exifImageHeight: NotNull }>() .executeTakeFirst(); } @@ -350,7 +341,7 @@ export class PersonRepository { join .onRef('assets.id', '=', 'asset_faces.assetId') .on('asset_faces.personId', '=', personId) - .on('assets.isArchived', '=', false) + .on('assets.visibility', '!=', AssetVisibility.ARCHIVE) .on('assets.deletedAt', 'is', null), ) .select((eb) => eb.fn.count(eb.fn('distinct', ['assets.id'])).as('count')) @@ -373,7 +364,7 @@ export class PersonRepository { join .onRef('assets.id', '=', 'asset_faces.assetId') .on('assets.deletedAt', 'is', null) - .on('assets.isArchived', '=', false), + .on('assets.visibility', '!=', AssetVisibility.ARCHIVE), ) .select((eb) => eb.fn.count(eb.fn('distinct', ['person.id'])).as('total')) .select((eb) => @@ -526,7 +517,7 @@ export class PersonRepository { await this.db.updateTable('asset_faces').set({ deletedAt: new Date() }).where('asset_faces.id', '=', id).execute(); } - private async vacuum({ reindexVectors }: { reindexVectors: boolean }): Promise { + async vacuum({ reindexVectors }: { reindexVectors: boolean }): Promise { await sql`VACUUM ANALYZE asset_faces, face_search, person`.execute(this.db); await sql`REINDEX TABLE asset_faces`.execute(this.db); await sql`REINDEX TABLE person`.execute(this.db); diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index c86ae8f60e..a7b7027b7b 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -1,48 +1,17 @@ import { Injectable } from '@nestjs/common'; -import { Kysely, OrderByDirection, sql } from 'kysely'; +import { Kysely, OrderByDirection, Selectable, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { randomUUID } from 'node:crypto'; -import { DB } from 'src/db'; +import { DB, Exif } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity'; -import { AssetStatus, AssetType } from 'src/enum'; -import { anyUuid, asUuid } from 'src/utils/database'; -import { Paginated } from 'src/utils/pagination'; +import { MapAsset } from 'src/dtos/asset-response.dto'; +import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; +import { probes } from 'src/repositories/database.repository'; +import { anyUuid, asUuid, searchAssetBuilder } from 'src/utils/database'; +import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; -export interface SearchResult { - /** total matches */ - total: number; - /** collection size */ - count: number; - /** current page */ - page: number; - /** items for page */ - items: T[]; - /** score */ - distances: number[]; - facets: SearchFacet[]; -} - -export interface SearchFacet { - fieldName: string; - counts: Array<{ - count: number; - value: string; - }>; -} - -export type SearchExploreItemSet = Array<{ - value: string; - data: T; -}>; - -export interface SearchExploreItem { - fieldName: string; - items: SearchExploreItemSet; -} - -export interface SearchAssetIDOptions { +export interface SearchAssetIdOptions { checksum?: Buffer; deviceAssetId?: string; id?: string; @@ -54,20 +23,19 @@ export interface SearchUserIdOptions { userIds?: string[]; } -export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions; +export type SearchIdOptions = SearchAssetIdOptions & SearchUserIdOptions; export interface SearchStatusOptions { - isArchived?: boolean; isEncoded?: boolean; isFavorite?: boolean; isMotion?: boolean; isOffline?: boolean; - isVisible?: boolean; isNotInAlbum?: boolean; type?: AssetType; status?: AssetStatus; withArchived?: boolean; withDeleted?: boolean; + visibility?: AssetVisibility; } export interface SearchOneToOneRelationOptions { @@ -144,8 +112,6 @@ type BaseAssetSearchOptions = SearchDateOptions & export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions; -export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions; - export type AssetSearchBuilderOptions = Omit; export type SmartSearchOptions = SearchDateOptions & @@ -216,16 +182,15 @@ export class SearchRepository { }, ], }) - async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated { + async searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions) { const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection; const items = await searchAssetBuilder(this.db, options) .orderBy('assets.fileCreatedAt', orderDirection) .limit(pagination.size + 1) .offset((pagination.page - 1) * pagination.size) .execute(); - const hasNextPage = items.length > pagination.size; - items.splice(pagination.size); - return { items: items as any as AssetEntity[], hasNextPage }; + + return paginationHelper(items, pagination.size); } @GenerateSql({ @@ -240,7 +205,7 @@ export class SearchRepository { }, ], }) - async searchRandom(size: number, options: AssetSearchOptions): Promise { + async searchRandom(size: number, options: AssetSearchOptions) { const uuid = randomUUID(); const builder = searchAssetBuilder(this.db, options); const lessThan = builder @@ -251,8 +216,8 @@ export class SearchRepository { .where('assets.id', '>', uuid) .orderBy(sql`random()`) .limit(size); - const { rows } = await sql`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db); - return rows as any as AssetEntity[]; + const { rows } = await sql`${lessThan} union all ${greaterThan} limit ${size}`.execute(this.db); + return rows; } @GenerateSql({ @@ -268,21 +233,21 @@ export class SearchRepository { }, ], }) - async searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated { + searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions) { if (!isValidInteger(pagination.size, { min: 1, max: 1000 })) { throw new Error(`Invalid value for 'size': ${pagination.size}`); } - const items = (await searchAssetBuilder(this.db, options) - .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') - .orderBy(sql`smart_search.embedding <=> ${options.embedding}`) - .limit(pagination.size + 1) - .offset((pagination.page - 1) * pagination.size) - .execute()) as any as AssetEntity[]; - - const hasNextPage = items.length > pagination.size; - items.splice(pagination.size); - return { items, hasNextPage }; + return this.db.transaction().execute(async (trx) => { + await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.CLIP])}`.execute(trx); + const items = await searchAssetBuilder(trx, options) + .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') + .orderBy(sql`smart_search.embedding <=> ${options.embedding}`) + .limit(pagination.size + 1) + .offset((pagination.page - 1) * pagination.size) + .execute(); + return paginationHelper(items, pagination.size); + }); } @GenerateSql({ @@ -297,29 +262,32 @@ export class SearchRepository { ], }) searchDuplicates({ assetId, embedding, maxDistance, type, userIds }: AssetDuplicateSearch) { - return this.db - .with('cte', (qb) => - qb - .selectFrom('assets') - .select([ - 'assets.id as assetId', - 'assets.duplicateId', - sql`smart_search.embedding <=> ${embedding}`.as('distance'), - ]) - .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') - .where('assets.ownerId', '=', anyUuid(userIds)) - .where('assets.deletedAt', 'is', null) - .where('assets.isVisible', '=', true) - .where('assets.type', '=', type) - .where('assets.id', '!=', asUuid(assetId)) - .where('assets.stackId', 'is', null) - .orderBy(sql`smart_search.embedding <=> ${embedding}`) - .limit(64), - ) - .selectFrom('cte') - .selectAll() - .where('cte.distance', '<=', maxDistance as number) - .execute(); + return this.db.transaction().execute(async (trx) => { + await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.CLIP])}`.execute(trx); + return await trx + .with('cte', (qb) => + qb + .selectFrom('assets') + .select([ + 'assets.id as assetId', + 'assets.duplicateId', + sql`smart_search.embedding <=> ${embedding}`.as('distance'), + ]) + .innerJoin('smart_search', 'assets.id', 'smart_search.assetId') + .where('assets.ownerId', '=', anyUuid(userIds)) + .where('assets.deletedAt', 'is', null) + .where('assets.visibility', '!=', AssetVisibility.HIDDEN) + .where('assets.type', '=', type) + .where('assets.id', '!=', asUuid(assetId)) + .where('assets.stackId', 'is', null) + .orderBy('distance') + .limit(64), + ) + .selectFrom('cte') + .selectAll() + .where('cte.distance', '<=', maxDistance as number) + .execute(); + }); } @GenerateSql({ @@ -337,31 +305,36 @@ export class SearchRepository { throw new Error(`Invalid value for 'numResults': ${numResults}`); } - return this.db - .with('cte', (qb) => - qb - .selectFrom('asset_faces') - .select([ - 'asset_faces.id', - 'asset_faces.personId', - sql`face_search.embedding <=> ${embedding}`.as('distance'), - ]) - .innerJoin('assets', 'assets.id', 'asset_faces.assetId') - .innerJoin('face_search', 'face_search.faceId', 'asset_faces.id') - .leftJoin('person', 'person.id', 'asset_faces.personId') - .where('assets.ownerId', '=', anyUuid(userIds)) - .where('assets.deletedAt', 'is', null) - .$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null)) - .$if(!!minBirthDate, (qb) => - qb.where((eb) => eb.or([eb('person.birthDate', 'is', null), eb('person.birthDate', '<=', minBirthDate!)])), - ) - .orderBy(sql`face_search.embedding <=> ${embedding}`) - .limit(numResults), - ) - .selectFrom('cte') - .selectAll() - .where('cte.distance', '<=', maxDistance) - .execute(); + return this.db.transaction().execute(async (trx) => { + await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.FACE])}`.execute(trx); + return await trx + .with('cte', (qb) => + qb + .selectFrom('asset_faces') + .select([ + 'asset_faces.id', + 'asset_faces.personId', + sql`face_search.embedding <=> ${embedding}`.as('distance'), + ]) + .innerJoin('assets', 'assets.id', 'asset_faces.assetId') + .innerJoin('face_search', 'face_search.faceId', 'asset_faces.id') + .leftJoin('person', 'person.id', 'asset_faces.personId') + .where('assets.ownerId', '=', anyUuid(userIds)) + .where('assets.deletedAt', 'is', null) + .$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null)) + .$if(!!minBirthDate, (qb) => + qb.where((eb) => + eb.or([eb('person.birthDate', 'is', null), eb('person.birthDate', '<=', minBirthDate!)]), + ), + ) + .orderBy('distance') + .limit(numResults), + ) + .selectFrom('cte') + .selectAll() + .where('cte.distance', '<=', maxDistance) + .execute(); + }); } @GenerateSql({ params: [DummyValue.STRING] }) @@ -392,7 +365,7 @@ export class SearchRepository { } @GenerateSql({ params: [[DummyValue.UUID]] }) - getAssetsByCity(userIds: string[]): Promise { + getAssetsByCity(userIds: string[]) { return this.db .withRecursive('cte', (qb) => { const base = qb @@ -400,8 +373,7 @@ export class SearchRepository { .select(['city', 'assetId']) .innerJoin('assets', 'assets.id', 'exif.assetId') .where('assets.ownerId', '=', anyUuid(userIds)) - .where('assets.isVisible', '=', true) - .where('assets.isArchived', '=', false) + .where('assets.visibility', '=', AssetVisibility.TIMELINE) .where('assets.type', '=', AssetType.IMAGE) .where('assets.deletedAt', 'is', null) .orderBy('city') @@ -417,8 +389,7 @@ export class SearchRepository { .select(['city', 'assetId']) .innerJoin('assets', 'assets.id', 'exif.assetId') .where('assets.ownerId', '=', anyUuid(userIds)) - .where('assets.isVisible', '=', true) - .where('assets.isArchived', '=', false) + .where('assets.visibility', '=', AssetVisibility.TIMELINE) .where('assets.type', '=', AssetType.IMAGE) .where('assets.deletedAt', 'is', null) .whereRef('exif.city', '>', 'cte.city') @@ -434,56 +405,24 @@ export class SearchRepository { .innerJoin('exif', 'assets.id', 'exif.assetId') .innerJoin('cte', 'assets.id', 'cte.assetId') .selectAll('assets') - .select((eb) => eb.fn('to_jsonb', [eb.table('exif')]).as('exifInfo')) + .select((eb) => + eb + .fn('to_jsonb', [eb.table('exif')]) + .$castTo>() + .as('exifInfo'), + ) .orderBy('exif.city') - .execute() as any as Promise; + .execute(); } async upsert(assetId: string, embedding: string): Promise { await this.db .insertInto('smart_search') - .values({ assetId: asUuid(assetId), embedding } as any) - .onConflict((oc) => oc.column('assetId').doUpdateSet({ embedding } as any)) + .values({ assetId, embedding }) + .onConflict((oc) => oc.column('assetId').doUpdateSet((eb) => ({ embedding: eb.ref('excluded.embedding') }))) .execute(); } - async getDimensionSize(): Promise { - const { rows } = await sql<{ dimsize: number }>` - select atttypmod as dimsize - from pg_attribute f - join pg_class c ON c.oid = f.attrelid - where c.relkind = 'r'::char - and f.attnum > 0 - and c.relname = 'smart_search' - and f.attname = 'embedding' - `.execute(this.db); - - const dimSize = rows[0]['dimsize']; - if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { - throw new Error(`Could not retrieve CLIP dimension size`); - } - return dimSize; - } - - setDimensionSize(dimSize: number): Promise { - if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) { - throw new Error(`Invalid CLIP dimension size: ${dimSize}`); - } - - return this.db.transaction().execute(async (trx) => { - await sql`truncate ${sql.table('smart_search')}`.execute(trx); - await trx.schema - .alterTable('smart_search') - .alterColumn('embedding', (col) => col.setDataType(sql.raw(`vector(${dimSize})`))) - .execute(); - await sql`reindex index clip_index`.execute(trx); - }); - } - - async deleteAllSearchEmbeddings(): Promise { - await sql`truncate ${sql.table('smart_search')}`.execute(this.db); - } - async getCountries(userIds: string[]): Promise { const res = await this.getExifField('country', userIds).execute(); return res.map((row) => row.country!); @@ -533,7 +472,7 @@ export class SearchRepository { .distinctOn(field) .innerJoin('assets', 'assets.id', 'exif.assetId') .where('ownerId', '=', anyUuid(userIds)) - .where('isVisible', '=', true) + .where('visibility', '!=', AssetVisibility.HIDDEN) .where('deletedAt', 'is', null) .where(field, 'is not', null); } diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index deb24123d0..4500094899 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -73,26 +73,54 @@ export class ServerInfoRepository { } } + buildVersions?: ServerBuildVersions; + + private async retrieveVersionFallback( + command: string, + commandTransform?: (output: string) => string, + version?: string, + ): Promise { + if (!version) { + const output = await maybeFirstLine(command); + version = commandTransform ? commandTransform(output) : output; + } + return version; + } + async getBuildVersions(): Promise { - const { nodeVersion, resourcePaths } = this.configRepository.getEnv(); + if (!this.buildVersions) { + const { nodeVersion, resourcePaths } = this.configRepository.getEnv(); - const [nodejsOutput, ffmpegOutput, magickOutput] = await Promise.all([ - maybeFirstLine('node --version'), - maybeFirstLine('ffmpeg -version'), - maybeFirstLine('convert --version'), - ]); + const lockfile: BuildLockfile | undefined = await readFile(resourcePaths.lockFile) + .then((buffer) => JSON.parse(buffer.toString())) + .catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`)); - const lockfile = await readFile(resourcePaths.lockFile) - .then((buffer) => JSON.parse(buffer.toString())) - .catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`)); + const [nodejsVersion, ffmpegVersion, magickVersion, exiftoolVersion] = await Promise.all([ + this.retrieveVersionFallback('node --version', undefined, nodeVersion), + this.retrieveVersionFallback( + 'ffmpeg -version', + (output) => output.replaceAll('ffmpeg version ', ''), + getLockfileVersion('ffmpeg', lockfile), + ), + this.retrieveVersionFallback( + 'magick --version', + (output) => output.replaceAll('Version: ImageMagick ', ''), + getLockfileVersion('imagemagick', lockfile), + ), + exiftool.version(), + ]); - return { - nodejs: nodejsOutput || nodeVersion || '', - exiftool: await exiftool.version(), - ffmpeg: getLockfileVersion('ffmpeg', lockfile) || ffmpegOutput.replaceAll('ffmpeg version', '') || '', - libvips: getLockfileVersion('libvips', lockfile) || sharp.versions.vips, - imagemagick: - getLockfileVersion('imagemagick', lockfile) || magickOutput.replaceAll('Version: ImageMagick ', '') || '', - }; + const libvipsVersion = getLockfileVersion('libvips', lockfile) || sharp.versions.vips; + + this.buildVersions = { + nodejs: nodejsVersion, + exiftool: exiftoolVersion, + ffmpeg: ffmpegVersion, + libvips: libvipsVersion, + imagemagick: magickVersion, + }; + } + + return this.buildVersions; } } diff --git a/server/src/repositories/session.repository.ts b/server/src/repositories/session.repository.ts index 742807dc9c..6c3d10cb9a 100644 --- a/server/src/repositories/session.repository.ts +++ b/server/src/repositories/session.repository.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; +import { DateTime } from 'luxon'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { DB, Sessions } from 'src/db'; @@ -13,13 +14,26 @@ export type SessionSearchOptions = { updatedBefore: Date }; export class SessionRepository { constructor(@InjectKysely() private db: Kysely) {} - @GenerateSql({ params: [{ updatedBefore: DummyValue.DATE }] }) - search(options: SessionSearchOptions) { + cleanup() { + return this.db + .deleteFrom('sessions') + .where((eb) => + eb.or([ + eb('updatedAt', '<=', DateTime.now().minus({ days: 90 }).toJSDate()), + eb.and([eb('expiresAt', 'is not', null), eb('expiresAt', '<=', DateTime.now().toJSDate())]), + ]), + ) + .returning(['id', 'deviceOS', 'deviceType']) + .execute(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + get(id: string) { return this.db .selectFrom('sessions') - .selectAll() - .where('sessions.updatedAt', '<=', options.updatedBefore) - .execute(); + .select(['id', 'expiresAt', 'pinExpiresAt']) + .where('id', '=', id) + .executeTakeFirst(); } @GenerateSql({ params: [DummyValue.STRING] }) @@ -37,6 +51,9 @@ export class SessionRepository { ).as('user'), ]) .where('sessions.token', '=', token) + .where((eb) => + eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]), + ) .executeTakeFirst(); } @@ -47,6 +64,9 @@ export class SessionRepository { .innerJoin('users', (join) => join.onRef('users.id', '=', 'sessions.userId').on('users.deletedAt', 'is', null)) .selectAll('sessions') .where('sessions.userId', '=', userId) + .where((eb) => + eb.or([eb('sessions.expiresAt', 'is', null), eb('sessions.expiresAt', '>', DateTime.now().toJSDate())]), + ) .orderBy('sessions.updatedAt', 'desc') .orderBy('sessions.createdAt', 'desc') .execute(); @@ -69,4 +89,9 @@ export class SessionRepository { async delete(id: string) { await this.db.deleteFrom('sessions').where('id', '=', asUuid(id)).execute(); } + + @GenerateSql({ params: [DummyValue.UUID] }) + async lockAll(userId: string) { + await this.db.updateTable('sessions').set({ pinExpiresAt: null }).where('userId', '=', userId).execute(); + } } diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 272d7f3794..67a97dc2d5 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, sql, Updateable } from 'kysely'; +import { Insertable, Kysely, NotNull, sql, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import _ from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; -import { columns } from 'src/database'; +import { Album, columns } from 'src/database'; import { DB, SharedLinks } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; +import { MapAsset } from 'src/dtos/asset-response.dto'; import { SharedLinkType } from 'src/enum'; export type SharedLinkSearchOptions = { @@ -19,7 +19,7 @@ export class SharedLinkRepository { constructor(@InjectKysely() private db: Kysely) {} @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - get(userId: string, id: string): Promise { + get(userId: string, id: string) { return this.db .selectFrom('shared_links') .selectAll('shared_links') @@ -87,18 +87,23 @@ export class SharedLinkRepository { .as('album'), (join) => join.onTrue(), ) - .select((eb) => eb.fn.coalesce(eb.fn.jsonAgg('a').filterWhere('a.id', 'is not', null), sql`'[]'`).as('assets')) + .select((eb) => + eb.fn + .coalesce(eb.fn.jsonAgg('a').filterWhere('a.id', 'is not', null), sql`'[]'`) + .$castTo() + .as('assets'), + ) .groupBy(['shared_links.id', sql`"album".*`]) - .select((eb) => eb.fn.toJson('album').as('album')) + .select((eb) => eb.fn.toJson('album').$castTo().as('album')) .where('shared_links.id', '=', id) .where('shared_links.userId', '=', userId) .where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)])) .orderBy('shared_links.createdAt', 'desc') - .executeTakeFirst() as Promise; + .executeTakeFirst(); } @GenerateSql({ params: [{ userId: DummyValue.UUID, albumId: DummyValue.UUID }] }) - getAll({ userId, albumId }: SharedLinkSearchOptions): Promise { + getAll({ userId, albumId }: SharedLinkSearchOptions) { return this.db .selectFrom('shared_links') .selectAll('shared_links') @@ -115,6 +120,7 @@ export class SharedLinkRepository { (join) => join.onTrue(), ) .select('assets.assets') + .$narrowType<{ assets: NotNull }>() .leftJoinLateral( (eb) => eb @@ -152,12 +158,12 @@ export class SharedLinkRepository { .as('album'), (join) => join.onTrue(), ) - .select((eb) => eb.fn.toJson('album').as('album')) + .select((eb) => eb.fn.toJson('album').$castTo().as('album')) .where((eb) => eb.or([eb('shared_links.type', '=', SharedLinkType.INDIVIDUAL), eb('album.id', 'is not', null)])) .$if(!!albumId, (eb) => eb.where('shared_links.albumId', '=', albumId!)) .orderBy('shared_links.createdAt', 'desc') .distinctOn(['shared_links.createdAt']) - .execute() as unknown as Promise; + .execute(); } @GenerateSql({ params: [DummyValue.BUFFER] }) @@ -177,7 +183,7 @@ export class SharedLinkRepository { .executeTakeFirst(); } - async create(entity: Insertable & { assetIds?: string[] }): Promise { + async create(entity: Insertable & { assetIds?: string[] }) { const { id } = await this.db .insertInto('shared_links') .values(_.omit(entity, 'assetIds')) @@ -194,7 +200,7 @@ export class SharedLinkRepository { return this.getSharedLinks(id); } - async update(entity: Updateable & { id: string; assetIds?: string[] }): Promise { + async update(entity: Updateable & { id: string; assetIds?: string[] }) { const { id } = await this.db .updateTable('shared_links') .set(_.omit(entity, 'assets', 'album', 'assetIds')) @@ -212,8 +218,8 @@ export class SharedLinkRepository { return this.getSharedLinks(id); } - async remove(entity: SharedLinkEntity): Promise { - await this.db.deleteFrom('shared_links').where('shared_links.id', '=', entity.id).execute(); + async remove(id: string): Promise { + await this.db.deleteFrom('shared_links').where('shared_links.id', '=', id).execute(); } private getSharedLinks(id: string) { @@ -236,9 +242,12 @@ export class SharedLinkRepository { (join) => join.onTrue(), ) .select((eb) => - eb.fn.coalesce(eb.fn.jsonAgg('assets').filterWhere('assets.id', 'is not', null), sql`'[]'`).as('assets'), + eb.fn + .coalesce(eb.fn.jsonAgg('assets').filterWhere('assets.id', 'is not', null), sql`'[]'`) + .$castTo() + .as('assets'), ) .groupBy('shared_links.id') - .executeTakeFirstOrThrow() as Promise; + .executeTakeFirstOrThrow(); } } diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index 75dd9b497f..c9d69fb37f 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -5,7 +5,6 @@ import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { AssetStack, DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { AssetEntity } from 'src/entities/asset.entity'; import { asUuid } from 'src/utils/database'; export interface StackSearch { @@ -36,9 +35,7 @@ const withAssets = (eb: ExpressionBuilder, withTags = false) .select((eb) => eb.fn.toJson('exifInfo').as('exifInfo')) .where('assets.deletedAt', 'is', null) .whereRef('assets.stackId', '=', 'asset_stack.id'), - ) - .$castTo() - .as('assets'); + ).as('assets'); }; @Injectable() diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 613142cb99..43fd732747 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -7,8 +7,8 @@ import { DummyValue, GenerateSql } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; import { SyncAck } from 'src/types'; -type auditTables = 'users_audit' | 'partners_audit' | 'assets_audit'; -type upsertTables = 'users' | 'partners' | 'assets' | 'exif'; +type AuditTables = 'users_audit' | 'partners_audit' | 'assets_audit' | 'albums_audit' | 'album_users_audit'; +type UpsertTables = 'users' | 'partners' | 'assets' | 'exif' | 'albums' | 'albums_shared_users_users'; @Injectable() export class SyncRepository { @@ -110,7 +110,6 @@ export class SyncRepository { .selectFrom('assets_audit') .select(['id', 'assetId']) .where('ownerId', '=', userId) - .$if(!!ack, (qb) => qb.where('id', '>', ack!.updateId)) .$call((qb) => this.auditTableFilters(qb, ack)) .stream(); } @@ -154,22 +153,118 @@ export class SyncRepository { .stream(); } - private auditTableFilters, D>(qb: SelectQueryBuilder, ack?: SyncAck) { - const builder = qb as SelectQueryBuilder; + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getAlbumDeletes(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('albums_audit') + .select(['id', 'albumId']) + .where('userId', '=', userId) + .$call((qb) => this.auditTableFilters(qb, ack)) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getAlbumUpserts(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('albums') + .distinctOn(['albums.id', 'albums.updateId']) + .where('albums.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('albums.updateId', '>', ack!.updateId)) + .orderBy('albums.updateId', 'asc') + .leftJoin('albums_shared_users_users as album_users', 'albums.id', 'album_users.albumsId') + .where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('album_users.usersId', '=', userId)])) + .select([ + 'albums.id', + 'albums.ownerId', + 'albums.albumName as name', + 'albums.description', + 'albums.createdAt', + 'albums.updatedAt', + 'albums.albumThumbnailAssetId as thumbnailAssetId', + 'albums.isActivityEnabled', + 'albums.order', + 'albums.updateId', + ]) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getAlbumUserDeletes(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('album_users_audit') + .select(['id', 'userId', 'albumId']) + .where((eb) => + eb( + 'albumId', + 'in', + eb + .selectFrom('albums') + .select(['id']) + .where('ownerId', '=', userId) + .union((eb) => + eb.parens( + eb + .selectFrom('albums_shared_users_users as albumUsers') + .select(['albumUsers.albumsId as id']) + .where('albumUsers.usersId', '=', userId), + ), + ), + ), + ) + .$call((qb) => this.auditTableFilters(qb, ack)) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getAlbumUserUpserts(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('albums_shared_users_users') + .select([ + 'albums_shared_users_users.albumsId as albumId', + 'albums_shared_users_users.usersId as userId', + 'albums_shared_users_users.role', + 'albums_shared_users_users.updateId', + ]) + .where('albums_shared_users_users.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('albums_shared_users_users.updateId', '>', ack!.updateId)) + .orderBy('albums_shared_users_users.updateId', 'asc') + .where((eb) => + eb( + 'albums_shared_users_users.albumsId', + 'in', + eb + .selectFrom('albums') + .select(['id']) + .where('ownerId', '=', userId) + .union((eb) => + eb.parens( + eb + .selectFrom('albums_shared_users_users as albumUsers') + .select(['albumUsers.albumsId as id']) + .where('albumUsers.usersId', '=', userId), + ), + ), + ), + ) + .stream(); + } + + private auditTableFilters, D>(qb: SelectQueryBuilder, ack?: SyncAck) { + const builder = qb as SelectQueryBuilder; return builder .where('deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) .$if(!!ack, (qb) => qb.where('id', '>', ack!.updateId)) - .orderBy(['id asc']) as SelectQueryBuilder; + .orderBy('id', 'asc') as SelectQueryBuilder; } - private upsertTableFilters, D>( + private upsertTableFilters, D>( qb: SelectQueryBuilder, ack?: SyncAck, ) { - const builder = qb as SelectQueryBuilder; + const builder = qb as SelectQueryBuilder; return builder .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) .$if(!!ack, (qb) => qb.where('updateId', '>', ack!.updateId)) - .orderBy(['updateId asc']) as SelectQueryBuilder; + .orderBy('updateId', 'asc') as SelectQueryBuilder; } } diff --git a/server/src/repositories/system-metadata.repository.ts b/server/src/repositories/system-metadata.repository.ts index 2038f204f7..fcccde6a5c 100644 --- a/server/src/repositories/system-metadata.repository.ts +++ b/server/src/repositories/system-metadata.repository.ts @@ -26,7 +26,6 @@ export class SystemMetadataRepository { return metadata.value as SystemMetadata[T]; } - @GenerateSql({ params: ['metadata_key', { foo: 'bar' }] }) async set(key: T, value: SystemMetadata[T]): Promise { await this.db .insertInto('system_metadata') diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index 9a3b33188f..a7cdc9554c 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -68,7 +68,7 @@ export class TagRepository { @GenerateSql({ params: [DummyValue.UUID] }) getAll(userId: string) { - return this.db.selectFrom('tags').select(columns.tag).where('userId', '=', userId).orderBy('value asc').execute(); + return this.db.selectFrom('tags').select(columns.tag).where('userId', '=', userId).orderBy('value').execute(); } @GenerateSql({ params: [{ userId: DummyValue.UUID, color: DummyValue.STRING, value: DummyValue.STRING }] }) @@ -126,7 +126,7 @@ export class TagRepository { await this.db.deleteFrom('tag_asset').where('tagsId', '=', tagId).where('assetsId', 'in', assetIds).execute(); } - @GenerateSql({ params: [{ assetId: DummyValue.UUID, tagsIds: [DummyValue.UUID] }] }) + @GenerateSql({ params: [[{ assetId: DummyValue.UUID, tagsIds: [DummyValue.UUID] }]] }) @Chunked() upsertAssetIds(items: Insertable[]) { if (items.length === 0) { @@ -160,7 +160,6 @@ export class TagRepository { }); } - @GenerateSql() async deleteEmptyTags() { // TODO rewrite as a single statement await this.db.transaction().execute(async (tx) => { diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index e2e396f7b2..6972479df6 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -6,7 +6,7 @@ import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { DB, UserMetadata as DbUserMetadata } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { AssetType, UserStatus } from 'src/enum'; +import { AssetType, AssetVisibility, UserStatus } from 'src/enum'; import { UserTable } from 'src/schema/tables/user.table'; import { UserMetadata, UserMetadataItem } from 'src/types'; import { asUuid } from 'src/utils/database'; @@ -14,6 +14,7 @@ import { asUuid } from 'src/utils/database'; type Upsert = Insertable; export interface UserListFilter { + id?: string; withDeleted?: boolean; } @@ -89,13 +90,23 @@ export class UserRepository { return !!admin; } + @GenerateSql({ params: [DummyValue.UUID] }) + getForPinCode(id: string) { + return this.db + .selectFrom('users') + .select(['users.pinCode', 'users.password']) + .where('users.id', '=', id) + .where('users.deletedAt', 'is', null) + .executeTakeFirstOrThrow(); + } + @GenerateSql({ params: [DummyValue.EMAIL] }) - getByEmail(email: string, withPassword?: boolean) { + getByEmail(email: string, options?: { withPassword?: boolean }) { return this.db .selectFrom('users') .select(columns.userAdmin) .select(withMetadata) - .$if(!!withPassword, (eb) => eb.select('password')) + .$if(!!options?.withPassword, (eb) => eb.select('password')) .where('email', '=', email) .where('users.deletedAt', 'is', null) .executeTakeFirst(); @@ -131,12 +142,13 @@ export class UserRepository { { name: 'with deleted', params: [{ withDeleted: true }] }, { name: 'without deleted', params: [{ withDeleted: false }] }, ) - getList({ withDeleted }: UserListFilter = {}) { + getList({ id, withDeleted }: UserListFilter = {}) { return this.db .selectFrom('users') .select(columns.userAdmin) .select(withMetadata) .$if(!withDeleted, (eb) => eb.where('users.deletedAt', 'is', null)) + .$if(!!id, (eb) => eb.where('users.id', '=', id!)) .orderBy('createdAt', 'desc') .execute(); } @@ -205,13 +217,19 @@ export class UserRepository { eb.fn .countAll() .filterWhere((eb) => - eb.and([eb('assets.type', '=', sql.lit(AssetType.IMAGE)), eb('assets.isVisible', '=', sql.lit(true))]), + eb.and([ + eb('assets.type', '=', sql.lit(AssetType.IMAGE)), + eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)), + ]), ) .as('photos'), eb.fn .countAll() .filterWhere((eb) => - eb.and([eb('assets.type', '=', sql.lit(AssetType.VIDEO)), eb('assets.isVisible', '=', sql.lit(true))]), + eb.and([ + eb('assets.type', '=', sql.lit(AssetType.VIDEO)), + eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)), + ]), ) .as('videos'), eb.fn diff --git a/server/src/repositories/version-history.repository.ts b/server/src/repositories/version-history.repository.ts index 063ee0da84..b1d2696164 100644 --- a/server/src/repositories/version-history.repository.ts +++ b/server/src/repositories/version-history.repository.ts @@ -18,7 +18,6 @@ export class VersionHistoryRepository { return this.db.selectFrom('version_history').selectAll().orderBy('createdAt', 'desc').executeTakeFirst(); } - @GenerateSql({ params: [{ version: 'v1.123.0' }] }) create(version: Insertable) { return this.db.insertInto('version_history').values(version).returningAll().executeTakeFirstOrThrow(); } diff --git a/server/src/repositories/view-repository.ts b/server/src/repositories/view-repository.ts index ae2303e9e2..03e8b3763f 100644 --- a/server/src/repositories/view-repository.ts +++ b/server/src/repositories/view-repository.ts @@ -2,8 +2,8 @@ import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DB } from 'src/db'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { withExif } from 'src/entities/asset.entity'; -import { asUuid } from 'src/utils/database'; +import { AssetVisibility } from 'src/enum'; +import { asUuid, withExif } from 'src/utils/database'; export class ViewRepository { constructor(@InjectKysely() private db: Kysely) {} @@ -15,8 +15,7 @@ export class ViewRepository { .select((eb) => eb.fn('substring', ['assets.originalPath', eb.val('^(.*/)[^/]*$')]).as('directoryPath')) .distinct() .where('ownerId', '=', asUuid(userId)) - .where('isVisible', '=', true) - .where('isArchived', '=', false) + .where('visibility', '=', AssetVisibility.TIMELINE) .where('deletedAt', 'is', null) .where('fileCreatedAt', 'is not', null) .where('fileModifiedAt', 'is not', null) @@ -35,8 +34,7 @@ export class ViewRepository { .selectAll('assets') .$call(withExif) .where('ownerId', '=', asUuid(userId)) - .where('isVisible', '=', true) - .where('isArchived', '=', false) + .where('visibility', '=', AssetVisibility.TIMELINE) .where('deletedAt', 'is', null) .where('fileCreatedAt', 'is not', null) .where('fileModifiedAt', 'is not', null) diff --git a/server/src/schema/enums.ts b/server/src/schema/enums.ts index 100b92aa63..a1134df6bc 100644 --- a/server/src/schema/enums.ts +++ b/server/src/schema/enums.ts @@ -1,4 +1,4 @@ -import { AssetStatus, SourceType } from 'src/enum'; +import { AssetStatus, AssetVisibility, SourceType } from 'src/enum'; import { registerEnum } from 'src/sql-tools'; export const assets_status_enum = registerEnum({ @@ -10,3 +10,8 @@ export const asset_face_source_type = registerEnum({ name: 'sourcetype', values: Object.values(SourceType), }); + +export const asset_visibility_enum = registerEnum({ + name: 'asset_visibility_enum', + values: Object.values(AssetVisibility), +}); diff --git a/server/src/schema/functions.ts b/server/src/schema/functions.ts index 65ad2b72dc..a03f715bff 100644 --- a/server/src/schema/functions.ts +++ b/server/src/schema/functions.ts @@ -23,6 +23,19 @@ export const immich_uuid_v7 = registerFunction({ synchronize: false, }); +export const album_user_after_insert = registerFunction({ + name: 'album_user_after_insert', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + UPDATE albums SET "updatedAt" = clock_timestamp(), "updateId" = immich_uuid_v7(clock_timestamp()) + WHERE "id" IN (SELECT DISTINCT "albumsId" FROM inserted_rows); + RETURN NULL; + END`, + synchronize: false, +}); + export const updated_at = registerFunction({ name: 'updated_at', returnType: 'TRIGGER', @@ -114,3 +127,38 @@ export const assets_delete_audit = registerFunction({ END`, synchronize: false, }); + +export const albums_delete_audit = registerFunction({ + name: 'albums_delete_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO albums_audit ("albumId", "userId") + SELECT "id", "ownerId" + FROM OLD; + RETURN NULL; + END`, + synchronize: false, +}); + +export const album_users_delete_audit = registerFunction({ + name: 'album_users_delete_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO albums_audit ("albumId", "userId") + SELECT "albumsId", "usersId" + FROM OLD; + + IF pg_trigger_depth() = 1 THEN + INSERT INTO album_users_audit ("albumId", "userId") + SELECT "albumsId", "usersId" + FROM OLD; + END IF; + + RETURN NULL; + END`, + synchronize: false, +}); diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index fe4b86d65c..d2f8d80afc 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -1,5 +1,8 @@ -import { asset_face_source_type, assets_status_enum } from 'src/schema/enums'; +import { asset_face_source_type, asset_visibility_enum, assets_status_enum } from 'src/schema/enums'; import { + album_user_after_insert, + album_users_delete_audit, + albums_delete_audit, assets_delete_audit, f_concat_ws, f_unaccent, @@ -11,6 +14,8 @@ import { } from 'src/schema/functions'; import { ActivityTable } from 'src/schema/tables/activity.table'; import { AlbumAssetTable } from 'src/schema/tables/album-asset.table'; +import { AlbumAuditTable } from 'src/schema/tables/album-audit.table'; +import { AlbumUserAuditTable } from 'src/schema/tables/album-user-audit.table'; import { AlbumUserTable } from 'src/schema/tables/album-user.table'; import { AlbumTable } from 'src/schema/tables/album.table'; import { APIKeyTable } from 'src/schema/tables/api-key.table'; @@ -28,6 +33,7 @@ import { MemoryTable } from 'src/schema/tables/memory.table'; import { MemoryAssetTable } from 'src/schema/tables/memory_asset.table'; import { MoveTable } from 'src/schema/tables/move.table'; import { NaturalEarthCountriesTable } from 'src/schema/tables/natural-earth-countries.table'; +import { NotificationTable } from 'src/schema/tables/notification.table'; import { PartnerAuditTable } from 'src/schema/tables/partner-audit.table'; import { PartnerTable } from 'src/schema/tables/partner.table'; import { PersonTable } from 'src/schema/tables/person.table'; @@ -44,21 +50,16 @@ import { UserAuditTable } from 'src/schema/tables/user-audit.table'; import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserTable } from 'src/schema/tables/user.table'; import { VersionHistoryTable } from 'src/schema/tables/version-history.table'; -import { ConfigurationParameter, Database, Extensions } from 'src/sql-tools'; +import { Database, Extensions } from 'src/sql-tools'; -@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'vectors', 'plpgsql']) -@ConfigurationParameter({ name: 'search_path', value: () => '"$user", public, vectors', scope: 'database' }) -@ConfigurationParameter({ - name: 'vectors.pgvector_compatibility', - value: () => 'on', - scope: 'user', - synchronize: false, -}) +@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql']) @Database({ name: 'immich' }) export class ImmichDatabase { tables = [ ActivityTable, AlbumAssetTable, + AlbumAuditTable, + AlbumUserAuditTable, AlbumUserTable, AlbumTable, APIKeyTable, @@ -76,6 +77,7 @@ export class ImmichDatabase { MemoryTable, MoveTable, NaturalEarthCountriesTable, + NotificationTable, PartnerAuditTable, PartnerTable, PersonTable, @@ -103,7 +105,10 @@ export class ImmichDatabase { users_delete_audit, partners_delete_audit, assets_delete_audit, + albums_delete_audit, + album_user_after_insert, + album_users_delete_audit, ]; - enum = [assets_status_enum, asset_face_source_type]; + enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum]; } diff --git a/server/src/schema/migrations/1744910873969-InitialMigration.ts b/server/src/schema/migrations/1744910873969-InitialMigration.ts new file mode 100644 index 0000000000..63625a69ad --- /dev/null +++ b/server/src/schema/migrations/1744910873969-InitialMigration.ts @@ -0,0 +1,411 @@ +import { Kysely, sql } from 'kysely'; +import { DatabaseExtension } from 'src/enum'; +import { getVectorExtension } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { vectorIndexQuery } from 'src/utils/database'; + +const lastMigrationSql = sql<{ name: string }>`SELECT "name" FROM "migrations" ORDER BY "timestamp" DESC LIMIT 1;`; +const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; +const logger = LoggingRepository.create(); + +export async function up(db: Kysely): Promise { + const { rows } = await tableExists.execute(db); + const hasTypeOrmMigrations = !!rows[0]?.result; + if (hasTypeOrmMigrations) { + const { + rows: [lastMigration], + } = await lastMigrationSql.execute(db); + if (lastMigration?.name !== 'AddMissingIndex1744910873956') { + throw new Error( + 'Invalid upgrade path. For more information, see https://immich.app/errors#typeorm-upgrade', + ); + } + logger.log('Database has up to date TypeORM migrations, skipping initial Kysely migration'); + return; + } + + const vectorExtension = await getVectorExtension(db); + + await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS "unaccent";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS "cube";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS "earthdistance";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS "pg_trgm";`.execute(db); + await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(vectorExtension)} CASCADE`.execute(db); + await sql`CREATE OR REPLACE FUNCTION immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) + RETURNS uuid + VOLATILE LANGUAGE SQL + AS $$ + select encode( + set_bit( + set_bit( + overlay(uuid_send(gen_random_uuid()) + placing substring(int8send(floor(extract(epoch from p_timestamp) * 1000)::bigint) from 3) + from 1 for 6 + ), + 52, 1 + ), + 53, 1 + ), + 'hex')::uuid; + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION updated_at() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + DECLARE + clock_timestamp TIMESTAMP := clock_timestamp(); + BEGIN + new."updatedAt" = clock_timestamp; + new."updateId" = immich_uuid_v7(clock_timestamp); + return new; + END; + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION f_concat_ws(text, text[]) + RETURNS text + PARALLEL SAFE IMMUTABLE LANGUAGE SQL + AS $$SELECT array_to_string($2, $1)$$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION f_unaccent(text) + RETURNS text + PARALLEL SAFE STRICT IMMUTABLE LANGUAGE SQL + RETURN unaccent('unaccent', $1)`.execute(db); + await sql`CREATE OR REPLACE FUNCTION ll_to_earth_public(latitude double precision, longitude double precision) + RETURNS public.earth + PARALLEL SAFE STRICT IMMUTABLE LANGUAGE SQL + AS $$ + SELECT public.cube(public.cube(public.cube(public.earth()*cos(radians(latitude))*cos(radians(longitude))),public.earth()*cos(radians(latitude))*sin(radians(longitude))),public.earth()*sin(radians(latitude)))::public.earth + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION users_delete_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO users_audit ("userId") + SELECT "id" + FROM OLD; + RETURN NULL; + END; + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION partners_delete_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO partners_audit ("sharedById", "sharedWithId") + SELECT "sharedById", "sharedWithId" + FROM OLD; + RETURN NULL; + END; + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION assets_delete_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO assets_audit ("assetId", "ownerId") + SELECT "id", "ownerId" + FROM OLD; + RETURN NULL; + END; + $$;`.execute(db); + if (vectorExtension === DatabaseExtension.VECTORS) { + await sql`SET search_path TO "$user", public, vectors`.execute(db); + } + await sql`CREATE TYPE "assets_status_enum" AS ENUM ('active','trashed','deleted');`.execute(db); + await sql`CREATE TYPE "sourcetype" AS ENUM ('machine-learning','exif','manual');`.execute(db); + await sql`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "email" character varying NOT NULL, "password" character varying NOT NULL DEFAULT '', "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "profileImagePath" character varying NOT NULL DEFAULT '', "isAdmin" boolean NOT NULL DEFAULT false, "shouldChangePassword" boolean NOT NULL DEFAULT true, "deletedAt" timestamp with time zone, "oauthId" character varying NOT NULL DEFAULT '', "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "storageLabel" character varying, "name" character varying NOT NULL DEFAULT '', "quotaSizeInBytes" bigint, "quotaUsageInBytes" bigint NOT NULL DEFAULT 0, "status" character varying NOT NULL DEFAULT 'active', "profileChangedAt" timestamp with time zone NOT NULL DEFAULT now(), "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "libraries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "ownerId" uuid NOT NULL, "importPaths" text[] NOT NULL, "exclusionPatterns" text[] NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "deletedAt" timestamp with time zone, "refreshedAt" timestamp with time zone, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "asset_stack" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "primaryAssetId" uuid NOT NULL, "ownerId" uuid NOT NULL);`.execute(db); + await sql`CREATE TABLE "assets" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "deviceAssetId" character varying NOT NULL, "ownerId" uuid NOT NULL, "deviceId" character varying NOT NULL, "type" character varying NOT NULL, "originalPath" character varying NOT NULL, "fileCreatedAt" timestamp with time zone NOT NULL, "fileModifiedAt" timestamp with time zone NOT NULL, "isFavorite" boolean NOT NULL DEFAULT false, "duration" character varying, "encodedVideoPath" character varying DEFAULT '', "checksum" bytea NOT NULL, "isVisible" boolean NOT NULL DEFAULT true, "livePhotoVideoId" uuid, "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "isArchived" boolean NOT NULL DEFAULT false, "originalFileName" character varying NOT NULL, "sidecarPath" character varying, "thumbhash" bytea, "isOffline" boolean NOT NULL DEFAULT false, "libraryId" uuid, "isExternal" boolean NOT NULL DEFAULT false, "deletedAt" timestamp with time zone, "localDateTime" timestamp with time zone NOT NULL, "stackId" uuid, "duplicateId" uuid, "status" assets_status_enum NOT NULL DEFAULT 'active', "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "albums" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "ownerId" uuid NOT NULL, "albumName" character varying NOT NULL DEFAULT 'Untitled Album', "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "albumThumbnailAssetId" uuid, "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "description" text NOT NULL DEFAULT '', "deletedAt" timestamp with time zone, "isActivityEnabled" boolean NOT NULL DEFAULT true, "order" character varying NOT NULL DEFAULT 'desc', "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`COMMENT ON COLUMN "albums"."albumThumbnailAssetId" IS 'Asset ID to be used as thumbnail';`.execute(db); + await sql`CREATE TABLE "activity" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "assetId" uuid, "comment" text, "isLiked" boolean NOT NULL DEFAULT false, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "albums_assets_assets" ("albumsId" uuid NOT NULL, "assetsId" uuid NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now());`.execute(db); + await sql`CREATE TABLE "albums_shared_users_users" ("albumsId" uuid NOT NULL, "usersId" uuid NOT NULL, "role" character varying NOT NULL DEFAULT 'editor');`.execute(db); + await sql`CREATE TABLE "api_keys" ("name" character varying NOT NULL, "key" character varying NOT NULL, "userId" uuid NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "permissions" character varying[] NOT NULL, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "assetId" uuid NOT NULL, "ownerId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db); + await sql`CREATE TABLE "person" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "ownerId" uuid NOT NULL, "name" character varying NOT NULL DEFAULT '', "thumbnailPath" character varying NOT NULL DEFAULT '', "isHidden" boolean NOT NULL DEFAULT false, "birthDate" date, "faceAssetId" uuid, "isFavorite" boolean NOT NULL DEFAULT false, "color" character varying, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "asset_faces" ("assetId" uuid NOT NULL, "personId" uuid, "imageWidth" integer NOT NULL DEFAULT 0, "imageHeight" integer NOT NULL DEFAULT 0, "boundingBoxX1" integer NOT NULL DEFAULT 0, "boundingBoxY1" integer NOT NULL DEFAULT 0, "boundingBoxX2" integer NOT NULL DEFAULT 0, "boundingBoxY2" integer NOT NULL DEFAULT 0, "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "sourceType" sourcetype NOT NULL DEFAULT 'machine-learning', "deletedAt" timestamp with time zone);`.execute(db); + await sql`CREATE TABLE "asset_files" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "assetId" uuid NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "type" character varying NOT NULL, "path" character varying NOT NULL, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "asset_job_status" ("assetId" uuid NOT NULL, "facesRecognizedAt" timestamp with time zone, "metadataExtractedAt" timestamp with time zone, "duplicatesDetectedAt" timestamp with time zone, "previewAt" timestamp with time zone, "thumbnailAt" timestamp with time zone);`.execute(db); + await sql`CREATE TABLE "audit" ("id" serial NOT NULL, "entityType" character varying NOT NULL, "entityId" uuid NOT NULL, "action" character varying NOT NULL, "ownerId" uuid NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now());`.execute(db); + await sql`CREATE TABLE "exif" ("assetId" uuid NOT NULL, "make" character varying, "model" character varying, "exifImageWidth" integer, "exifImageHeight" integer, "fileSizeInByte" bigint, "orientation" character varying, "dateTimeOriginal" timestamp with time zone, "modifyDate" timestamp with time zone, "lensModel" character varying, "fNumber" double precision, "focalLength" double precision, "iso" integer, "latitude" double precision, "longitude" double precision, "city" character varying, "state" character varying, "country" character varying, "description" text NOT NULL DEFAULT '', "fps" double precision, "exposureTime" character varying, "livePhotoCID" character varying, "timeZone" character varying, "projectionType" character varying, "profileDescription" character varying, "colorspace" character varying, "bitsPerSample" integer, "autoStackId" character varying, "rating" integer, "updatedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "face_search" ("faceId" uuid NOT NULL, "embedding" vector(512) NOT NULL);`.execute(db); + await sql`CREATE TABLE "geodata_places" ("id" integer NOT NULL, "name" character varying(200) NOT NULL, "longitude" double precision NOT NULL, "latitude" double precision NOT NULL, "countryCode" character(2) NOT NULL, "admin1Code" character varying(20), "admin2Code" character varying(80), "modificationDate" date NOT NULL, "admin1Name" character varying, "admin2Name" character varying, "alternateNames" character varying);`.execute(db); + await sql`CREATE TABLE "memories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "deletedAt" timestamp with time zone, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "data" jsonb NOT NULL, "isSaved" boolean NOT NULL DEFAULT false, "memoryAt" timestamp with time zone NOT NULL, "seenAt" timestamp with time zone, "showAt" timestamp with time zone, "hideAt" timestamp with time zone, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "memories_assets_assets" ("memoriesId" uuid NOT NULL, "assetsId" uuid NOT NULL);`.execute(db); + await sql`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" uuid NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL);`.execute(db); + await sql`CREATE TABLE "naturalearth_countries" ("id" integer NOT NULL GENERATED ALWAYS AS IDENTITY, "admin" character varying(50) NOT NULL, "admin_a3" character varying(3) NOT NULL, "type" character varying(50) NOT NULL, "coordinates" polygon NOT NULL);`.execute(db); + await sql`CREATE TABLE "partners_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db); + await sql`CREATE TABLE "partners" ("sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "inTimeline" boolean NOT NULL DEFAULT false, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "sessions" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "userId" uuid NOT NULL, "deviceType" character varying NOT NULL DEFAULT '', "deviceOS" character varying NOT NULL DEFAULT '', "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "shared_links" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "description" character varying, "userId" uuid NOT NULL, "key" bytea NOT NULL, "type" character varying NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "expiresAt" timestamp with time zone, "allowUpload" boolean NOT NULL DEFAULT false, "albumId" uuid, "allowDownload" boolean NOT NULL DEFAULT true, "showExif" boolean NOT NULL DEFAULT true, "password" character varying);`.execute(db); + await sql`CREATE TABLE "shared_link__asset" ("assetsId" uuid NOT NULL, "sharedLinksId" uuid NOT NULL);`.execute(db); + await sql`CREATE TABLE "smart_search" ("assetId" uuid NOT NULL, "embedding" vector(512) NOT NULL);`.execute(db); + await sql`ALTER TABLE "smart_search" ALTER COLUMN "embedding" SET STORAGE EXTERNAL;`.execute(db); + await sql`CREATE TABLE "session_sync_checkpoints" ("sessionId" uuid NOT NULL, "type" character varying NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "ack" character varying NOT NULL, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "system_metadata" ("key" character varying NOT NULL, "value" jsonb NOT NULL);`.execute(db); + await sql`CREATE TABLE "tags" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "userId" uuid NOT NULL, "value" character varying NOT NULL, "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "color" character varying, "parentId" uuid, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "tag_asset" ("assetsId" uuid NOT NULL, "tagsId" uuid NOT NULL);`.execute(db); + await sql`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL);`.execute(db); + await sql`CREATE TABLE "users_audit" ("userId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), "id" uuid NOT NULL DEFAULT immich_uuid_v7());`.execute(db); + await sql`CREATE TABLE "user_metadata" ("userId" uuid NOT NULL, "key" character varying NOT NULL, "value" jsonb NOT NULL);`.execute(db); + await sql`CREATE TABLE "version_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "version" character varying NOT NULL);`.execute(db); + await sql`ALTER TABLE "users" ADD CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "libraries" ADD CONSTRAINT "PK_505fedfcad00a09b3734b4223de" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "asset_stack" ADD CONSTRAINT "PK_74a27e7fcbd5852463d0af3034b" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "assets" ADD CONSTRAINT "PK_da96729a8b113377cfb6a62439c" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "albums" ADD CONSTRAINT "PK_7f71c7b5bc7c87b8f94c9a93a00" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "activity" ADD CONSTRAINT "PK_24625a1d6b1b089c8ae206fe467" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId");`.execute(db); + await sql`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId");`.execute(db); + await sql`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "assets_audit" ADD CONSTRAINT "PK_99bd5c015f81a641927a32b4212" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "person" ADD CONSTRAINT "PK_5fdaf670315c4b7e70cce85daa3" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "asset_files" ADD CONSTRAINT "PK_c41dc3e9ef5e1c57ca5a08a0004" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "asset_job_status" ADD CONSTRAINT "PK_420bec36fc02813bddf5c8b73d4" PRIMARY KEY ("assetId");`.execute(db); + await sql`ALTER TABLE "audit" ADD CONSTRAINT "PK_1d3d120ddaf7bc9b1ed68ed463a" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "exif" ADD CONSTRAINT "PK_c0117fdbc50b917ef9067740c44" PRIMARY KEY ("assetId");`.execute(db); + await sql`ALTER TABLE "face_search" ADD CONSTRAINT "face_search_pkey" PRIMARY KEY ("faceId");`.execute(db); + await sql`ALTER TABLE "geodata_places" ADD CONSTRAINT "PK_c29918988912ef4036f3d7fbff4" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "memories" ADD CONSTRAINT "PK_aaa0692d9496fe827b0568612f8" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "PK_fcaf7112a013d1703c011c6793d" PRIMARY KEY ("memoriesId", "assetsId");`.execute(db); + await sql`ALTER TABLE "move_history" ADD CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "naturalearth_countries" ADD CONSTRAINT "PK_21a6d86d1ab5d841648212e5353" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "partners_audit" ADD CONSTRAINT "PK_952b50217ff78198a7e380f0359" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "partners" ADD CONSTRAINT "PK_f1cc8f73d16b367f426261a8736" PRIMARY KEY ("sharedById", "sharedWithId");`.execute(db); + await sql`ALTER TABLE "sessions" ADD CONSTRAINT "PK_48cb6b5c20faa63157b3c1baf7f" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "shared_links" ADD CONSTRAINT "PK_642e2b0f619e4876e5f90a43465" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "PK_9b4f3687f9b31d1e311336b05e3" PRIMARY KEY ("assetsId", "sharedLinksId");`.execute(db); + await sql`ALTER TABLE "smart_search" ADD CONSTRAINT "smart_search_pkey" PRIMARY KEY ("assetId");`.execute(db); + await sql`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "PK_b846ab547a702863ef7cd9412fb" PRIMARY KEY ("sessionId", "type");`.execute(db); + await sql`ALTER TABLE "system_metadata" ADD CONSTRAINT "PK_fa94f6857470fb5b81ec6084465" PRIMARY KEY ("key");`.execute(db); + await sql`ALTER TABLE "tags" ADD CONSTRAINT "PK_e7dc17249a1148a1970748eda99" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "tag_asset" ADD CONSTRAINT "PK_ef5346fe522b5fb3bc96454747e" PRIMARY KEY ("assetsId", "tagsId");`.execute(db); + await sql`ALTER TABLE "tags_closure" ADD CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant");`.execute(db); + await sql`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "user_metadata" ADD CONSTRAINT "PK_5931462150b3438cbc83277fe5a" PRIMARY KEY ("userId", "key");`.execute(db); + await sql`ALTER TABLE "version_history" ADD CONSTRAINT "PK_5db259cbb09ce82c0d13cfd1b23" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "libraries" ADD CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_91704e101438fd0653f582426dc" FOREIGN KEY ("primaryAssetId") REFERENCES "assets" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION;`.execute(db); + await sql`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE SET NULL;`.execute(db); + await sql`ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack" ("id") ON UPDATE CASCADE ON DELETE SET NULL;`.execute(db); + await sql`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "albums" ADD CONSTRAINT "FK_05895aa505a670300d4816debce" FOREIGN KEY ("albumThumbnailAssetId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE SET NULL;`.execute(db); + await sql`ALTER TABLE "activity" ADD CONSTRAINT "FK_1af8519996fbfb3684b58df280b" FOREIGN KEY ("albumId") REFERENCES "albums" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "activity" ADD CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "activity" ADD CONSTRAINT "FK_8091ea76b12338cb4428d33d782" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "person" ADD CONSTRAINT "FK_5527cc99f530a547093f9e577b6" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "asset_faces" ("id") ON UPDATE NO ACTION ON DELETE SET NULL;`.execute(db); + await sql`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person" ("id") ON UPDATE CASCADE ON DELETE SET NULL;`.execute(db); + await sql`ALTER TABLE "asset_files" ADD CONSTRAINT "FK_e3e103a5f1d8bc8402999286040" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "asset_job_status" ADD CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "face_search" ADD CONSTRAINT "face_search_faceId_fkey" FOREIGN KEY ("faceId") REFERENCES "asset_faces" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "memories" ADD CONSTRAINT "FK_575842846f0c28fa5da46c99b19" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e" FOREIGN KEY ("memoriesId") REFERENCES "memories" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f" FOREIGN KEY ("assetsId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "partners" ADD CONSTRAINT "FK_7e077a8b70b3530138610ff5e04" FOREIGN KEY ("sharedById") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "partners" ADD CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3" FOREIGN KEY ("sharedWithId") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "sessions" ADD CONSTRAINT "FK_57de40bc620f456c7311aa3a1e6" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66" FOREIGN KEY ("assetsId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "smart_search" ADD CONSTRAINT "smart_search_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "assets" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc" FOREIGN KEY ("sessionId") REFERENCES "sessions" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags" ("id") ON UPDATE NO ACTION ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "user_metadata" ADD CONSTRAINT "FK_6afb43681a21cf7815932bc38ac" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email");`.execute(db); + await sql`ALTER TABLE "users" ADD CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a" UNIQUE ("storageLabel");`.execute(db); + await sql`ALTER TABLE "asset_stack" ADD CONSTRAINT "REL_91704e101438fd0653f582426d" UNIQUE ("primaryAssetId");`.execute(db); + await sql`ALTER TABLE "asset_files" ADD CONSTRAINT "UQ_assetId_type" UNIQUE ("assetId", "type");`.execute(db); + await sql`ALTER TABLE "move_history" ADD CONSTRAINT "UQ_newPath" UNIQUE ("newPath");`.execute(db); + await sql`ALTER TABLE "move_history" ADD CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType");`.execute(db); + await sql`ALTER TABLE "shared_links" ADD CONSTRAINT "UQ_sharedlink_key" UNIQUE ("key");`.execute(db); + await sql`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value");`.execute(db); + await sql`ALTER TABLE "activity" ADD CONSTRAINT "CHK_2ab1e70f113f450eb40c1e3ec8" CHECK (("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false));`.execute(db); + await sql`ALTER TABLE "person" ADD CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45" CHECK ("birthDate" <= CURRENT_DATE);`.execute(db); + await sql`CREATE INDEX "IDX_users_updated_at_asc_id_asc" ON "users" ("updatedAt", "id")`.execute(db); + await sql`CREATE INDEX "IDX_users_update_id" ON "users" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`.execute(db); + await sql`CREATE INDEX "IDX_libraries_update_id" ON "libraries" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`.execute(db); + await sql`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`.execute(db); + await sql`CREATE INDEX "idx_originalfilename_trigram" ON "assets" USING gin (f_unaccent("originalFileName") gin_trgm_ops)`.execute(db); + await sql`CREATE INDEX "IDX_asset_id_stackId" ON "assets" ("id", "stackId")`.execute(db); + await sql`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`.execute(db); + await sql`CREATE INDEX "idx_local_date_time_month" ON "assets" ((date_trunc('MONTH'::text, ("localDateTime" AT TIME ZONE 'UTC'::text)) AT TIME ZONE 'UTC'::text))`.execute(db); + await sql`CREATE INDEX "idx_local_date_time" ON "assets" ((("localDateTime" at time zone 'UTC')::date))`.execute(db); + await sql`CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" ON "assets" ("ownerId", "libraryId", "checksum") WHERE ("libraryId" IS NOT NULL)`.execute(db); + await sql`CREATE UNIQUE INDEX "UQ_assets_owner_checksum" ON "assets" ("ownerId", "checksum") WHERE ("libraryId" IS NULL)`.execute(db); + await sql`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`.execute(db); + await sql`CREATE INDEX "idx_asset_file_created_at" ON "assets" ("fileCreatedAt")`.execute(db); + await sql`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum")`.execute(db); + await sql`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`.execute(db); + await sql`CREATE INDEX "IDX_4d66e76dada1ca180f67a205dc" ON "assets" ("originalFileName")`.execute(db); + await sql`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`.execute(db); + await sql`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`.execute(db); + await sql`CREATE INDEX "IDX_assets_duplicateId" ON "assets" ("duplicateId")`.execute(db); + await sql`CREATE INDEX "IDX_assets_update_id" ON "assets" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`.execute(db); + await sql`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`.execute(db); + await sql`CREATE INDEX "IDX_albums_update_id" ON "albums" ("updateId")`.execute(db); + await sql`CREATE UNIQUE INDEX "IDX_activity_like" ON "activity" ("assetId", "userId", "albumId") WHERE ("isLiked" = true)`.execute(db); + await sql`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`.execute(db); + await sql`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`.execute(db); + await sql`CREATE INDEX "IDX_activity_update_id" ON "activity" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId")`.execute(db); + await sql`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId")`.execute(db); + await sql`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId")`.execute(db); + await sql`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId")`.execute(db); + await sql`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_api_keys_update_id" ON "api_keys" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_assets_audit_asset_id" ON "assets_audit" ("assetId")`.execute(db); + await sql`CREATE INDEX "IDX_assets_audit_owner_id" ON "assets_audit" ("ownerId")`.execute(db); + await sql`CREATE INDEX "IDX_assets_audit_deleted_at" ON "assets_audit" ("deletedAt")`.execute(db); + await sql`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`.execute(db); + await sql`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`.execute(db); + await sql`CREATE INDEX "IDX_person_update_id" ON "person" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_bf339a24070dac7e71304ec530" ON "asset_faces" ("personId", "assetId")`.execute(db); + await sql`CREATE INDEX "IDX_asset_faces_assetId_personId" ON "asset_faces" ("assetId", "personId")`.execute(db); + await sql`CREATE INDEX "IDX_02a43fd0b3c50fb6d7f0cb7282" ON "asset_faces" ("assetId")`.execute(db); + await sql`CREATE INDEX "IDX_95ad7106dd7b484275443f580f" ON "asset_faces" ("personId")`.execute(db); + await sql`CREATE INDEX "IDX_asset_files_assetId" ON "asset_files" ("assetId")`.execute(db); + await sql`CREATE INDEX "IDX_asset_files_update_id" ON "asset_files" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_ownerId_createdAt" ON "audit" ("ownerId", "createdAt")`.execute(db); + await sql`CREATE INDEX "exif_city" ON "exif" ("city")`.execute(db); + await sql`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID")`.execute(db); + await sql`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId")`.execute(db); + await sql`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId")`.execute(db); + await sql.raw(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })).execute(db); + await sql`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`.execute(db); + await sql`CREATE INDEX "idx_geodata_places_name" ON "geodata_places" USING gin (f_unaccent("name") gin_trgm_ops)`.execute(db); + await sql`CREATE INDEX "idx_geodata_places_admin2_name" ON "geodata_places" USING gin (f_unaccent("admin2Name") gin_trgm_ops)`.execute(db); + await sql`CREATE INDEX "idx_geodata_places_admin1_name" ON "geodata_places" USING gin (f_unaccent("admin1Name") gin_trgm_ops)`.execute(db); + await sql`CREATE INDEX "idx_geodata_places_alternate_names" ON "geodata_places" USING gin (f_unaccent("alternateNames") gin_trgm_ops)`.execute(db); + await sql`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`.execute(db); + await sql`CREATE INDEX "IDX_memories_update_id" ON "memories" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_984e5c9ab1f04d34538cd32334" ON "memories_assets_assets" ("memoriesId")`.execute(db); + await sql`CREATE INDEX "IDX_6942ecf52d75d4273de19d2c16" ON "memories_assets_assets" ("assetsId")`.execute(db); + await sql`CREATE INDEX "IDX_partners_audit_shared_by_id" ON "partners_audit" ("sharedById")`.execute(db); + await sql`CREATE INDEX "IDX_partners_audit_shared_with_id" ON "partners_audit" ("sharedWithId")`.execute(db); + await sql`CREATE INDEX "IDX_partners_audit_deleted_at" ON "partners_audit" ("deletedAt")`.execute(db); + await sql`CREATE INDEX "IDX_7e077a8b70b3530138610ff5e0" ON "partners" ("sharedById")`.execute(db); + await sql`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`.execute(db); + await sql`CREATE INDEX "IDX_partners_update_id" ON "partners" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_sessions_update_id" ON "sessions" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_sharedlink_key" ON "shared_links" ("key")`.execute(db); + await sql`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId")`.execute(db); + await sql`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId")`.execute(db); + await sql`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId")`.execute(db); + await sql.raw(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })).execute(db); + await sql`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`.execute(db); + await sql`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_92e67dc508c705dd66c9461557" ON "tags" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`.execute(db); + await sql`CREATE INDEX "IDX_tags_update_id" ON "tags" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" ON "tag_asset" ("assetsId", "tagsId")`.execute(db); + await sql`CREATE INDEX "IDX_f8e8a9e893cb5c54907f1b798e" ON "tag_asset" ("assetsId")`.execute(db); + await sql`CREATE INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4" ON "tag_asset" ("tagsId")`.execute(db); + await sql`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor")`.execute(db); + await sql`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant")`.execute(db); + await sql`CREATE INDEX "IDX_users_audit_deleted_at" ON "users_audit" ("deletedAt")`.execute(db); + await sql`CREATE INDEX "IDX_6afb43681a21cf7815932bc38a" ON "user_metadata" ("userId")`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "users_delete_audit" + AFTER DELETE ON "users" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION users_delete_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "users_updated_at" + BEFORE UPDATE ON "users" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "libraries_updated_at" + BEFORE UPDATE ON "libraries" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "assets_delete_audit" + AFTER DELETE ON "assets" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION assets_delete_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "assets_updated_at" + BEFORE UPDATE ON "assets" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "albums_updated_at" + BEFORE UPDATE ON "albums" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "activity_updated_at" + BEFORE UPDATE ON "activity" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "api_keys_updated_at" + BEFORE UPDATE ON "api_keys" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "person_updated_at" + BEFORE UPDATE ON "person" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_files_updated_at" + BEFORE UPDATE ON "asset_files" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_exif_updated_at" + BEFORE UPDATE ON "exif" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "memories_updated_at" + BEFORE UPDATE ON "memories" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "partners_delete_audit" + AFTER DELETE ON "partners" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION partners_delete_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "partners_updated_at" + BEFORE UPDATE ON "partners" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "sessions_updated_at" + BEFORE UPDATE ON "sessions" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "session_sync_checkpoints_updated_at" + BEFORE UPDATE ON "session_sync_checkpoints" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "tags_updated_at" + BEFORE UPDATE ON "tags" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); +} + +export async function down(): Promise { +// not implemented +} diff --git a/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts b/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts new file mode 100644 index 0000000000..28dca6658c --- /dev/null +++ b/server/src/schema/migrations/1744991379464-AddNotificationsTable.ts @@ -0,0 +1,22 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE TABLE "notifications" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" timestamp with time zone NOT NULL DEFAULT now(), "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), "deletedAt" timestamp with time zone, "updateId" uuid NOT NULL DEFAULT immich_uuid_v7(), "userId" uuid, "level" character varying NOT NULL DEFAULT 'info', "type" character varying NOT NULL DEFAULT 'info', "data" jsonb, "title" character varying NOT NULL, "description" text, "readAt" timestamp with time zone);`.execute(db); + await sql`ALTER TABLE "notifications" ADD CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "notifications" ADD CONSTRAINT "FK_692a909ee0fa9383e7859f9b406" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`CREATE INDEX "IDX_notifications_update_id" ON "notifications" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_692a909ee0fa9383e7859f9b40" ON "notifications" ("userId")`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "notifications_updated_at" + BEFORE UPDATE ON "notifications" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TRIGGER "notifications_updated_at" ON "notifications";`.execute(db); + await sql`DROP INDEX "IDX_notifications_update_id";`.execute(db); + await sql`DROP INDEX "IDX_692a909ee0fa9383e7859f9b40";`.execute(db); + await sql`ALTER TABLE "notifications" DROP CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a";`.execute(db); + await sql`ALTER TABLE "notifications" DROP CONSTRAINT "FK_692a909ee0fa9383e7859f9b406";`.execute(db); + await sql`DROP TABLE "notifications";`.execute(db); +} diff --git a/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts new file mode 100644 index 0000000000..5f3fdbedc8 --- /dev/null +++ b/server/src/schema/migrations/1745244781846-AddUserAvatarColorColumn.ts @@ -0,0 +1,14 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "users" ADD "avatarColor" character varying;`.execute(db); + await sql` + UPDATE "users" + SET "avatarColor" = "user_metadata"."value"->'avatar'->>'color' + FROM "user_metadata" + WHERE "users"."id" = "user_metadata"."userId" AND "user_metadata"."key" = 'preferences';`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "users" DROP COLUMN "avatarColor";`.execute(db); +} diff --git a/server/src/schema/migrations/1745902563899-AddAssetVisibilityColumn.ts b/server/src/schema/migrations/1745902563899-AddAssetVisibilityColumn.ts new file mode 100644 index 0000000000..6fe9dab1a0 --- /dev/null +++ b/server/src/schema/migrations/1745902563899-AddAssetVisibilityColumn.ts @@ -0,0 +1,37 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE TYPE "asset_visibility_enum" AS ENUM ('archive','timeline','hidden');`.execute(db); + await sql`ALTER TABLE "assets" + ADD "visibility" asset_visibility_enum NOT NULL DEFAULT 'timeline';`.execute(db); + + await sql` + UPDATE "assets" + SET "visibility" = CASE + WHEN "isArchived" THEN 'archive'::asset_visibility_enum + WHEN "isVisible" THEN 'timeline'::asset_visibility_enum + ELSE 'hidden'::asset_visibility_enum + END; + `.execute(db); + + await sql`ALTER TABLE "assets" DROP COLUMN "isVisible";`.execute(db); + await sql`ALTER TABLE "assets" DROP COLUMN "isArchived";`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "assets" ADD COLUMN "isArchived" BOOLEAN NOT NULL DEFAULT FALSE;`.execute(db); + await sql`ALTER TABLE "assets" ADD COLUMN "isVisible" BOOLEAN NOT NULL DEFAULT TRUE;`.execute(db); + + await sql` + UPDATE "assets" + SET + "isArchived" = ("visibility" = 'archive'::asset_visibility_enum), + "isVisible" = CASE + WHEN "visibility" = 'timeline'::asset_visibility_enum THEN TRUE + WHEN "visibility" = 'archive'::asset_visibility_enum THEN TRUE + ELSE FALSE + END; + `.execute(db); + await sql`ALTER TABLE "assets" DROP COLUMN "visibility";`.execute(db); + await sql`DROP TYPE "asset_visibility_enum";`.execute(db); +} diff --git a/server/src/schema/migrations/1746636476623-DropExtraIndexes.ts b/server/src/schema/migrations/1746636476623-DropExtraIndexes.ts new file mode 100644 index 0000000000..1518593428 --- /dev/null +++ b/server/src/schema/migrations/1746636476623-DropExtraIndexes.ts @@ -0,0 +1,29 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db`.execute(db); + const databaseName = rows[0].db; + await sql.raw(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`).execute(db); + const naturalearth_pkey = await sql<{ constraint_name: string }>`SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'naturalearth_countries' + AND constraint_type = 'PRIMARY KEY';`.execute(db); + const naturalearth_pkey_name = naturalearth_pkey.rows[0]?.constraint_name; + if(naturalearth_pkey_name) { + await sql`ALTER TABLE "naturalearth_countries" + DROP CONSTRAINT ${sql.ref(naturalearth_pkey_name)};`.execute(db); + } + await sql`ALTER TABLE "naturalearth_countries" ADD CONSTRAINT "naturalearth_countries_pkey" PRIMARY KEY ("id") WITH (FILLFACTOR = 100);`.execute(db); + await sql`DROP INDEX IF EXISTS "IDX_02a43fd0b3c50fb6d7f0cb7282";`.execute(db); + await sql`DROP INDEX IF EXISTS "IDX_95ad7106dd7b484275443f580f";`.execute(db); + await sql`DROP INDEX IF EXISTS "IDX_7e077a8b70b3530138610ff5e0";`.execute(db); + await sql`DROP INDEX IF EXISTS "IDX_92e67dc508c705dd66c9461557";`.execute(db); + await sql`DROP INDEX IF EXISTS "IDX_6afb43681a21cf7815932bc38a";`.execute(db); +} + +export async function down(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db`.execute(db); + const databaseName = rows[0].db; + await sql.raw(`ALTER DATABASE "${databaseName}" RESET "search_path"`).execute(db); +} diff --git a/server/src/schema/migrations/1746768490606-AddUserPincode.ts b/server/src/schema/migrations/1746768490606-AddUserPincode.ts new file mode 100644 index 0000000000..12dc3c2d12 --- /dev/null +++ b/server/src/schema/migrations/1746768490606-AddUserPincode.ts @@ -0,0 +1,9 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "users" ADD "pinCode" character varying;`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "users" DROP COLUMN "pinCode";`.execute(db); +} diff --git a/server/src/schema/migrations/1746844028242-AddLockedVisibilityEnum.ts b/server/src/schema/migrations/1746844028242-AddLockedVisibilityEnum.ts new file mode 100644 index 0000000000..9a344be66d --- /dev/null +++ b/server/src/schema/migrations/1746844028242-AddLockedVisibilityEnum.ts @@ -0,0 +1,9 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TYPE "asset_visibility_enum" ADD VALUE IF NOT EXISTS 'locked';`.execute(db); +} + +export async function down(): Promise { + // noop +} diff --git a/server/src/schema/migrations/1746987967923-AddPinExpiresAtColumn.ts b/server/src/schema/migrations/1746987967923-AddPinExpiresAtColumn.ts new file mode 100644 index 0000000000..b0f7d072d5 --- /dev/null +++ b/server/src/schema/migrations/1746987967923-AddPinExpiresAtColumn.ts @@ -0,0 +1,9 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "sessions" ADD "pinExpiresAt" timestamp with time zone;`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "sessions" DROP COLUMN "pinExpiresAt";`.execute(db); +} diff --git a/server/src/schema/migrations/1747329504572-AddNewSessionColumns.ts b/server/src/schema/migrations/1747329504572-AddNewSessionColumns.ts new file mode 100644 index 0000000000..d3cf8de173 --- /dev/null +++ b/server/src/schema/migrations/1747329504572-AddNewSessionColumns.ts @@ -0,0 +1,15 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "sessions" ADD "expiredAt" timestamp with time zone;`.execute(db); + await sql`ALTER TABLE "sessions" ADD "parentId" uuid;`.execute(db); + await sql`ALTER TABLE "sessions" ADD CONSTRAINT "FK_afbbabbd7daf5b91de4dca84de8" FOREIGN KEY ("parentId") REFERENCES "sessions" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db); + await sql`CREATE INDEX "IDX_afbbabbd7daf5b91de4dca84de" ON "sessions" ("parentId")`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP INDEX "IDX_afbbabbd7daf5b91de4dca84de";`.execute(db); + await sql`ALTER TABLE "sessions" DROP CONSTRAINT "FK_afbbabbd7daf5b91de4dca84de8";`.execute(db); + await sql`ALTER TABLE "sessions" DROP COLUMN "expiredAt";`.execute(db); + await sql`ALTER TABLE "sessions" DROP COLUMN "parentId";`.execute(db); +} diff --git a/server/src/schema/migrations/1747338664832-SessionRename.ts b/server/src/schema/migrations/1747338664832-SessionRename.ts new file mode 100644 index 0000000000..5ba532d136 --- /dev/null +++ b/server/src/schema/migrations/1747338664832-SessionRename.ts @@ -0,0 +1,9 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "sessions" RENAME "expiredAt" TO "expiresAt";`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "sessions" RENAME "expiresAt" TO "expiredAt";`.execute(db); +} diff --git a/server/src/schema/migrations/1747664684909-AddAlbumAuditTables.ts b/server/src/schema/migrations/1747664684909-AddAlbumAuditTables.ts new file mode 100644 index 0000000000..25ccfee710 --- /dev/null +++ b/server/src/schema/migrations/1747664684909-AddAlbumAuditTables.ts @@ -0,0 +1,96 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE OR REPLACE FUNCTION album_user_after_insert() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + UPDATE albums SET "updatedAt" = clock_timestamp(), "updateId" = immich_uuid_v7(clock_timestamp()) + WHERE "id" IN (SELECT DISTINCT "albumsId" FROM inserted_rows); + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION albums_delete_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO albums_audit ("albumId", "userId") + SELECT "id", "ownerId" + FROM OLD; + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE OR REPLACE FUNCTION album_users_delete_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO albums_audit ("albumId", "userId") + SELECT "albumsId", "usersId" + FROM OLD; + + IF pg_trigger_depth() = 1 THEN + INSERT INTO album_users_audit ("albumId", "userId") + SELECT "albumsId", "usersId" + FROM OLD; + END IF; + + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE TABLE "albums_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db); + await sql`CREATE TABLE "album_users_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db); + await sql`ALTER TABLE "albums_audit" ADD CONSTRAINT "PK_c75efea8d4dce316ad29b851a8b" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "album_users_audit" ADD CONSTRAINT "PK_f479a2e575b7ebc9698362c1688" PRIMARY KEY ("id");`.execute(db); + await sql`ALTER TABLE "albums_shared_users_users" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db); + await sql`ALTER TABLE "albums_shared_users_users" ADD "updatedAt" timestamp with time zone NOT NULL DEFAULT now();`.execute(db); + await sql`CREATE INDEX "IDX_album_users_update_id" ON "albums_shared_users_users" ("updateId")`.execute(db); + await sql`CREATE INDEX "IDX_albums_audit_album_id" ON "albums_audit" ("albumId")`.execute(db); + await sql`CREATE INDEX "IDX_albums_audit_user_id" ON "albums_audit" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_albums_audit_deleted_at" ON "albums_audit" ("deletedAt")`.execute(db); + await sql`CREATE INDEX "IDX_album_users_audit_album_id" ON "album_users_audit" ("albumId")`.execute(db); + await sql`CREATE INDEX "IDX_album_users_audit_user_id" ON "album_users_audit" ("userId")`.execute(db); + await sql`CREATE INDEX "IDX_album_users_audit_deleted_at" ON "album_users_audit" ("deletedAt")`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "albums_delete_audit" + AFTER DELETE ON "albums" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION albums_delete_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "album_users_delete_audit" + AFTER DELETE ON "albums_shared_users_users" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() <= 1) + EXECUTE FUNCTION album_users_delete_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "album_user_after_insert" + AFTER INSERT ON "albums_shared_users_users" + REFERENCING NEW TABLE AS "inserted_rows" + FOR EACH STATEMENT + EXECUTE FUNCTION album_user_after_insert();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "album_users_updated_at" + BEFORE UPDATE ON "albums_shared_users_users" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TRIGGER "albums_delete_audit" ON "albums";`.execute(db); + await sql`DROP TRIGGER "album_users_delete_audit" ON "albums_shared_users_users";`.execute(db); + await sql`DROP TRIGGER "album_user_after_insert" ON "albums_shared_users_users";`.execute(db); + await sql`DROP INDEX "IDX_albums_audit_album_id";`.execute(db); + await sql`DROP INDEX "IDX_albums_audit_user_id";`.execute(db); + await sql`DROP INDEX "IDX_albums_audit_deleted_at";`.execute(db); + await sql`DROP INDEX "IDX_album_users_audit_album_id";`.execute(db); + await sql`DROP INDEX "IDX_album_users_audit_user_id";`.execute(db); + await sql`DROP INDEX "IDX_album_users_audit_deleted_at";`.execute(db); + await sql`ALTER TABLE "albums_audit" DROP CONSTRAINT "PK_c75efea8d4dce316ad29b851a8b";`.execute(db); + await sql`ALTER TABLE "album_users_audit" DROP CONSTRAINT "PK_f479a2e575b7ebc9698362c1688";`.execute(db); + await sql`DROP TABLE "albums_audit";`.execute(db); + await sql`DROP TABLE "album_users_audit";`.execute(db); + await sql`DROP FUNCTION album_user_after_insert;`.execute(db); + await sql`DROP FUNCTION albums_delete_audit;`.execute(db); + await sql`DROP FUNCTION album_users_delete_audit;`.execute(db); +} diff --git a/server/src/schema/tables/activity.table.ts b/server/src/schema/tables/activity.table.ts index e7a144722c..802a86a303 100644 --- a/server/src/schema/tables/activity.table.ts +++ b/server/src/schema/tables/activity.table.ts @@ -5,7 +5,6 @@ import { UserTable } from 'src/schema/tables/user.table'; import { Check, Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, Index, @@ -51,7 +50,6 @@ export class ActivityTable { @Column({ type: 'boolean', default: false }) isLiked!: boolean; - @ColumnIndex('IDX_activity_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_activity_update_id' }) updateId!: string; } diff --git a/server/src/schema/tables/album-asset.table.ts b/server/src/schema/tables/album-asset.table.ts index 1b931e3116..8054009c39 100644 --- a/server/src/schema/tables/album-asset.table.ts +++ b/server/src/schema/tables/album-asset.table.ts @@ -1,25 +1,13 @@ import { AlbumTable } from 'src/schema/tables/album.table'; import { AssetTable } from 'src/schema/tables/asset.table'; -import { ColumnIndex, CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools'; @Table({ name: 'albums_assets_assets', primaryConstraintName: 'PK_c67bc36fa845fb7b18e0e398180' }) export class AlbumAssetTable { - @ForeignKeyColumn(() => AlbumTable, { - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - nullable: false, - primary: true, - }) - @ColumnIndex() + @ForeignKeyColumn(() => AlbumTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false, primary: true }) albumsId!: string; - @ForeignKeyColumn(() => AssetTable, { - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - nullable: false, - primary: true, - }) - @ColumnIndex() + @ForeignKeyColumn(() => AssetTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false, primary: true }) assetsId!: string; @CreateDateColumn() diff --git a/server/src/schema/tables/album-audit.table.ts b/server/src/schema/tables/album-audit.table.ts new file mode 100644 index 0000000000..66b70654e9 --- /dev/null +++ b/server/src/schema/tables/album-audit.table.ts @@ -0,0 +1,17 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { Column, CreateDateColumn, Table } from 'src/sql-tools'; + +@Table('albums_audit') +export class AlbumAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: string; + + @Column({ type: 'uuid', indexName: 'IDX_albums_audit_album_id' }) + albumId!: string; + + @Column({ type: 'uuid', indexName: 'IDX_albums_audit_user_id' }) + userId!: string; + + @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_albums_audit_deleted_at' }) + deletedAt!: Date; +} diff --git a/server/src/schema/tables/album-user-audit.table.ts b/server/src/schema/tables/album-user-audit.table.ts new file mode 100644 index 0000000000..46ad6b682b --- /dev/null +++ b/server/src/schema/tables/album-user-audit.table.ts @@ -0,0 +1,17 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { Column, CreateDateColumn, Table } from 'src/sql-tools'; + +@Table('album_users_audit') +export class AlbumUserAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: string; + + @Column({ type: 'uuid', indexName: 'IDX_album_users_audit_album_id' }) + albumId!: string; + + @Column({ type: 'uuid', indexName: 'IDX_album_users_audit_user_id' }) + userId!: string; + + @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_album_users_audit_deleted_at' }) + deletedAt!: Date; +} diff --git a/server/src/schema/tables/album-user.table.ts b/server/src/schema/tables/album-user.table.ts index 8bd05df2ee..276efd126a 100644 --- a/server/src/schema/tables/album-user.table.ts +++ b/server/src/schema/tables/album-user.table.ts @@ -1,12 +1,36 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { AlbumUserRole } from 'src/enum'; +import { album_user_after_insert, album_users_delete_audit } from 'src/schema/functions'; import { AlbumTable } from 'src/schema/tables/album.table'; import { UserTable } from 'src/schema/tables/user.table'; -import { Column, ForeignKeyColumn, Index, Table } from 'src/sql-tools'; +import { + AfterDeleteTrigger, + AfterInsertTrigger, + Column, + ForeignKeyColumn, + Index, + Table, + UpdateDateColumn, +} from 'src/sql-tools'; @Table({ name: 'albums_shared_users_users', primaryConstraintName: 'PK_7df55657e0b2e8b626330a0ebc8' }) // Pre-existing indices from original album <--> user ManyToMany mapping @Index({ name: 'IDX_427c350ad49bd3935a50baab73', columns: ['albumsId'] }) @Index({ name: 'IDX_f48513bf9bccefd6ff3ad30bd0', columns: ['usersId'] }) +@UpdatedAtTrigger('album_users_updated_at') +@AfterInsertTrigger({ + name: 'album_user_after_insert', + scope: 'statement', + referencingNewTableAs: 'inserted_rows', + function: album_user_after_insert, +}) +@AfterDeleteTrigger({ + name: 'album_users_delete_audit', + scope: 'statement', + function: album_users_delete_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() <= 1', +}) export class AlbumUserTable { @ForeignKeyColumn(() => AlbumTable, { onDelete: 'CASCADE', @@ -26,4 +50,10 @@ export class AlbumUserTable { @Column({ type: 'character varying', default: AlbumUserRole.EDITOR }) role!: AlbumUserRole; + + @UpdateIdColumn({ indexName: 'IDX_album_users_update_id' }) + updateId?: string; + + @UpdateDateColumn() + updatedAt!: Date; } diff --git a/server/src/schema/tables/album.table.ts b/server/src/schema/tables/album.table.ts index cdfd092b1b..5d02cc9f25 100644 --- a/server/src/schema/tables/album.table.ts +++ b/server/src/schema/tables/album.table.ts @@ -1,10 +1,11 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { AssetOrder } from 'src/enum'; +import { albums_delete_audit } from 'src/schema/functions'; import { AssetTable } from 'src/schema/tables/asset.table'; import { UserTable } from 'src/schema/tables/user.table'; import { + AfterDeleteTrigger, Column, - ColumnIndex, CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, @@ -15,6 +16,13 @@ import { @Table({ name: 'albums', primaryConstraintName: 'PK_7f71c7b5bc7c87b8f94c9a93a00' }) @UpdatedAtTrigger('albums_updated_at') +@AfterDeleteTrigger({ + name: 'albums_delete_audit', + scope: 'statement', + function: albums_delete_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() = 0', +}) export class AlbumTable { @PrimaryGeneratedColumn() id!: string; @@ -51,7 +59,6 @@ export class AlbumTable { @Column({ default: AssetOrder.DESC }) order!: AssetOrder; - @ColumnIndex('IDX_albums_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_albums_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/api-key.table.ts b/server/src/schema/tables/api-key.table.ts index 29c4ad2b0f..1d4cc83172 100644 --- a/server/src/schema/tables/api-key.table.ts +++ b/server/src/schema/tables/api-key.table.ts @@ -3,7 +3,6 @@ import { Permission } from 'src/enum'; import { UserTable } from 'src/schema/tables/user.table'; import { Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, @@ -35,7 +34,6 @@ export class APIKeyTable { @Column({ array: true, type: 'character varying' }) permissions!: Permission[]; - @ColumnIndex({ name: 'IDX_api_keys_update_id' }) - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_api_keys_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/asset-audit.table.ts b/server/src/schema/tables/asset-audit.table.ts index 55d6f5c911..030256480c 100644 --- a/server/src/schema/tables/asset-audit.table.ts +++ b/server/src/schema/tables/asset-audit.table.ts @@ -1,20 +1,17 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, ColumnIndex, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Table } from 'src/sql-tools'; @Table('assets_audit') export class AssetAuditTable { @PrimaryGeneratedUuidV7Column() id!: string; - @ColumnIndex('IDX_assets_audit_asset_id') - @Column({ type: 'uuid' }) + @Column({ type: 'uuid', indexName: 'IDX_assets_audit_asset_id' }) assetId!: string; - @ColumnIndex('IDX_assets_audit_owner_id') - @Column({ type: 'uuid' }) + @Column({ type: 'uuid', indexName: 'IDX_assets_audit_owner_id' }) ownerId!: string; - @ColumnIndex('IDX_assets_audit_deleted_at') - @CreateDateColumn({ default: () => 'clock_timestamp()' }) + @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_assets_audit_deleted_at' }) deletedAt!: Date; } diff --git a/server/src/schema/tables/asset-face.table.ts b/server/src/schema/tables/asset-face.table.ts index 0ae99f44bf..52f4364a93 100644 --- a/server/src/schema/tables/asset-face.table.ts +++ b/server/src/schema/tables/asset-face.table.ts @@ -8,10 +8,21 @@ import { Column, DeleteDateColumn, ForeignKeyColumn, Index, PrimaryGeneratedColu @Index({ name: 'IDX_asset_faces_assetId_personId', columns: ['assetId', 'personId'] }) @Index({ columns: ['personId', 'assetId'] }) export class AssetFaceTable { - @ForeignKeyColumn(() => AssetTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) + @ForeignKeyColumn(() => AssetTable, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + // [assetId, personId] is the PK constraint + index: false, + }) assetId!: string; - @ForeignKeyColumn(() => PersonTable, { onDelete: 'SET NULL', onUpdate: 'CASCADE', nullable: true }) + @ForeignKeyColumn(() => PersonTable, { + onDelete: 'SET NULL', + onUpdate: 'CASCADE', + nullable: true, + // [personId, assetId] makes this redundant + index: false, + }) personId!: string | null; @Column({ default: 0, type: 'integer' }) diff --git a/server/src/schema/tables/asset-files.table.ts b/server/src/schema/tables/asset-files.table.ts index fb8750a8ef..0859bd5cf0 100644 --- a/server/src/schema/tables/asset-files.table.ts +++ b/server/src/schema/tables/asset-files.table.ts @@ -3,7 +3,6 @@ import { AssetFileType } from 'src/enum'; import { AssetTable } from 'src/schema/tables/asset.table'; import { Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, @@ -19,8 +18,11 @@ export class AssetFileTable { @PrimaryGeneratedColumn() id!: string; - @ColumnIndex('IDX_asset_files_assetId') - @ForeignKeyColumn(() => AssetTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) + @ForeignKeyColumn(() => AssetTable, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + indexName: 'IDX_asset_files_assetId', + }) assetId?: string; @CreateDateColumn() @@ -35,7 +37,6 @@ export class AssetFileTable { @Column() path!: string; - @ColumnIndex('IDX_asset_files_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_asset_files_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/asset.table.ts b/server/src/schema/tables/asset.table.ts index 250c3546a2..d337984a46 100644 --- a/server/src/schema/tables/asset.table.ts +++ b/server/src/schema/tables/asset.table.ts @@ -1,7 +1,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; -import { ASSET_CHECKSUM_CONSTRAINT } from 'src/entities/asset.entity'; -import { AssetStatus, AssetType } from 'src/enum'; -import { assets_status_enum } from 'src/schema/enums'; +import { AssetStatus, AssetType, AssetVisibility } from 'src/enum'; +import { asset_visibility_enum, assets_status_enum } from 'src/schema/enums'; import { assets_delete_audit } from 'src/schema/functions'; import { LibraryTable } from 'src/schema/tables/library.table'; import { StackTable } from 'src/schema/tables/stack.table'; @@ -9,7 +8,6 @@ import { UserTable } from 'src/schema/tables/user.table'; import { AfterDeleteTrigger, Column, - ColumnIndex, CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, @@ -18,6 +16,7 @@ import { Table, UpdateDateColumn, } from 'src/sql-tools'; +import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; @Table('assets') @UpdatedAtTrigger('assets_updated_at') @@ -78,8 +77,7 @@ export class AssetTable { @Column() originalPath!: string; - @ColumnIndex('idx_asset_file_created_at') - @Column({ type: 'timestamp with time zone' }) + @Column({ type: 'timestamp with time zone', indexName: 'idx_asset_file_created_at' }) fileCreatedAt!: Date; @Column({ type: 'timestamp with time zone' }) @@ -94,13 +92,9 @@ export class AssetTable { @Column({ type: 'character varying', nullable: true, default: '' }) encodedVideoPath!: string | null; - @Column({ type: 'bytea' }) - @ColumnIndex() + @Column({ type: 'bytea', index: true }) checksum!: Buffer; // sha1 checksum - @Column({ type: 'boolean', default: true }) - isVisible!: boolean; - @ForeignKeyColumn(() => AssetTable, { nullable: true, onUpdate: 'CASCADE', onDelete: 'SET NULL' }) livePhotoVideoId!: string | null; @@ -110,11 +104,7 @@ export class AssetTable { @CreateDateColumn() createdAt!: Date; - @Column({ type: 'boolean', default: false }) - isArchived!: boolean; - - @Column() - @ColumnIndex() + @Column({ index: true }) originalFileName!: string; @Column({ nullable: true }) @@ -141,14 +131,15 @@ export class AssetTable { @ForeignKeyColumn(() => StackTable, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' }) stackId?: string | null; - @ColumnIndex('IDX_assets_duplicateId') - @Column({ type: 'uuid', nullable: true }) + @Column({ type: 'uuid', nullable: true, indexName: 'IDX_assets_duplicateId' }) duplicateId!: string | null; @Column({ enum: assets_status_enum, default: AssetStatus.ACTIVE }) status!: AssetStatus; - @ColumnIndex('IDX_assets_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_assets_update_id' }) updateId?: string; + + @Column({ enum: asset_visibility_enum, default: AssetVisibility.TIMELINE }) + visibility!: AssetVisibility; } diff --git a/server/src/schema/tables/exif.table.ts b/server/src/schema/tables/exif.table.ts index e40ce94b4f..ca300945c3 100644 --- a/server/src/schema/tables/exif.table.ts +++ b/server/src/schema/tables/exif.table.ts @@ -1,6 +1,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { AssetTable } from 'src/schema/tables/asset.table'; -import { Column, ColumnIndex, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; +import { Column, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; @Table('exif') @UpdatedAtTrigger('asset_exif_updated_at') @@ -50,8 +50,7 @@ export class ExifTable { @Column({ type: 'double precision', nullable: true }) longitude!: number | null; - @ColumnIndex('exif_city') - @Column({ type: 'character varying', nullable: true }) + @Column({ type: 'character varying', nullable: true, indexName: 'exif_city' }) city!: string | null; @Column({ type: 'character varying', nullable: true }) @@ -69,8 +68,7 @@ export class ExifTable { @Column({ type: 'character varying', nullable: true }) exposureTime!: string | null; - @ColumnIndex('IDX_live_photo_cid') - @Column({ type: 'character varying', nullable: true }) + @Column({ type: 'character varying', nullable: true, indexName: 'IDX_live_photo_cid' }) livePhotoCID!: string | null; @Column({ type: 'character varying', nullable: true }) @@ -88,8 +86,7 @@ export class ExifTable { @Column({ type: 'integer', nullable: true }) bitsPerSample!: number | null; - @ColumnIndex('IDX_auto_stack_id') - @Column({ type: 'character varying', nullable: true }) + @Column({ type: 'character varying', nullable: true, indexName: 'IDX_auto_stack_id' }) autoStackId!: string | null; @Column({ type: 'integer', nullable: true }) @@ -98,7 +95,6 @@ export class ExifTable { @UpdateDateColumn({ default: () => 'clock_timestamp()' }) updatedAt?: Date; - @ColumnIndex('IDX_asset_exif_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_asset_exif_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/geodata-places.table.ts b/server/src/schema/tables/geodata-places.table.ts index 631cfdff08..3e78b4cfcf 100644 --- a/server/src/schema/tables/geodata-places.table.ts +++ b/server/src/schema/tables/geodata-places.table.ts @@ -1,6 +1,6 @@ import { Column, Index, PrimaryColumn, Table } from 'src/sql-tools'; -@Table({ name: 'geodata_places' }) +@Table({ name: 'geodata_places', synchronize: false }) @Index({ name: 'idx_geodata_places_alternate_names', using: 'gin', @@ -26,11 +26,10 @@ import { Column, Index, PrimaryColumn, Table } from 'src/sql-tools'; synchronize: false, }) @Index({ - name: 'idx_geodata_places_gist_earthcoord', + name: 'IDX_geodata_gist_earthcoord', expression: 'll_to_earth_public(latitude, longitude)', synchronize: false, }) -@Table({ name: 'idx_geodata_places', synchronize: false }) export class GeodataPlacesTable { @PrimaryColumn({ type: 'integer' }) id!: number; diff --git a/server/src/schema/tables/library.table.ts b/server/src/schema/tables/library.table.ts index 54b3752f41..8b21d5feb0 100644 --- a/server/src/schema/tables/library.table.ts +++ b/server/src/schema/tables/library.table.ts @@ -2,7 +2,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UserTable } from 'src/schema/tables/user.table'; import { Column, - ColumnIndex, CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, @@ -41,7 +40,6 @@ export class LibraryTable { @Column({ type: 'timestamp with time zone', nullable: true }) refreshedAt!: Date | null; - @ColumnIndex('IDX_libraries_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_libraries_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/memory.table.ts b/server/src/schema/tables/memory.table.ts index 1926405565..32dafe3384 100644 --- a/server/src/schema/tables/memory.table.ts +++ b/server/src/schema/tables/memory.table.ts @@ -3,7 +3,6 @@ import { MemoryType } from 'src/enum'; import { UserTable } from 'src/schema/tables/user.table'; import { Column, - ColumnIndex, CreateDateColumn, DeleteDateColumn, ForeignKeyColumn, @@ -55,7 +54,6 @@ export class MemoryTable { @Column({ type: 'timestamp with time zone', nullable: true }) hideAt?: Date; - @ColumnIndex('IDX_memories_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_memories_update_id' }) updateId?: string; } diff --git a/server/src/schema/tables/memory_asset.table.ts b/server/src/schema/tables/memory_asset.table.ts index 864e6291c7..0e5ca29a08 100644 --- a/server/src/schema/tables/memory_asset.table.ts +++ b/server/src/schema/tables/memory_asset.table.ts @@ -1,14 +1,12 @@ import { AssetTable } from 'src/schema/tables/asset.table'; import { MemoryTable } from 'src/schema/tables/memory.table'; -import { ColumnIndex, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { ForeignKeyColumn, Table } from 'src/sql-tools'; @Table('memories_assets_assets') export class MemoryAssetTable { - @ColumnIndex() @ForeignKeyColumn(() => MemoryTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) memoriesId!: string; - @ColumnIndex() @ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) assetsId!: string; } diff --git a/server/src/schema/tables/natural-earth-countries.table.ts b/server/src/schema/tables/natural-earth-countries.table.ts index df1132d17d..e5e6ead772 100644 --- a/server/src/schema/tables/natural-earth-countries.table.ts +++ b/server/src/schema/tables/natural-earth-countries.table.ts @@ -1,6 +1,6 @@ import { Column, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; -@Table({ name: 'naturalearth_countries' }) +@Table({ name: 'naturalearth_countries', primaryConstraintName: 'naturalearth_countries_pkey' }) export class NaturalEarthCountriesTable { @PrimaryGeneratedColumn({ strategy: 'identity' }) id!: number; diff --git a/server/src/schema/tables/notification.table.ts b/server/src/schema/tables/notification.table.ts new file mode 100644 index 0000000000..bf9b8bdf3b --- /dev/null +++ b/server/src/schema/tables/notification.table.ts @@ -0,0 +1,52 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { UserTable } from 'src/schema/tables/user.table'; +import { + Column, + CreateDateColumn, + DeleteDateColumn, + ForeignKeyColumn, + PrimaryGeneratedColumn, + Table, + UpdateDateColumn, +} from 'src/sql-tools'; + +@Table('notifications') +@UpdatedAtTrigger('notifications_updated_at') +export class NotificationTable { + @PrimaryGeneratedColumn() + id!: string; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; + + @DeleteDateColumn() + deletedAt?: Date; + + @UpdateIdColumn({ indexName: 'IDX_notifications_update_id' }) + updateId?: string; + + @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) + userId!: string; + + @Column({ default: NotificationLevel.Info }) + level!: NotificationLevel; + + @Column({ default: NotificationLevel.Info }) + type!: NotificationType; + + @Column({ type: 'jsonb', nullable: true }) + data!: any | null; + + @Column() + title!: string; + + @Column({ type: 'text', nullable: true }) + description!: string; + + @Column({ type: 'timestamp with time zone', nullable: true }) + readAt?: Date | null; +} diff --git a/server/src/schema/tables/partner-audit.table.ts b/server/src/schema/tables/partner-audit.table.ts index 08b6e94626..da5243dc75 100644 --- a/server/src/schema/tables/partner-audit.table.ts +++ b/server/src/schema/tables/partner-audit.table.ts @@ -1,20 +1,17 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, ColumnIndex, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Table } from 'src/sql-tools'; @Table('partners_audit') export class PartnerAuditTable { @PrimaryGeneratedUuidV7Column() id!: string; - @ColumnIndex('IDX_partners_audit_shared_by_id') - @Column({ type: 'uuid' }) + @Column({ type: 'uuid', indexName: 'IDX_partners_audit_shared_by_id' }) sharedById!: string; - @ColumnIndex('IDX_partners_audit_shared_with_id') - @Column({ type: 'uuid' }) + @Column({ type: 'uuid', indexName: 'IDX_partners_audit_shared_with_id' }) sharedWithId!: string; - @ColumnIndex('IDX_partners_audit_deleted_at') - @CreateDateColumn({ default: () => 'clock_timestamp()' }) + @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_partners_audit_deleted_at' }) deletedAt!: Date; } diff --git a/server/src/schema/tables/partner.table.ts b/server/src/schema/tables/partner.table.ts index 770107fe7a..0da60cfc0c 100644 --- a/server/src/schema/tables/partner.table.ts +++ b/server/src/schema/tables/partner.table.ts @@ -1,15 +1,7 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { partners_delete_audit } from 'src/schema/functions'; import { UserTable } from 'src/schema/tables/user.table'; -import { - AfterDeleteTrigger, - Column, - ColumnIndex, - CreateDateColumn, - ForeignKeyColumn, - Table, - UpdateDateColumn, -} from 'src/sql-tools'; +import { AfterDeleteTrigger, Column, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools'; @Table('partners') @UpdatedAtTrigger('partners_updated_at') @@ -21,7 +13,12 @@ import { when: 'pg_trigger_depth() = 0', }) export class PartnerTable { - @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', primary: true }) + @ForeignKeyColumn(() => UserTable, { + onDelete: 'CASCADE', + primary: true, + // [sharedById, sharedWithId] is the PK constraint + index: false, + }) sharedById!: string; @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', primary: true }) @@ -36,7 +33,6 @@ export class PartnerTable { @Column({ type: 'boolean', default: false }) inTimeline!: boolean; - @ColumnIndex('IDX_partners_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_partners_update_id' }) updateId!: string; } diff --git a/server/src/schema/tables/person.table.ts b/server/src/schema/tables/person.table.ts index b96fc5b709..1320b91f18 100644 --- a/server/src/schema/tables/person.table.ts +++ b/server/src/schema/tables/person.table.ts @@ -4,7 +4,6 @@ import { UserTable } from 'src/schema/tables/user.table'; import { Check, Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, @@ -49,7 +48,6 @@ export class PersonTable { @Column({ type: 'character varying', nullable: true, default: null }) color?: string | null; - @ColumnIndex('IDX_person_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_person_update_id' }) updateId!: string; } diff --git a/server/src/schema/tables/session.table.ts b/server/src/schema/tables/session.table.ts index a66732a7d9..6bd5d84cb2 100644 --- a/server/src/schema/tables/session.table.ts +++ b/server/src/schema/tables/session.table.ts @@ -2,7 +2,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UserTable } from 'src/schema/tables/user.table'; import { Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, @@ -26,16 +25,24 @@ export class SessionTable { @UpdateDateColumn() updatedAt!: Date; + @Column({ type: 'timestamp with time zone', nullable: true }) + expiresAt!: Date | null; + @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) userId!: string; + @ForeignKeyColumn(() => SessionTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', nullable: true }) + parentId!: string | null; + @Column({ default: '' }) deviceType!: string; @Column({ default: '' }) deviceOS!: string; - @ColumnIndex('IDX_sessions_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_sessions_update_id' }) updateId!: string; + + @Column({ type: 'timestamp with time zone', nullable: true }) + pinExpiresAt!: Date | null; } diff --git a/server/src/schema/tables/shared-link-asset.table.ts b/server/src/schema/tables/shared-link-asset.table.ts index 1eb294c1e8..66c9068441 100644 --- a/server/src/schema/tables/shared-link-asset.table.ts +++ b/server/src/schema/tables/shared-link-asset.table.ts @@ -1,14 +1,12 @@ import { AssetTable } from 'src/schema/tables/asset.table'; import { SharedLinkTable } from 'src/schema/tables/shared-link.table'; -import { ColumnIndex, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { ForeignKeyColumn, Table } from 'src/sql-tools'; @Table('shared_link__asset') export class SharedLinkAssetTable { - @ColumnIndex() @ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) assetsId!: string; - @ColumnIndex() @ForeignKeyColumn(() => SharedLinkTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) sharedLinksId!: string; } diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 36237c58ef..39693f3893 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -1,15 +1,7 @@ import { SharedLinkType } from 'src/enum'; import { AlbumTable } from 'src/schema/tables/album.table'; import { UserTable } from 'src/schema/tables/user.table'; -import { - Column, - ColumnIndex, - CreateDateColumn, - ForeignKeyColumn, - PrimaryGeneratedColumn, - Table, - Unique, -} from 'src/sql-tools'; +import { Column, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools'; @Table('shared_links') @Unique({ name: 'UQ_sharedlink_key', columns: ['key'] }) @@ -23,9 +15,8 @@ export class SharedLinkTable { @ForeignKeyColumn(() => UserTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' }) userId!: string; - @ColumnIndex('IDX_sharedlink_key') - @Column({ type: 'bytea' }) - key!: Buffer; // use to access the inidividual asset + @Column({ type: 'bytea', indexName: 'IDX_sharedlink_key' }) + key!: Buffer; // use to access the individual asset @Column() type!: SharedLinkType; @@ -39,8 +30,12 @@ export class SharedLinkTable { @Column({ type: 'boolean', default: false }) allowUpload!: boolean; - @ColumnIndex('IDX_sharedlink_albumId') - @ForeignKeyColumn(() => AlbumTable, { nullable: true, onDelete: 'CASCADE', onUpdate: 'CASCADE' }) + @ForeignKeyColumn(() => AlbumTable, { + nullable: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + indexName: 'IDX_sharedlink_albumId', + }) albumId!: string; @Column({ type: 'boolean', default: true }) diff --git a/server/src/schema/tables/sync-checkpoint.table.ts b/server/src/schema/tables/sync-checkpoint.table.ts index 831205ce7a..21fd7983ac 100644 --- a/server/src/schema/tables/sync-checkpoint.table.ts +++ b/server/src/schema/tables/sync-checkpoint.table.ts @@ -1,15 +1,7 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; import { SessionTable } from 'src/schema/tables/session.table'; -import { - Column, - ColumnIndex, - CreateDateColumn, - ForeignKeyColumn, - PrimaryColumn, - Table, - UpdateDateColumn, -} from 'src/sql-tools'; +import { Column, CreateDateColumn, ForeignKeyColumn, PrimaryColumn, Table, UpdateDateColumn } from 'src/sql-tools'; @Table('session_sync_checkpoints') @UpdatedAtTrigger('session_sync_checkpoints_updated_at') @@ -29,7 +21,6 @@ export class SessionSyncCheckpointTable { @Column() ack!: string; - @ColumnIndex('IDX_session_sync_checkpoints_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_session_sync_checkpoints_update_id' }) updateId!: string; } diff --git a/server/src/schema/tables/tag-asset.table.ts b/server/src/schema/tables/tag-asset.table.ts index 5f24799cec..8793af0a8a 100644 --- a/server/src/schema/tables/tag-asset.table.ts +++ b/server/src/schema/tables/tag-asset.table.ts @@ -1,15 +1,13 @@ import { AssetTable } from 'src/schema/tables/asset.table'; import { TagTable } from 'src/schema/tables/tag.table'; -import { ColumnIndex, ForeignKeyColumn, Index, Table } from 'src/sql-tools'; +import { ForeignKeyColumn, Index, Table } from 'src/sql-tools'; @Index({ name: 'IDX_tag_asset_assetsId_tagsId', columns: ['assetsId', 'tagsId'] }) @Table('tag_asset') export class TagAssetTable { - @ColumnIndex() - @ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) + @ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true, index: true }) assetsId!: string; - @ColumnIndex() - @ForeignKeyColumn(() => TagTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) + @ForeignKeyColumn(() => TagTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true, index: true }) tagsId!: string; } diff --git a/server/src/schema/tables/tag-closure.table.ts b/server/src/schema/tables/tag-closure.table.ts index acde84b91d..8829e802e1 100644 --- a/server/src/schema/tables/tag-closure.table.ts +++ b/server/src/schema/tables/tag-closure.table.ts @@ -1,13 +1,11 @@ import { TagTable } from 'src/schema/tables/tag.table'; -import { ColumnIndex, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { ForeignKeyColumn, Table } from 'src/sql-tools'; @Table('tags_closure') export class TagClosureTable { - @ColumnIndex() - @ForeignKeyColumn(() => TagTable, { primary: true, onDelete: 'CASCADE', onUpdate: 'NO ACTION' }) + @ForeignKeyColumn(() => TagTable, { primary: true, onDelete: 'CASCADE', onUpdate: 'NO ACTION', index: true }) id_ancestor!: string; - @ColumnIndex() - @ForeignKeyColumn(() => TagTable, { primary: true, onDelete: 'CASCADE', onUpdate: 'NO ACTION' }) + @ForeignKeyColumn(() => TagTable, { primary: true, onDelete: 'CASCADE', onUpdate: 'NO ACTION', index: true }) id_descendant!: string; } diff --git a/server/src/schema/tables/tag.table.ts b/server/src/schema/tables/tag.table.ts index 5042e2eb0e..a9f2a57f27 100644 --- a/server/src/schema/tables/tag.table.ts +++ b/server/src/schema/tables/tag.table.ts @@ -2,7 +2,6 @@ import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { UserTable } from 'src/schema/tables/user.table'; import { Column, - ColumnIndex, CreateDateColumn, ForeignKeyColumn, PrimaryGeneratedColumn, @@ -18,7 +17,12 @@ export class TagTable { @PrimaryGeneratedColumn() id!: string; - @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE' }) + @ForeignKeyColumn(() => UserTable, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + // [userId, value] makes this redundant + index: false, + }) userId!: string; @Column() @@ -36,7 +40,6 @@ export class TagTable { @ForeignKeyColumn(() => TagTable, { nullable: true, onDelete: 'CASCADE' }) parentId?: string; - @ColumnIndex('IDX_tags_update_id') - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_tags_update_id' }) updateId!: string; } diff --git a/server/src/schema/tables/user-audit.table.ts b/server/src/schema/tables/user-audit.table.ts index 0f881ccc9a..e0c9afcdc3 100644 --- a/server/src/schema/tables/user-audit.table.ts +++ b/server/src/schema/tables/user-audit.table.ts @@ -1,13 +1,12 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; -import { Column, ColumnIndex, CreateDateColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, Table } from 'src/sql-tools'; @Table('users_audit') export class UserAuditTable { @Column({ type: 'uuid' }) userId!: string; - @ColumnIndex('IDX_users_audit_deleted_at') - @CreateDateColumn({ default: () => 'clock_timestamp()' }) + @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_users_audit_deleted_at' }) deletedAt!: Date; @PrimaryGeneratedUuidV7Column() diff --git a/server/src/schema/tables/user-metadata.table.ts b/server/src/schema/tables/user-metadata.table.ts index 6d03acaf80..04b457867f 100644 --- a/server/src/schema/tables/user-metadata.table.ts +++ b/server/src/schema/tables/user-metadata.table.ts @@ -5,7 +5,13 @@ import { UserMetadata, UserMetadataItem } from 'src/types'; @Table('user_metadata') export class UserMetadataTable implements UserMetadataItem { - @ForeignKeyColumn(() => UserTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true }) + @ForeignKeyColumn(() => UserTable, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + primary: true, + // [userId, key] is the PK constraint + index: false, + }) userId!: string; @PrimaryColumn({ type: 'character varying' }) diff --git a/server/src/schema/tables/user.table.ts b/server/src/schema/tables/user.table.ts index 5160f979b9..c806d6e3f7 100644 --- a/server/src/schema/tables/user.table.ts +++ b/server/src/schema/tables/user.table.ts @@ -1,11 +1,10 @@ import { ColumnType } from 'kysely'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; -import { UserStatus } from 'src/enum'; +import { UserAvatarColor, UserStatus } from 'src/enum'; import { users_delete_audit } from 'src/schema/functions'; import { AfterDeleteTrigger, Column, - ColumnIndex, CreateDateColumn, DeleteDateColumn, Index, @@ -38,6 +37,9 @@ export class UserTable { @Column({ default: '' }) password!: Generated; + @Column({ nullable: true }) + pinCode!: string | null; + @CreateDateColumn() createdAt!: Generated; @@ -50,6 +52,9 @@ export class UserTable { @Column({ type: 'boolean', default: true }) shouldChangePassword!: Generated; + @Column({ default: null }) + avatarColor!: UserAvatarColor | null; + @DeleteDateColumn() deletedAt!: Timestamp | null; @@ -77,7 +82,6 @@ export class UserTable { @Column({ type: 'timestamp with time zone', default: () => 'now()' }) profileChangedAt!: Generated; - @ColumnIndex({ name: 'IDX_users_update_id' }) - @UpdateIdColumn() + @UpdateIdColumn({ indexName: 'IDX_users_update_id' }) updateId!: Generated; } diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index a0fbfc0817..c2b792d091 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -163,7 +163,7 @@ describe(AlbumService.name, () => { ); expect(mocks.user.get).toHaveBeenCalledWith('user-id', {}); - expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123'])); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']), false); expect(mocks.event.emit).toHaveBeenCalledWith('album.invite', { id: albumStub.empty.id, userId: 'user-id', @@ -207,6 +207,7 @@ describe(AlbumService.name, () => { expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( authStub.admin.user.id, new Set(['asset-1', 'asset-2']), + false, ); }); }); @@ -606,7 +607,7 @@ describe(AlbumService.name, () => { expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); expect(mocks.event.emit).toHaveBeenCalledWith('album.update', { id: 'album-123', - recipientIds: ['admin_id'], + recipientId: 'admin_id', }); }); @@ -688,7 +689,11 @@ describe(AlbumService.name, () => { { success: false, id: 'asset-1', error: BulkIdErrorReason.NO_PERMISSION }, ]); - expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['asset-1'])); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1']), + false, + ); expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['asset-1'])); }); diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index eac000005b..d4e6ab7ffd 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -6,15 +6,15 @@ import { AlbumStatisticsResponseDto, CreateAlbumDto, GetAlbumsDto, - UpdateAlbumDto, - UpdateAlbumUserDto, mapAlbum, + MapAlbumDto, mapAlbumWithAssets, mapAlbumWithoutAssets, + UpdateAlbumDto, + UpdateAlbumUserDto, } from 'src/dtos/album.dto'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AlbumEntity } from 'src/entities/album.entity'; import { Permission } from 'src/enum'; import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository'; import { BaseService } from 'src/services/base.service'; @@ -39,7 +39,7 @@ export class AlbumService extends BaseService { async getAll({ user: { id: ownerId } }: AuthDto, { assetId, shared }: GetAlbumsDto): Promise { await this.albumRepository.updateThumbnails(); - let albums: AlbumEntity[]; + let albums: MapAlbumDto[]; if (assetId) { albums = await this.albumRepository.getByAssetId(ownerId, assetId); } else if (shared === true) { @@ -170,8 +170,8 @@ export class AlbumService extends BaseService { (userId) => userId !== auth.user.id, ); - if (allUsersExceptUs.length > 0) { - await this.eventRepository.emit('album.update', { id, recipientIds: allUsersExceptUs }); + for (const recipientId of allUsersExceptUs) { + await this.eventRepository.emit('album.update', { id, recipientId }); } } diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index 680cd38f1e..3448b4330f 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -18,7 +18,7 @@ describe(ApiKeyService.name, () => { const apiKey = factory.apiKey({ userId: auth.user.id, permissions: [Permission.ALL] }); const key = 'super-secret'; - mocks.crypto.newPassword.mockReturnValue(key); + mocks.crypto.randomBytesAsText.mockReturnValue(key); mocks.apiKey.create.mockResolvedValue(apiKey); await sut.create(auth, { name: apiKey.name, permissions: apiKey.permissions }); @@ -29,7 +29,7 @@ describe(ApiKeyService.name, () => { permissions: apiKey.permissions, userId: apiKey.userId, }); - expect(mocks.crypto.newPassword).toHaveBeenCalled(); + expect(mocks.crypto.randomBytesAsText).toHaveBeenCalled(); expect(mocks.crypto.hashSha256).toHaveBeenCalled(); }); @@ -38,7 +38,7 @@ describe(ApiKeyService.name, () => { const apiKey = factory.apiKey({ userId: auth.user.id }); const key = 'super-secret'; - mocks.crypto.newPassword.mockReturnValue(key); + mocks.crypto.randomBytesAsText.mockReturnValue(key); mocks.apiKey.create.mockResolvedValue(apiKey); await sut.create(auth, { permissions: [Permission.ALL] }); @@ -49,7 +49,7 @@ describe(ApiKeyService.name, () => { permissions: [Permission.ALL], userId: auth.user.id, }); - expect(mocks.crypto.newPassword).toHaveBeenCalled(); + expect(mocks.crypto.randomBytesAsText).toHaveBeenCalled(); expect(mocks.crypto.hashSha256).toHaveBeenCalled(); }); @@ -69,7 +69,9 @@ describe(ApiKeyService.name, () => { mocks.apiKey.getById.mockResolvedValue(void 0); - await expect(sut.update(auth, id, { name: 'New Name' })).rejects.toBeInstanceOf(BadRequestException); + await expect(sut.update(auth, id, { name: 'New Name', permissions: [Permission.ALL] })).rejects.toBeInstanceOf( + BadRequestException, + ); expect(mocks.apiKey.update).not.toHaveBeenCalledWith(id); }); @@ -82,9 +84,28 @@ describe(ApiKeyService.name, () => { mocks.apiKey.getById.mockResolvedValue(apiKey); mocks.apiKey.update.mockResolvedValue(apiKey); - await sut.update(auth, apiKey.id, { name: newName }); + await sut.update(auth, apiKey.id, { name: newName, permissions: [Permission.ALL] }); - expect(mocks.apiKey.update).toHaveBeenCalledWith(auth.user.id, apiKey.id, { name: newName }); + expect(mocks.apiKey.update).toHaveBeenCalledWith(auth.user.id, apiKey.id, { + name: newName, + permissions: [Permission.ALL], + }); + }); + + it('should update permissions', async () => { + const auth = factory.auth(); + const apiKey = factory.apiKey({ userId: auth.user.id }); + const newPermissions = [Permission.ACTIVITY_CREATE, Permission.ACTIVITY_READ, Permission.ACTIVITY_UPDATE]; + + mocks.apiKey.getById.mockResolvedValue(apiKey); + mocks.apiKey.update.mockResolvedValue(apiKey); + + await sut.update(auth, apiKey.id, { name: apiKey.name, permissions: newPermissions }); + + expect(mocks.apiKey.update).toHaveBeenCalledWith(auth.user.id, apiKey.id, { + name: apiKey.name, + permissions: newPermissions, + }); }); }); diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 33861d82cd..82d4eabdfd 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -9,20 +9,21 @@ import { isGranted } from 'src/utils/access'; @Injectable() export class ApiKeyService extends BaseService { async create(auth: AuthDto, dto: APIKeyCreateDto): Promise { - const secret = this.cryptoRepository.newPassword(32); + const token = this.cryptoRepository.randomBytesAsText(32); + const tokenHashed = this.cryptoRepository.hashSha256(token); if (auth.apiKey && !isGranted({ requested: dto.permissions, current: auth.apiKey.permissions })) { throw new BadRequestException('Cannot grant permissions you do not have'); } const entity = await this.apiKeyRepository.create({ - key: this.cryptoRepository.hashSha256(secret), + key: tokenHashed, name: dto.name || 'API Key', userId: auth.user.id, permissions: dto.permissions, }); - return { secret, apiKey: this.map(entity) }; + return { secret: token, apiKey: this.map(entity) }; } async update(auth: AuthDto, id: string, dto: APIKeyUpdateDto): Promise { @@ -31,7 +32,7 @@ export class ApiKeyService extends BaseService { throw new BadRequestException('API Key not found'); } - const key = await this.apiKeyRepository.update(auth.user.id, id, { name: dto.name }); + const key = await this.apiKeyRepository.update(auth.user.id, id, { name: dto.name, permissions: dto.permissions }); return this.map(key); } diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 0c1bbc3cee..bb8f7115b8 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -8,10 +8,11 @@ import { Stats } from 'node:fs'; import { AssetFile } from 'src/database'; import { AssetMediaStatus, AssetRejectReason, AssetUploadAction } from 'src/dtos/asset-media-response.dto'; import { AssetMediaCreateDto, AssetMediaReplaceDto, AssetMediaSize, UploadFieldName } from 'src/dtos/asset-media.dto'; -import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity'; -import { AssetFileType, AssetStatus, AssetType, CacheControl, JobName } from 'src/enum'; +import { MapAsset } from 'src/dtos/asset-response.dto'; +import { AssetFileType, AssetStatus, AssetType, AssetVisibility, CacheControl, JobName } from 'src/enum'; import { AuthRequest } from 'src/middleware/auth.guard'; import { AssetMediaService } from 'src/services/asset-media.service'; +import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; import { ImmichFileResponse } from 'src/utils/file'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; @@ -141,7 +142,6 @@ const createDto = Object.freeze({ fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), isFavorite: false, - isArchived: false, duration: '0:00:00.000000', }) as AssetMediaCreateDto; @@ -163,7 +163,6 @@ const assetEntity = Object.freeze({ fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), updatedAt: new Date('2022-06-19T23:41:36.910Z'), isFavorite: false, - isArchived: false, encodedVideoPath: '', duration: '0:00:00.000000', files: [] as AssetFile[], @@ -173,7 +172,7 @@ const assetEntity = Object.freeze({ }, livePhotoVideoId: null, sidecarPath: null, -}) as AssetEntity; +} as MapAsset); const existingAsset = Object.freeze({ ...assetEntity, @@ -182,18 +181,18 @@ const existingAsset = Object.freeze({ checksum: Buffer.from('_getExistingAsset', 'utf8'), libraryId: 'libraryId', originalFileName: 'existing-filename.jpeg', -}) as AssetEntity; +}) as MapAsset; const sidecarAsset = Object.freeze({ ...existingAsset, sidecarPath: 'sidecar-path', checksum: Buffer.from('_getExistingAssetWithSideCar', 'utf8'), -}) as AssetEntity; +}) as MapAsset; const copiedAsset = Object.freeze({ id: 'copied-asset', originalPath: 'copied-path', -}) as AssetEntity; +}) as MapAsset; describe(AssetMediaService.name, () => { let sut: AssetMediaService; @@ -436,7 +435,10 @@ describe(AssetMediaService.name, () => { }); it('should hide the linked motion asset', async () => { - mocks.asset.getById.mockResolvedValueOnce({ ...assetStub.livePhotoMotionAsset, isVisible: true }); + mocks.asset.getById.mockResolvedValueOnce({ + ...assetStub.livePhotoMotionAsset, + visibility: AssetVisibility.TIMELINE, + }); mocks.asset.create.mockResolvedValueOnce(assetStub.livePhotoStillAsset); await expect( @@ -451,7 +453,10 @@ describe(AssetMediaService.name, () => { }); expect(mocks.asset.getById).toHaveBeenCalledWith('live-photo-motion-asset'); - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'live-photo-motion-asset', isVisible: false }); + expect(mocks.asset.update).toHaveBeenCalledWith({ + id: 'live-photo-motion-asset', + visibility: AssetVisibility.HIDDEN, + }); }); it('should handle a sidecar file', async () => { @@ -476,7 +481,11 @@ describe(AssetMediaService.name, () => { it('should require the asset.download permission', async () => { await expect(sut.downloadOriginal(authStub.admin, 'asset-1')).rejects.toBeInstanceOf(BadRequestException); - expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['asset-1'])); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1']), + undefined, + ); expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['asset-1'])); expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['asset-1'])); }); @@ -507,7 +516,7 @@ describe(AssetMediaService.name, () => { it('should require asset.view permissions', async () => { await expect(sut.viewThumbnail(authStub.admin, 'id', {})).rejects.toBeInstanceOf(BadRequestException); - expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id']), undefined); expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); }); @@ -606,7 +615,7 @@ describe(AssetMediaService.name, () => { it('should require asset.view permissions', async () => { await expect(sut.playbackVideo(authStub.admin, 'id')).rejects.toBeInstanceOf(BadRequestException); - expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id']), undefined); expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(userStub.admin.id, new Set(['id'])); }); @@ -819,8 +828,8 @@ describe(AssetMediaService.name, () => { const file2 = Buffer.from('53be335e99f18a66ff12e9a901c7a6171dd76573', 'hex'); mocks.asset.getByChecksums.mockResolvedValue([ - { id: 'asset-1', checksum: file1 } as AssetEntity, - { id: 'asset-2', checksum: file2 } as AssetEntity, + { id: 'asset-1', checksum: file1, deletedAt: null }, + { id: 'asset-2', checksum: file2, deletedAt: null }, ]); await expect( @@ -856,7 +865,7 @@ describe(AssetMediaService.name, () => { const file1 = Buffer.from('d2947b871a706081be194569951b7db246907957', 'hex'); const file2 = Buffer.from('53be335e99f18a66ff12e9a901c7a6171dd76573', 'hex'); - mocks.asset.getByChecksums.mockResolvedValue([{ id: 'asset-1', checksum: file1 } as AssetEntity]); + mocks.asset.getByChecksums.mockResolvedValue([{ id: 'asset-1', checksum: file1, deletedAt: null }]); await expect( sut.bulkUploadCheck(authStub.admin, { diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 2929950f4d..87d617ede6 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -2,6 +2,7 @@ import { BadRequestException, Injectable, InternalServerErrorException, NotFound import { extname } from 'node:path'; import sanitize from 'sanitize-filename'; import { StorageCore } from 'src/cores/storage.core'; +import { Asset } from 'src/database'; import { AssetBulkUploadCheckResponseDto, AssetMediaResponseDto, @@ -20,13 +21,13 @@ import { UploadFieldName, } from 'src/dtos/asset-media.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity'; -import { AssetStatus, AssetType, CacheControl, JobName, Permission, StorageFolder } from 'src/enum'; +import { AssetStatus, AssetType, AssetVisibility, CacheControl, JobName, Permission, StorageFolder } from 'src/enum'; import { AuthRequest } from 'src/middleware/auth.guard'; import { BaseService } from 'src/services/base.service'; import { UploadFile } from 'src/types'; import { requireUploadAccess } from 'src/utils/access'; import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util'; +import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file'; import { mimeTypes } from 'src/utils/mime-types'; import { fromChecksum } from 'src/utils/request'; @@ -145,7 +146,6 @@ export class AssetMediaService extends BaseService { { userId: auth.user.id, livePhotoVideoId: dto.livePhotoVideoId }, ); } - const asset = await this.create(auth.user.id, dto, file, sidecarFile); await this.userRepository.updateUsage(auth.user.id, file.size); @@ -212,7 +212,7 @@ export class AssetMediaService extends BaseService { const asset = await this.findOrFail(id); const size = dto.size ?? AssetMediaSize.THUMBNAIL; - const { thumbnailFile, previewFile, fullsizeFile } = getAssetFiles(asset.files); + const { thumbnailFile, previewFile, fullsizeFile } = getAssetFiles(asset.files ?? []); let filepath = previewFile?.path; if (size === AssetMediaSize.THUMBNAIL && thumbnailFile) { filepath = thumbnailFile.path; @@ -375,7 +375,7 @@ export class AssetMediaService extends BaseService { * Uses only vital properties excluding things like: stacks, faces, smart search info, etc, * and then queues a METADATA_EXTRACTION job. */ - private async createCopy(asset: AssetEntity): Promise { + private async createCopy(asset: Omit) { const created = await this.assetRepository.create({ ownerId: asset.ownerId, originalPath: asset.originalPath, @@ -398,12 +398,7 @@ export class AssetMediaService extends BaseService { return created; } - private async create( - ownerId: string, - dto: AssetMediaCreateDto, - file: UploadFile, - sidecarFile?: UploadFile, - ): Promise { + private async create(ownerId: string, dto: AssetMediaCreateDto, file: UploadFile, sidecarFile?: UploadFile) { const asset = await this.assetRepository.create({ ownerId, libraryId: null, @@ -420,9 +415,8 @@ export class AssetMediaService extends BaseService { type: mimeTypes.assetType(file.originalPath), isFavorite: dto.isFavorite, - isArchived: dto.isArchived ?? false, duration: dto.duration || null, - isVisible: dto.isVisible ?? true, + visibility: dto.visibility ?? AssetVisibility.TIMELINE, livePhotoVideoId: dto.livePhotoVideoId, originalFileName: file.originalName, sidecarPath: sidecarFile?.originalPath, @@ -444,7 +438,7 @@ export class AssetMediaService extends BaseService { } } - private async findOrFail(id: string): Promise { + private async findOrFail(id: string) { const asset = await this.assetRepository.getById(id, { files: true }); if (!asset) { throw new NotFoundException('Asset not found'); diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 5fc4984b62..333f4530de 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -1,9 +1,8 @@ import { BadRequestException } from '@nestjs/common'; import { DateTime } from 'luxon'; -import { mapAsset } from 'src/dtos/asset-response.dto'; +import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { AssetStatus, AssetType, JobName, JobStatus } from 'src/enum'; +import { AssetStatus, AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum'; import { AssetStats } from 'src/repositories/asset.repository'; import { AssetService } from 'src/services/asset.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -12,7 +11,6 @@ import { faceStub } from 'test/fixtures/face.stub'; import { userStub } from 'test/fixtures/user.stub'; import { factory } from 'test/small.factory'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -import { vitest } from 'vitest'; const stats: AssetStats = { [AssetType.IMAGE]: 10, @@ -35,7 +33,7 @@ describe(AssetService.name, () => { expect(sut).toBeDefined(); }); - const mockGetById = (assets: AssetEntity[]) => { + const mockGetById = (assets: MapAsset[]) => { mocks.asset.getById.mockImplementation((assetId) => Promise.resolve(assets.find((asset) => asset.id === assetId))); }; @@ -45,73 +43,25 @@ describe(AssetService.name, () => { mockGetById([assetStub.livePhotoStillAsset, assetStub.livePhotoMotionAsset]); }); - describe('getMemoryLane', () => { - beforeAll(() => { - vitest.useFakeTimers(); - vitest.setSystemTime(new Date('2024-01-15')); - }); - - afterAll(() => { - vitest.useRealTimers(); - }); - - it('should group the assets correctly', async () => { - const image1 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 0, 0, 0) }; - const image2 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 1, 0, 0) }; - const image3 = { ...assetStub.image, localDateTime: new Date(2015, 1, 15) }; - const image4 = { ...assetStub.image, localDateTime: new Date(2009, 1, 15) }; - - mocks.partner.getAll.mockResolvedValue([]); - mocks.asset.getByDayOfYear.mockResolvedValue([ - { - year: 2023, - assets: [image1, image2], - }, - { - year: 2015, - assets: [image3], - }, - { - year: 2009, - assets: [image4], - }, - ] as any); - - await expect(sut.getMemoryLane(authStub.admin, { day: 15, month: 1 })).resolves.toEqual([ - { yearsAgo: 1, title: '1 year ago', assets: [mapAsset(image1), mapAsset(image2)] }, - { yearsAgo: 9, title: '9 years ago', assets: [mapAsset(image3)] }, - { yearsAgo: 15, title: '15 years ago', assets: [mapAsset(image4)] }, - ]); - - expect(mocks.asset.getByDayOfYear.mock.calls).toEqual([[[authStub.admin.user.id], { day: 15, month: 1 }]]); - }); - - it('should get memories with partners with inTimeline enabled', async () => { - const partner = factory.partner(); - const auth = factory.auth({ user: { id: partner.sharedWithId } }); - - mocks.partner.getAll.mockResolvedValue([partner]); - mocks.asset.getByDayOfYear.mockResolvedValue([]); - - await sut.getMemoryLane(auth, { day: 15, month: 1 }); - - expect(mocks.asset.getByDayOfYear.mock.calls).toEqual([ - [[auth.user.id, partner.sharedById], { day: 15, month: 1 }], - ]); - }); - }); - describe('getStatistics', () => { it('should get the statistics for a user, excluding archived assets', async () => { mocks.asset.getStatistics.mockResolvedValue(stats); - await expect(sut.getStatistics(authStub.admin, { isArchived: false })).resolves.toEqual(statResponse); - expect(mocks.asset.getStatistics).toHaveBeenCalledWith(authStub.admin.user.id, { isArchived: false }); + await expect(sut.getStatistics(authStub.admin, { visibility: AssetVisibility.TIMELINE })).resolves.toEqual( + statResponse, + ); + expect(mocks.asset.getStatistics).toHaveBeenCalledWith(authStub.admin.user.id, { + visibility: AssetVisibility.TIMELINE, + }); }); it('should get the statistics for a user for archived assets', async () => { mocks.asset.getStatistics.mockResolvedValue(stats); - await expect(sut.getStatistics(authStub.admin, { isArchived: true })).resolves.toEqual(statResponse); - expect(mocks.asset.getStatistics).toHaveBeenCalledWith(authStub.admin.user.id, { isArchived: true }); + await expect(sut.getStatistics(authStub.admin, { visibility: AssetVisibility.ARCHIVE })).resolves.toEqual( + statResponse, + ); + expect(mocks.asset.getStatistics).toHaveBeenCalledWith(authStub.admin.user.id, { + visibility: AssetVisibility.ARCHIVE, + }); }); it('should get the statistics for a user for favorite assets', async () => { @@ -172,6 +122,7 @@ describe(AssetService.name, () => { expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( authStub.admin.user.id, new Set([assetStub.image.id]), + undefined, ); }); @@ -250,9 +201,9 @@ describe(AssetService.name, () => { describe('update', () => { it('should require asset write access for the id', async () => { - await expect(sut.update(authStub.admin, 'asset-1', { isArchived: false })).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.update(authStub.admin, 'asset-1', { visibility: AssetVisibility.TIMELINE }), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -300,7 +251,10 @@ describe(AssetService.name, () => { id: assetStub.livePhotoStillAsset.id, livePhotoVideoId: assetStub.livePhotoMotionAsset.id, }); - expect(mocks.asset.update).not.toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: true }); + expect(mocks.asset.update).not.toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: AssetVisibility.TIMELINE, + }); expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', { assetId: assetStub.livePhotoMotionAsset.id, userId: userStub.admin.id, @@ -321,7 +275,10 @@ describe(AssetService.name, () => { id: assetStub.livePhotoStillAsset.id, livePhotoVideoId: assetStub.livePhotoMotionAsset.id, }); - expect(mocks.asset.update).not.toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: true }); + expect(mocks.asset.update).not.toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: AssetVisibility.TIMELINE, + }); expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', { assetId: assetStub.livePhotoMotionAsset.id, userId: userStub.admin.id, @@ -342,7 +299,10 @@ describe(AssetService.name, () => { id: assetStub.livePhotoStillAsset.id, livePhotoVideoId: assetStub.livePhotoMotionAsset.id, }); - expect(mocks.asset.update).not.toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: true }); + expect(mocks.asset.update).not.toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: AssetVisibility.TIMELINE, + }); expect(mocks.event.emit).not.toHaveBeenCalledWith('asset.show', { assetId: assetStub.livePhotoMotionAsset.id, userId: userStub.admin.id, @@ -354,7 +314,7 @@ describe(AssetService.name, () => { mocks.asset.getById.mockResolvedValueOnce({ ...assetStub.livePhotoMotionAsset, ownerId: authStub.admin.user.id, - isVisible: true, + visibility: AssetVisibility.TIMELINE, }); mocks.asset.getById.mockResolvedValueOnce(assetStub.image); mocks.asset.update.mockResolvedValue(assetStub.image); @@ -363,7 +323,10 @@ describe(AssetService.name, () => { livePhotoVideoId: assetStub.livePhotoMotionAsset.id, }); - expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: false }); + expect(mocks.asset.update).toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: AssetVisibility.HIDDEN, + }); expect(mocks.event.emit).toHaveBeenCalledWith('asset.hide', { assetId: assetStub.livePhotoMotionAsset.id, userId: userStub.admin.id, @@ -393,7 +356,10 @@ describe(AssetService.name, () => { id: assetStub.livePhotoStillAsset.id, livePhotoVideoId: null, }); - expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: true }); + expect(mocks.asset.update).toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: assetStub.livePhotoStillAsset.visibility, + }); expect(mocks.event.emit).toHaveBeenCalledWith('asset.show', { assetId: assetStub.livePhotoMotionAsset.id, userId: userStub.admin.id, @@ -419,7 +385,6 @@ describe(AssetService.name, () => { await expect( sut.updateAll(authStub.admin, { ids: ['asset-1'], - isArchived: false, }), ).rejects.toBeInstanceOf(BadRequestException); }); @@ -427,9 +392,11 @@ describe(AssetService.name, () => { it('should update all assets', async () => { mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2'])); - await sut.updateAll(authStub.admin, { ids: ['asset-1', 'asset-2'], isArchived: true }); + await sut.updateAll(authStub.admin, { ids: ['asset-1', 'asset-2'], visibility: AssetVisibility.ARCHIVE }); - expect(mocks.asset.updateAll).toHaveBeenCalledWith(['asset-1', 'asset-2'], { isArchived: true }); + expect(mocks.asset.updateAll).toHaveBeenCalledWith(['asset-1', 'asset-2'], { + visibility: AssetVisibility.ARCHIVE, + }); }); it('should not update Assets table if no relevant fields are provided', async () => { @@ -439,7 +406,6 @@ describe(AssetService.name, () => { ids: ['asset-1'], latitude: 0, longitude: 0, - isArchived: undefined, isFavorite: undefined, duplicateId: undefined, rating: undefined, @@ -447,14 +413,14 @@ describe(AssetService.name, () => { expect(mocks.asset.updateAll).not.toHaveBeenCalled(); }); - it('should update Assets table if isArchived field is provided', async () => { + it('should update Assets table if visibility field is provided', async () => { mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); await sut.updateAll(authStub.admin, { ids: ['asset-1'], latitude: 0, longitude: 0, - isArchived: undefined, + visibility: undefined, isFavorite: false, duplicateId: undefined, rating: undefined, @@ -474,7 +440,6 @@ describe(AssetService.name, () => { latitude: 30, longitude: 50, dateTimeOriginal, - isArchived: undefined, isFavorite: false, duplicateId: undefined, rating: undefined, @@ -489,6 +454,20 @@ describe(AssetService.name, () => { { name: JobName.SIDECAR_WRITE, data: { id: 'asset-1', dateTimeOriginal, latitude: 30, longitude: 50 } }, ]); }); + + it('should update Assets table if duplicateId is provided as null', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); + + await sut.updateAll(authStub.admin, { + ids: ['asset-1'], + latitude: 0, + longitude: 0, + isFavorite: undefined, + duplicateId: null, + rating: undefined, + }); + expect(mocks.asset.updateAll).toHaveBeenCalled(); + }); }); describe('deleteAll', () => { @@ -566,7 +545,7 @@ describe(AssetService.name, () => { it('should remove faces', async () => { const assetWithFace = { ...assetStub.image, faces: [faceStub.face1, faceStub.mergeFace1] }; - mocks.asset.getById.mockResolvedValue(assetWithFace); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetWithFace); await sut.handleAssetDeletion({ id: assetWithFace.id, deleteOnDisk: true }); @@ -593,7 +572,7 @@ describe(AssetService.name, () => { it('should update stack primary asset if deleted asset was primary asset in a stack', async () => { mocks.stack.update.mockResolvedValue(factory.stack() as any); - mocks.asset.getById.mockResolvedValue(assetStub.primaryImage); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.primaryImage); await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true }); @@ -605,10 +584,10 @@ describe(AssetService.name, () => { it('should delete the entire stack if deleted asset was the primary asset and the stack would only contain one asset afterwards', async () => { mocks.stack.delete.mockResolvedValue(); - mocks.asset.getById.mockResolvedValue({ + mocks.assetJob.getForAssetDeletion.mockResolvedValue({ ...assetStub.primaryImage, stack: { ...assetStub.primaryImage.stack, assets: assetStub.primaryImage.stack!.assets.slice(0, 2) }, - } as AssetEntity); + }); await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true }); @@ -616,7 +595,7 @@ describe(AssetService.name, () => { }); it('should delete a live photo', async () => { - mocks.asset.getById.mockResolvedValue(assetStub.livePhotoStillAsset); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.livePhotoStillAsset as any); mocks.asset.getLivePhotoCount.mockResolvedValue(0); await sut.handleAssetDeletion({ @@ -654,7 +633,7 @@ describe(AssetService.name, () => { it('should not delete a live motion part if it is being used by another asset', async () => { mocks.asset.getLivePhotoCount.mockResolvedValue(2); - mocks.asset.getById.mockResolvedValue(assetStub.livePhotoStillAsset); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.livePhotoStillAsset as any); await sut.handleAssetDeletion({ id: assetStub.livePhotoStillAsset.id, @@ -681,12 +660,13 @@ describe(AssetService.name, () => { }); it('should update usage', async () => { - mocks.asset.getById.mockResolvedValue(assetStub.image); + mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.image); await sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true }); expect(mocks.user.updateUsage).toHaveBeenCalledWith(assetStub.image.ownerId, -5000); }); it('should fail if asset could not be found', async () => { + mocks.assetJob.getForAssetDeletion.mockResolvedValue(void 0); await expect(sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true })).resolves.toBe( JobStatus.FAILED, ); diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 1ded79680b..bc73ff6410 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -3,12 +3,7 @@ import _ from 'lodash'; import { DateTime, Duration } from 'luxon'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { OnJob } from 'src/decorators'; -import { - AssetResponseDto, - MemoryLaneResponseDto, - SanitizedAssetResponseDto, - mapAsset, -} from 'src/dtos/asset-response.dto'; +import { AssetResponseDto, MapAsset, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AssetBulkDeleteDto, AssetBulkUpdateDto, @@ -19,35 +14,13 @@ import { mapStats, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { MemoryLaneDto } from 'src/dtos/search.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { AssetStatus, JobName, JobStatus, Permission, QueueName } from 'src/enum'; +import { AssetStatus, AssetVisibility, JobName, JobStatus, Permission, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { ISidecarWriteJob, JobItem, JobOf } from 'src/types'; import { getAssetFiles, getMyPartnerIds, onAfterUnlink, onBeforeLink, onBeforeUnlink } from 'src/utils/asset.util'; @Injectable() export class AssetService extends BaseService { - async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise { - const partnerIds = await getMyPartnerIds({ - userId: auth.user.id, - repository: this.partnerRepository, - timelineEnabled: true, - }); - const userIds = [auth.user.id, ...partnerIds]; - - const groups = await this.assetRepository.getByDayOfYear(userIds, dto); - return groups.map(({ year, assets }) => { - const yearsAgo = DateTime.utc().year - year; - return { - yearsAgo, - // TODO move this to clients - title: `${yearsAgo} year${yearsAgo > 1 ? 's' : ''} ago`, - assets: assets.map((asset) => mapAsset(asset as unknown as AssetEntity, { auth })), - }; - }); - } - async getStatistics(auth: AuthDto, dto: AssetStatsDto) { const stats = await this.assetRepository.getStatistics(auth.user.id, dto); return mapStats(stats); @@ -105,7 +78,7 @@ export class AssetService extends BaseService { const { description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto; const repos = { asset: this.assetRepository, event: this.eventRepository }; - let previousMotion: AssetEntity | null = null; + let previousMotion: MapAsset | null = null; if (rest.livePhotoVideoId) { await onBeforeLink(repos, { userId: auth.user.id, livePhotoVideoId: rest.livePhotoVideoId }); } else if (rest.livePhotoVideoId === null) { @@ -119,8 +92,12 @@ export class AssetService extends BaseService { const asset = await this.assetRepository.update({ id, ...rest }); - if (previousMotion) { - await onAfterUnlink(repos, { userId: auth.user.id, livePhotoVideoId: previousMotion.id }); + if (previousMotion && asset) { + await onAfterUnlink(repos, { + userId: auth.user.id, + livePhotoVideoId: previousMotion.id, + visibility: asset.visibility, + }); } if (!asset) { @@ -131,23 +108,35 @@ export class AssetService extends BaseService { } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { - const { ids, dateTimeOriginal, latitude, longitude, ...options } = dto; + const { ids, description, dateTimeOriginal, latitude, longitude, ...options } = dto; await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids }); - if (dateTimeOriginal !== undefined || latitude !== undefined || longitude !== undefined) { - await this.assetRepository.updateAllExif(ids, { dateTimeOriginal, latitude, longitude }); + if ( + description !== undefined || + dateTimeOriginal !== undefined || + latitude !== undefined || + longitude !== undefined + ) { + await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude }); await this.jobRepository.queueAll( - ids.map((id) => ({ name: JobName.SIDECAR_WRITE, data: { id, dateTimeOriginal, latitude, longitude } })), + ids.map((id) => ({ + name: JobName.SIDECAR_WRITE, + data: { id, description, dateTimeOriginal, latitude, longitude }, + })), ); } if ( - options.isArchived != undefined || - options.isFavorite != undefined || - options.duplicateId != undefined || - options.rating != undefined + options.visibility !== undefined || + options.isFavorite !== undefined || + options.duplicateId !== undefined || + options.rating !== undefined ) { await this.assetRepository.updateAll(ids, options); + + if (options.visibility === AssetVisibility.LOCKED) { + await this.albumRepository.removeAssetsFromAll(ids); + } } } @@ -189,13 +178,7 @@ export class AssetService extends BaseService { async handleAssetDeletion(job: JobOf): Promise { const { id, deleteOnDisk } = job; - const asset = await this.assetRepository.getById(id, { - faces: { person: true }, - library: true, - stack: { assets: true }, - exifInfo: true, - files: true, - }); + const asset = await this.assetJobRepository.getForAssetDeletion(id); if (!asset) { return JobStatus.FAILED; @@ -233,7 +216,7 @@ export class AssetService extends BaseService { } } - const { fullsizeFile, previewFile, thumbnailFile } = getAssetFiles(asset.files); + const { fullsizeFile, previewFile, thumbnailFile } = getAssetFiles(asset.files ?? []); const files = [thumbnailFile?.path, previewFile?.path, fullsizeFile?.path, asset.encodedVideoPath]; if (deleteOnDisk) { diff --git a/server/src/services/audit.service.spec.ts b/server/src/services/audit.service.spec.ts index 6ef139f506..381b2ec7e8 100644 --- a/server/src/services/audit.service.spec.ts +++ b/server/src/services/audit.service.spec.ts @@ -1,6 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; -import { FileReportItemDto } from 'src/dtos/audit.dto'; -import { AssetFileType, AssetPathType, JobStatus, PersonPathType, UserPathType } from 'src/enum'; +import { JobStatus } from 'src/enum'; import { AuditService } from 'src/services/audit.service'; import { newTestService, ServiceMocks } from 'test/utils'; @@ -25,148 +23,4 @@ describe(AuditService.name, () => { expect(mocks.audit.removeBefore).toHaveBeenCalledWith(expect.any(Date)); }); }); - - describe('getChecksums', () => { - it('should fail if the file is not in the immich path', async () => { - await expect(sut.getChecksums({ filenames: ['foo/bar'] })).rejects.toBeInstanceOf(BadRequestException); - - expect(mocks.crypto.hashFile).not.toHaveBeenCalled(); - }); - - it('should get checksum for valid file', async () => { - await expect(sut.getChecksums({ filenames: ['./upload/my-file.jpg'] })).resolves.toEqual([ - { filename: './upload/my-file.jpg', checksum: expect.any(String) }, - ]); - - expect(mocks.crypto.hashFile).toHaveBeenCalledWith('./upload/my-file.jpg'); - }); - }); - - describe('fixItems', () => { - it('should fail if the file is not in the immich path', async () => { - await expect( - sut.fixItems([ - { entityId: 'my-id', pathType: AssetPathType.ORIGINAL, pathValue: 'foo/bar' } as FileReportItemDto, - ]), - ).rejects.toBeInstanceOf(BadRequestException); - - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update encoded video path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.ENCODED_VIDEO, - pathValue: './upload/my-video.mp4', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', encodedVideoPath: './upload/my-video.mp4' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update preview path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.PREVIEW, - pathValue: './upload/my-preview.png', - } as FileReportItemDto, - ]); - - expect(mocks.asset.upsertFile).toHaveBeenCalledWith({ - assetId: 'my-id', - type: AssetFileType.PREVIEW, - path: './upload/my-preview.png', - }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update thumbnail path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.THUMBNAIL, - pathValue: './upload/my-thumbnail.webp', - } as FileReportItemDto, - ]); - - expect(mocks.asset.upsertFile).toHaveBeenCalledWith({ - assetId: 'my-id', - type: AssetFileType.THUMBNAIL, - path: './upload/my-thumbnail.webp', - }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update original path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.ORIGINAL, - pathValue: './upload/my-original.png', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', originalPath: './upload/my-original.png' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update sidecar path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: AssetPathType.SIDECAR, - pathValue: './upload/my-sidecar.xmp', - } as FileReportItemDto, - ]); - - expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'my-id', sidecarPath: './upload/my-sidecar.xmp' }); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update face path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: PersonPathType.FACE, - pathValue: './upload/my-face.jpg', - } as FileReportItemDto, - ]); - - expect(mocks.person.update).toHaveBeenCalledWith({ id: 'my-id', thumbnailPath: './upload/my-face.jpg' }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.user.update).not.toHaveBeenCalled(); - }); - - it('should update profile path', async () => { - await sut.fixItems([ - { - entityId: 'my-id', - pathType: UserPathType.PROFILE, - pathValue: './upload/my-profile-pic.jpg', - } as FileReportItemDto, - ]); - - expect(mocks.user.update).toHaveBeenCalledWith('my-id', { profileImagePath: './upload/my-profile-pic.jpg' }); - expect(mocks.asset.update).not.toHaveBeenCalled(); - expect(mocks.asset.upsertFile).not.toHaveBeenCalled(); - expect(mocks.person.update).not.toHaveBeenCalled(); - }); - }); }); diff --git a/server/src/services/audit.service.ts b/server/src/services/audit.service.ts index a049a9c64b..7c9a070dd0 100644 --- a/server/src/services/audit.service.ts +++ b/server/src/services/audit.service.ts @@ -1,23 +1,9 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { DateTime } from 'luxon'; -import { resolve } from 'node:path'; -import { AUDIT_LOG_MAX_DURATION, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; -import { StorageCore } from 'src/cores/storage.core'; +import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; import { OnJob } from 'src/decorators'; -import { FileChecksumDto, FileChecksumResponseDto, FileReportItemDto, PathEntityType } from 'src/dtos/audit.dto'; -import { - AssetFileType, - AssetPathType, - JobName, - JobStatus, - PersonPathType, - QueueName, - StorageFolder, - UserPathType, -} from 'src/enum'; +import { JobName, JobStatus, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; -import { getAssetFiles } from 'src/utils/asset.util'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class AuditService extends BaseService { @@ -26,187 +12,4 @@ export class AuditService extends BaseService { await this.auditRepository.removeBefore(DateTime.now().minus(AUDIT_LOG_MAX_DURATION).toJSDate()); return JobStatus.SUCCESS; } - - async getChecksums(dto: FileChecksumDto) { - const results: FileChecksumResponseDto[] = []; - for (const filename of dto.filenames) { - if (!StorageCore.isImmichPath(filename)) { - throw new BadRequestException( - `Could not get the checksum of ${filename} because the file isn't accessible by Immich`, - ); - } - - const checksum = await this.cryptoRepository.hashFile(filename); - results.push({ filename, checksum: checksum.toString('base64') }); - } - return results; - } - - async fixItems(items: FileReportItemDto[]) { - for (const { entityId: id, pathType, pathValue } of items) { - if (!StorageCore.isImmichPath(pathValue)) { - throw new BadRequestException( - `Could not fix item ${id} with path ${pathValue} because the file isn't accessible by Immich`, - ); - } - - switch (pathType) { - case AssetPathType.ENCODED_VIDEO: { - await this.assetRepository.update({ id, encodedVideoPath: pathValue }); - break; - } - - case AssetPathType.PREVIEW: { - await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.PREVIEW, path: pathValue }); - break; - } - - case AssetPathType.THUMBNAIL: { - await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.THUMBNAIL, path: pathValue }); - break; - } - - case AssetPathType.ORIGINAL: { - await this.assetRepository.update({ id, originalPath: pathValue }); - break; - } - - case AssetPathType.SIDECAR: { - await this.assetRepository.update({ id, sidecarPath: pathValue }); - break; - } - - case PersonPathType.FACE: { - await this.personRepository.update({ id, thumbnailPath: pathValue }); - break; - } - - case UserPathType.PROFILE: { - await this.userRepository.update(id, { profileImagePath: pathValue }); - break; - } - } - } - } - - private fullPath(filename: string) { - return resolve(filename); - } - - async getFileReport() { - const hasFile = (items: Set, filename: string) => items.has(filename) || items.has(this.fullPath(filename)); - const crawl = async (folder: StorageFolder) => - new Set( - await this.storageRepository.crawl({ - includeHidden: true, - pathsToCrawl: [StorageCore.getBaseFolder(folder)], - }), - ); - - const uploadFiles = await crawl(StorageFolder.UPLOAD); - const libraryFiles = await crawl(StorageFolder.LIBRARY); - const thumbFiles = await crawl(StorageFolder.THUMBNAILS); - const videoFiles = await crawl(StorageFolder.ENCODED_VIDEO); - const profileFiles = await crawl(StorageFolder.PROFILE); - const allFiles = new Set(); - for (const list of [libraryFiles, thumbFiles, videoFiles, profileFiles, uploadFiles]) { - for (const item of list) { - allFiles.add(item); - } - } - - const track = (filename: string | null | undefined) => { - if (!filename) { - return; - } - allFiles.delete(filename); - allFiles.delete(this.fullPath(filename)); - }; - - this.logger.log( - `Found ${libraryFiles.size} original files, ${thumbFiles.size} thumbnails, ${videoFiles.size} encoded videos, ${profileFiles.size} profile files`, - ); - const pagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (options) => - this.assetRepository.getAll(options, { withDeleted: true, withArchived: true }), - ); - - let assetCount = 0; - - const orphans: FileReportItemDto[] = []; - for await (const assets of pagination) { - assetCount += assets.length; - for (const { id, files, originalPath, encodedVideoPath, isExternal, checksum } of assets) { - const { fullsizeFile, previewFile, thumbnailFile } = getAssetFiles(files); - for (const file of [ - originalPath, - fullsizeFile?.path, - previewFile?.path, - encodedVideoPath, - thumbnailFile?.path, - ]) { - track(file); - } - - const entity = { entityId: id, entityType: PathEntityType.ASSET, checksum: checksum.toString('base64') }; - if ( - originalPath && - !hasFile(libraryFiles, originalPath) && - !hasFile(uploadFiles, originalPath) && - // Android motion assets - !hasFile(videoFiles, originalPath) && - // ignore external library assets - !isExternal - ) { - orphans.push({ ...entity, pathType: AssetPathType.ORIGINAL, pathValue: originalPath }); - } - if (previewFile && !hasFile(thumbFiles, previewFile.path)) { - orphans.push({ ...entity, pathType: AssetPathType.PREVIEW, pathValue: previewFile.path }); - } - if (thumbnailFile && !hasFile(thumbFiles, thumbnailFile.path)) { - orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: thumbnailFile.path }); - } - if (encodedVideoPath && !hasFile(videoFiles, encodedVideoPath)) { - orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: encodedVideoPath }); - } - } - } - - const users = await this.userRepository.getList(); - for (const { id, profileImagePath } of users) { - track(profileImagePath); - - const entity = { entityId: id, entityType: PathEntityType.USER }; - if (profileImagePath && !hasFile(profileFiles, profileImagePath)) { - orphans.push({ ...entity, pathType: UserPathType.PROFILE, pathValue: profileImagePath }); - } - } - - let peopleCount = 0; - for await (const { id, thumbnailPath } of this.personRepository.getAll()) { - track(thumbnailPath); - const entity = { entityId: id, entityType: PathEntityType.PERSON }; - if (thumbnailPath && !hasFile(thumbFiles, thumbnailPath)) { - orphans.push({ ...entity, pathType: PersonPathType.FACE, pathValue: thumbnailPath }); - } - - if (peopleCount === JOBS_ASSET_PAGINATION_SIZE) { - this.logger.log(`Found ${assetCount} assets, ${users.length} users, ${peopleCount} people`); - peopleCount = 0; - } - } - - this.logger.log(`Found ${assetCount} assets, ${users.length} users, ${peopleCount} people`); - - const extras: string[] = []; - for (const file of allFiles) { - extras.push(file); - } - - // send as absolute paths - for (const orphan of orphans) { - orphan.pathValue = this.fullPath(orphan.pathValue); - } - - return { orphans, extras }; - } } diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index b1bfe00e85..4bc5f1ce0b 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -1,5 +1,6 @@ import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import { DateTime } from 'luxon'; +import { SALT_ROUNDS } from 'src/constants'; import { UserAdmin } from 'src/database'; import { AuthDto, SignUpDto } from 'src/dtos/auth.dto'; import { AuthType, Permission } from 'src/enum'; @@ -55,7 +56,7 @@ describe(AuthService.name, () => { beforeEach(() => { ({ sut, mocks } = newTestService(AuthService)); - mocks.oauth.authorize.mockResolvedValue('access-token'); + mocks.oauth.authorize.mockResolvedValue({ url: 'http://test', state: 'state', codeVerifier: 'codeVerifier' }); mocks.oauth.getProfile.mockResolvedValue({ sub, email }); mocks.oauth.getLogoutEndpoint.mockResolvedValue('http://end-session-endpoint'); }); @@ -64,16 +65,6 @@ describe(AuthService.name, () => { expect(sut).toBeDefined(); }); - describe('onBootstrap', () => { - it('should init the repo', () => { - mocks.oauth.init.mockResolvedValue(); - - sut.onBootstrap(); - - expect(mocks.oauth.init).toHaveBeenCalled(); - }); - }); - describe('login', () => { it('should throw an error if password login is disabled', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.disabled); @@ -128,7 +119,7 @@ describe(AuthService.name, () => { await sut.changePassword(auth, dto); - expect(mocks.user.getByEmail).toHaveBeenCalledWith(auth.user.email, true); + expect(mocks.user.getByEmail).toHaveBeenCalledWith(auth.user.email, { withPassword: true }); expect(mocks.crypto.compareBcrypt).toHaveBeenCalledWith('old-password', 'hash-password'); }); @@ -262,6 +253,7 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), + pinExpiresAt: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -274,7 +266,7 @@ describe(AuthService.name, () => { }), ).resolves.toEqual({ user: sessionWithToken.user, - session: { id: session.id }, + session: { id: session.id, hasElevatedPermission: false }, }); }); }); @@ -385,6 +377,7 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), + pinExpiresAt: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -397,7 +390,7 @@ describe(AuthService.name, () => { }), ).resolves.toEqual({ user: sessionWithToken.user, - session: { id: session.id }, + session: { id: session.id, hasElevatedPermission: false }, }); }); @@ -407,6 +400,7 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), + pinExpiresAt: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -426,6 +420,7 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), + pinExpiresAt: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -519,16 +514,22 @@ describe(AuthService.name, () => { describe('callback', () => { it('should throw an error if OAuth is not enabled', async () => { - await expect(sut.callback({ url: '' }, loginDetails)).rejects.toBeInstanceOf(BadRequestException); + await expect( + sut.callback({ url: '', state: 'xyz789', codeVerifier: 'foo' }, {}, loginDetails), + ).rejects.toBeInstanceOf(BadRequestException); }); it('should not allow auto registering', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled); mocks.user.getByEmail.mockResolvedValue(void 0); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1); }); @@ -541,9 +542,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { oauthId: sub }); @@ -557,9 +562,13 @@ describe(AuthService.name, () => { mocks.user.getAdmin.mockResolvedValue(user); mocks.user.create.mockResolvedValue(user); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toThrow( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).rejects.toThrow(BadRequestException); expect(mocks.user.update).not.toHaveBeenCalled(); expect(mocks.user.create).not.toHaveBeenCalled(); @@ -574,9 +583,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.getByEmail).toHaveBeenCalledTimes(2); // second call is for domain check before create expect(mocks.user.create).toHaveBeenCalledTimes(1); @@ -592,18 +605,19 @@ describe(AuthService.name, () => { mocks.session.create.mockResolvedValue(factory.session()); mocks.oauth.getProfile.mockResolvedValue({ sub, email: undefined }); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foobar' }, + {}, + loginDetails, + ), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.getByEmail).not.toHaveBeenCalled(); expect(mocks.user.create).not.toHaveBeenCalled(); }); for (const url of [ - 'app.immich:/', - 'app.immich://', - 'app.immich:///', 'app.immich:/oauth-callback?code=abc123', 'app.immich://oauth-callback?code=abc123', 'app.immich:///oauth-callback?code=abc123', @@ -615,9 +629,14 @@ describe(AuthService.name, () => { mocks.user.getByOAuthId.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await sut.callback({ url }, loginDetails); + await sut.callback({ url, state: 'xyz789', codeVerifier: 'foo' }, {}, loginDetails); - expect(mocks.oauth.getProfile).toHaveBeenCalledWith(expect.objectContaining({}), url, 'http://mobile-redirect'); + expect(mocks.oauth.getProfile).toHaveBeenCalledWith( + expect.objectContaining({}), + 'http://mobile-redirect?code=abc123', + 'xyz789', + 'foo', + ); }); } @@ -630,9 +649,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -647,9 +670,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -664,9 +691,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 })); }); @@ -681,9 +712,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith({ email: user.email, @@ -705,9 +740,13 @@ describe(AuthService.name, () => { mocks.user.create.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.create).toHaveBeenCalledWith({ email: user.email, @@ -738,9 +777,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { profileImagePath: `upload/profile/${user.id}/${fileId}.jpg`, @@ -762,9 +805,13 @@ describe(AuthService.name, () => { mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); - await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual( - oauthResponse(user), - ); + await expect( + sut.callback( + { url: 'http://immich/auth/login?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + loginDetails, + ), + ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).not.toHaveBeenCalled(); expect(mocks.oauth.getProfilePicture).not.toHaveBeenCalled(); @@ -779,7 +826,11 @@ describe(AuthService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.update.mockResolvedValue(user); - await sut.link(auth, { url: 'http://immich/user-settings?code=abc123' }); + await sut.link( + auth, + { url: 'http://immich/user-settings?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, + {}, + ); expect(mocks.user.update).toHaveBeenCalledWith(auth.user.id, { oauthId: sub }); }); @@ -792,9 +843,9 @@ describe(AuthService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled); mocks.user.getByOAuthId.mockResolvedValue({ id: 'other-user' } as UserAdmin); - await expect(sut.link(auth, { url: 'http://immich/user-settings?code=abc123' })).rejects.toBeInstanceOf( - BadRequestException, - ); + await expect( + sut.link(auth, { url: 'http://immich/user-settings?code=abc123', state: 'xyz789', codeVerifier: 'foo' }, {}), + ).rejects.toBeInstanceOf(BadRequestException); expect(mocks.user.update).not.toHaveBeenCalled(); }); @@ -813,4 +864,81 @@ describe(AuthService.name, () => { expect(mocks.user.update).toHaveBeenCalledWith(auth.user.id, { oauthId: '' }); }); }); + + describe('setupPinCode', () => { + it('should setup a PIN code', async () => { + const user = factory.userAdmin(); + const auth = factory.auth({ user }); + const dto = { pinCode: '123456' }; + + mocks.user.getForPinCode.mockResolvedValue({ pinCode: null, password: '' }); + mocks.user.update.mockResolvedValue(user); + + await sut.setupPinCode(auth, dto); + + expect(mocks.user.getForPinCode).toHaveBeenCalledWith(user.id); + expect(mocks.crypto.hashBcrypt).toHaveBeenCalledWith('123456', SALT_ROUNDS); + expect(mocks.user.update).toHaveBeenCalledWith(user.id, { pinCode: expect.any(String) }); + }); + + it('should fail if the user already has a PIN code', async () => { + const user = factory.userAdmin(); + const auth = factory.auth({ user }); + + mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); + + await expect(sut.setupPinCode(auth, { pinCode: '123456' })).rejects.toThrow('User already has a PIN code'); + }); + }); + + describe('changePinCode', () => { + it('should change the PIN code', async () => { + const user = factory.userAdmin(); + const auth = factory.auth({ user }); + const dto = { pinCode: '123456', newPinCode: '012345' }; + + mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); + mocks.user.update.mockResolvedValue(user); + mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); + + await sut.changePinCode(auth, dto); + + expect(mocks.crypto.compareBcrypt).toHaveBeenCalledWith('123456', '123456 (hashed)'); + expect(mocks.user.update).toHaveBeenCalledWith(user.id, { pinCode: '012345 (hashed)' }); + }); + + it('should fail if the PIN code does not match', async () => { + const user = factory.userAdmin(); + mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); + mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); + + await expect( + sut.changePinCode(factory.auth({ user }), { pinCode: '000000', newPinCode: '012345' }), + ).rejects.toThrow('Wrong PIN code'); + }); + }); + + describe('resetPinCode', () => { + it('should reset the PIN code', async () => { + const currentSession = factory.session(); + const user = factory.userAdmin(); + mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); + mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); + mocks.session.lockAll.mockResolvedValue(void 0); + mocks.session.update.mockResolvedValue(currentSession); + + await sut.resetPinCode(factory.auth({ user }), { pinCode: '123456' }); + + expect(mocks.user.update).toHaveBeenCalledWith(user.id, { pinCode: null }); + expect(mocks.session.lockAll).toHaveBeenCalledWith(user.id); + }); + + it('should throw if the PIN code does not match', async () => { + const user = factory.userAdmin(); + mocks.user.getForPinCode.mockResolvedValue({ pinCode: '123456 (hashed)', password: '' }); + mocks.crypto.compareBcrypt.mockImplementation((a, b) => `${a} (hashed)` === b); + + await expect(sut.resetPinCode(factory.auth({ user }), { pinCode: '000000' })).rejects.toThrow('Wrong PIN code'); + }); + }); }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index ee4ca4dc5d..e6c541a624 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -7,15 +7,18 @@ import { join } from 'node:path'; import { LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { UserAdmin } from 'src/database'; -import { OnEvent } from 'src/decorators'; import { AuthDto, + AuthStatusResponseDto, ChangePasswordDto, LoginCredentialDto, LogoutResponseDto, - OAuthAuthorizeResponseDto, OAuthCallbackDto, OAuthConfigDto, + PinCodeChangeDto, + PinCodeResetDto, + PinCodeSetupDto, + SessionUnlockDto, SignUpDto, mapLoginResponse, } from 'src/dtos/auth.dto'; @@ -52,20 +55,15 @@ export type ValidateRequest = { @Injectable() export class AuthService extends BaseService { - @OnEvent({ name: 'app.bootstrap' }) - onBootstrap() { - this.oauthRepository.init(); - } - async login(dto: LoginCredentialDto, details: LoginDetails) { const config = await this.getConfig({ withCache: false }); if (!config.passwordLogin.enabled) { throw new UnauthorizedException('Password login has been disabled'); } - let user = await this.userRepository.getByEmail(dto.email, true); + let user = await this.userRepository.getByEmail(dto.email, { withPassword: true }); if (user) { - const isAuthenticated = this.validatePassword(dto.password, user); + const isAuthenticated = this.validateSecret(dto.password, user.password); if (!isAuthenticated) { user = undefined; } @@ -93,12 +91,12 @@ export class AuthService extends BaseService { async changePassword(auth: AuthDto, dto: ChangePasswordDto): Promise { const { password, newPassword } = dto; - const user = await this.userRepository.getByEmail(auth.user.email, true); + const user = await this.userRepository.getByEmail(auth.user.email, { withPassword: true }); if (!user) { throw new UnauthorizedException(); } - const valid = this.validatePassword(password, user); + const valid = this.validateSecret(password, user.password); if (!valid) { throw new BadRequestException('Wrong password'); } @@ -110,6 +108,57 @@ export class AuthService extends BaseService { return mapUserAdmin(updatedUser); } + async setupPinCode(auth: AuthDto, { pinCode }: PinCodeSetupDto) { + const user = await this.userRepository.getForPinCode(auth.user.id); + if (!user) { + throw new UnauthorizedException(); + } + + if (user.pinCode) { + throw new BadRequestException('User already has a PIN code'); + } + + const hashed = await this.cryptoRepository.hashBcrypt(pinCode, SALT_ROUNDS); + await this.userRepository.update(auth.user.id, { pinCode: hashed }); + } + + async resetPinCode(auth: AuthDto, dto: PinCodeResetDto) { + const user = await this.userRepository.getForPinCode(auth.user.id); + this.validatePinCode(user, dto); + + await this.userRepository.update(auth.user.id, { pinCode: null }); + await this.sessionRepository.lockAll(auth.user.id); + } + + async changePinCode(auth: AuthDto, dto: PinCodeChangeDto) { + const user = await this.userRepository.getForPinCode(auth.user.id); + this.validatePinCode(user, dto); + + const hashed = await this.cryptoRepository.hashBcrypt(dto.newPinCode, SALT_ROUNDS); + await this.userRepository.update(auth.user.id, { pinCode: hashed }); + } + + private validatePinCode( + user: { pinCode: string | null; password: string | null }, + dto: { pinCode?: string; password?: string }, + ) { + if (!user.pinCode) { + throw new BadRequestException('User does not have a PIN code'); + } + + if (dto.password) { + if (!this.validateSecret(dto.password, user.password)) { + throw new BadRequestException('Wrong password'); + } + } else if (dto.pinCode) { + if (!this.validateSecret(dto.pinCode, user.pinCode)) { + throw new BadRequestException('Wrong PIN code'); + } + } else { + throw new BadRequestException('Either password or pinCode is required'); + } + } + async adminSignUp(dto: SignUpDto): Promise { const adminUser = await this.userRepository.getAdmin(); if (adminUser) { @@ -176,20 +225,35 @@ export class AuthService extends BaseService { return `${MOBILE_REDIRECT}?${url.split('?')[1] || ''}`; } - async authorize(dto: OAuthConfigDto): Promise { + async authorize(dto: OAuthConfigDto) { const { oauth } = await this.getConfig({ withCache: false }); if (!oauth.enabled) { throw new BadRequestException('OAuth is not enabled'); } - const url = await this.oauthRepository.authorize(oauth, this.resolveRedirectUri(oauth, dto.redirectUri)); - return { url }; + return await this.oauthRepository.authorize( + oauth, + this.resolveRedirectUri(oauth, dto.redirectUri), + dto.state, + dto.codeChallenge, + ); } - async callback(dto: OAuthCallbackDto, loginDetails: LoginDetails) { + async callback(dto: OAuthCallbackDto, headers: IncomingHttpHeaders, loginDetails: LoginDetails) { + const expectedState = dto.state ?? this.getCookieOauthState(headers); + if (!expectedState?.length) { + throw new BadRequestException('OAuth state is missing'); + } + + const codeVerifier = dto.codeVerifier ?? this.getCookieCodeVerifier(headers); + if (!codeVerifier?.length) { + throw new BadRequestException('OAuth code verifier is missing'); + } + const { oauth } = await this.getConfig({ withCache: false }); - const profile = await this.oauthRepository.getProfile(oauth, dto.url, this.resolveRedirectUri(oauth, dto.url)); + const url = this.resolveRedirectUri(oauth, dto.url); + const profile = await this.oauthRepository.getProfile(oauth, url, expectedState, codeVerifier); const { autoRegister, defaultStorageQuota, storageLabelClaim, storageQuotaClaim } = oauth; this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`); let user: UserAdmin | undefined = await this.userRepository.getByOAuthId(profile.sub); @@ -271,13 +335,19 @@ export class AuthService extends BaseService { } } - async link(auth: AuthDto, dto: OAuthCallbackDto): Promise { + async link(auth: AuthDto, dto: OAuthCallbackDto, headers: IncomingHttpHeaders): Promise { + const expectedState = dto.state ?? this.getCookieOauthState(headers); + if (!expectedState?.length) { + throw new BadRequestException('OAuth state is missing'); + } + + const codeVerifier = dto.codeVerifier ?? this.getCookieCodeVerifier(headers); + if (!codeVerifier?.length) { + throw new BadRequestException('OAuth code verifier is missing'); + } + const { oauth } = await this.getConfig({ withCache: false }); - const { sub: oauthId } = await this.oauthRepository.getProfile( - oauth, - dto.url, - this.resolveRedirectUri(oauth, dto.url), - ); + const { sub: oauthId } = await this.oauthRepository.getProfile(oauth, dto.url, expectedState, codeVerifier); const duplicate = await this.userRepository.getByOAuthId(oauthId); if (duplicate && duplicate.id !== auth.user.id) { this.logger.warn(`OAuth link account failed: sub is already linked to another user (${duplicate.email}).`); @@ -320,6 +390,16 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.ACCESS_TOKEN] || null; } + private getCookieOauthState(headers: IncomingHttpHeaders): string | null { + const cookies = parse(headers.cookie || ''); + return cookies[ImmichCookie.OAUTH_STATE] || null; + } + + private getCookieCodeVerifier(headers: IncomingHttpHeaders): string | null { + const cookies = parse(headers.cookie || ''); + return cookies[ImmichCookie.OAUTH_CODE_VERIFIER] || null; + } + async validateSharedLink(key: string | string[]): Promise { key = Array.isArray(key) ? key[0] : key; @@ -347,11 +427,12 @@ export class AuthService extends BaseService { throw new UnauthorizedException('Invalid API key'); } - private validatePassword(inputPassword: string, user: { password?: string }): boolean { - if (!user || !user.password) { + private validateSecret(inputSecret: string, existingHash?: string | null): boolean { + if (!existingHash) { return false; } - return this.cryptoRepository.compareBcrypt(inputPassword, user.password); + + return this.cryptoRepository.compareBcrypt(inputSecret, existingHash); } private async validateSession(tokenValue: string): Promise { @@ -365,10 +446,25 @@ export class AuthService extends BaseService { await this.sessionRepository.update(session.id, { id: session.id, updatedAt: new Date() }); } + // Pin check + let hasElevatedPermission = false; + + if (session.pinExpiresAt) { + const pinExpiresAt = DateTime.fromJSDate(session.pinExpiresAt); + hasElevatedPermission = pinExpiresAt > now; + + if (hasElevatedPermission && now.plus({ minutes: 5 }) > pinExpiresAt) { + await this.sessionRepository.update(session.id, { + pinExpiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate(), + }); + } + } + return { user: session.user, session: { id: session.id, + hasElevatedPermission, }, }; } @@ -376,18 +472,39 @@ export class AuthService extends BaseService { throw new UnauthorizedException('Invalid user token'); } + async unlockSession(auth: AuthDto, dto: SessionUnlockDto): Promise { + if (!auth.session) { + throw new BadRequestException('This endpoint can only be used with a session token'); + } + + const user = await this.userRepository.getForPinCode(auth.user.id); + this.validatePinCode(user, { pinCode: dto.pinCode }); + + await this.sessionRepository.update(auth.session.id, { + pinExpiresAt: DateTime.now().plus({ minutes: 15 }).toJSDate(), + }); + } + + async lockSession(auth: AuthDto): Promise { + if (!auth.session) { + throw new BadRequestException('This endpoint can only be used with a session token'); + } + + await this.sessionRepository.update(auth.session.id, { pinExpiresAt: null }); + } + private async createLoginResponse(user: UserAdmin, loginDetails: LoginDetails) { - const key = this.cryptoRepository.newPassword(32); - const token = this.cryptoRepository.hashSha256(key); + const token = this.cryptoRepository.randomBytesAsText(32); + const tokenHashed = this.cryptoRepository.hashSha256(token); await this.sessionRepository.create({ - token, + token: tokenHashed, deviceOS: loginDetails.deviceOS, deviceType: loginDetails.deviceType, userId: user.id, }); - return mapLoginResponse(user, key); + return mapLoginResponse(user, token); } private getClaim(profile: OAuthProfile, options: ClaimOptions): T { @@ -399,11 +516,26 @@ export class AuthService extends BaseService { { mobileRedirectUri, mobileOverrideEnabled }: { mobileRedirectUri: string; mobileOverrideEnabled: boolean }, url: string, ) { - const redirectUri = url.split('?')[0]; - const isMobile = redirectUri.startsWith('app.immich:/'); - if (isMobile && mobileOverrideEnabled && mobileRedirectUri) { - return mobileRedirectUri; + if (mobileOverrideEnabled && mobileRedirectUri) { + return url.replace(/app\.immich:\/+oauth-callback/, mobileRedirectUri); } - return redirectUri; + return url; + } + + async getAuthStatus(auth: AuthDto): Promise { + const user = await this.userRepository.getForPinCode(auth.user.id); + if (!user) { + throw new UnauthorizedException(); + } + + const session = auth.session ? await this.sessionRepository.get(auth.session.id) : undefined; + + return { + pinCode: !!user.pinCode, + password: !!user.password, + isElevated: !!auth.session?.hasElevatedPermission, + expiresAt: session?.expiresAt?.toISOString(), + pinExpiresAt: session?.pinExpiresAt?.toISOString(), + }; } } diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index 704087ab05..aa72fd588a 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -142,52 +142,55 @@ describe(BackupService.name, () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.createWriteStream.mockReturnValue(new PassThrough()); }); + it('should run a database backup successfully', async () => { const result = await sut.handleBackupDatabase(); expect(result).toBe(JobStatus.SUCCESS); expect(mocks.storage.createWriteStream).toHaveBeenCalled(); }); + it('should rename file on success', async () => { const result = await sut.handleBackupDatabase(); expect(result).toBe(JobStatus.SUCCESS); expect(mocks.storage.rename).toHaveBeenCalled(); }); + it('should fail if pg_dumpall fails', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); }); + it('should not rename file if pgdump fails and gzip succeeds', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); expect(mocks.storage.rename).not.toHaveBeenCalled(); }); + it('should fail if gzip fails', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(0, 'data', '')); mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Gzip failed with code 1'); }); + it('should fail if write stream fails', async () => { mocks.storage.createWriteStream.mockImplementation(() => { throw new Error('error'); }); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('error'); }); + it('should fail if rename fails', async () => { mocks.storage.rename.mockRejectedValue(new Error('error')); - const result = await sut.handleBackupDatabase(); - expect(result).toBe(JobStatus.FAILED); + await expect(sut.handleBackupDatabase()).rejects.toThrow('error'); }); + it('should ignore unlink failing and still return failed job status', async () => { mocks.process.spawn.mockReturnValueOnce(mockSpawn(1, '', 'error')); mocks.storage.unlink.mockRejectedValue(new Error('error')); - const result = await sut.handleBackupDatabase(); + await expect(sut.handleBackupDatabase()).rejects.toThrow('Backup failed with code 1'); expect(mocks.storage.unlink).toHaveBeenCalled(); - expect(result).toBe(JobStatus.FAILED); }); + it.each` postgresVersion | expectedVersion ${'14.10'} | ${14} diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index dc4f71b992..10f7becc7d 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -70,7 +70,7 @@ export class BackupService extends BaseService { async handleBackupDatabase(): Promise { this.logger.debug(`Database Backup Started`); const { database } = this.configRepository.getEnv(); - const config = database.config.typeorm; + const config = database.config; const isUrlConnection = config.connectionType === 'url'; @@ -174,7 +174,7 @@ export class BackupService extends BaseService { await this.storageRepository .unlink(backupFilePath) .catch((error) => this.logger.error('Failed to delete failed backup file', error)); - return JobStatus.FAILED; + throw error; } this.logger.log(`Database Backup Success`); diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2fbdd6e4c0..3381ad7222 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -18,6 +18,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -70,6 +71,7 @@ export class BaseService { protected cryptoRepository: CryptoRepository, protected databaseRepository: DatabaseRepository, protected downloadRepository: DownloadRepository, + protected emailRepository: EmailRepository, protected eventRepository: EventRepository, protected jobRepository: JobRepository, protected libraryRepository: LibraryRepository, diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 87e004845d..f6173c69f7 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -17,7 +17,7 @@ export class CliService extends BaseService { } const providedPassword = await ask(mapUserAdmin(admin)); - const password = providedPassword || this.cryptoRepository.newPassword(24); + const password = providedPassword || this.cryptoRepository.randomBytesAsText(24); const hashedPassword = await this.cryptoRepository.hashBcrypt(password, SALT_ROUNDS); await this.userRepository.update(admin.id, { password: hashedPassword }); diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index 4e45ec3ae0..09b22dfd5e 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -1,5 +1,5 @@ import { EXTENSION_NAMES } from 'src/constants'; -import { DatabaseExtension } from 'src/enum'; +import { DatabaseExtension, VectorIndex } from 'src/enum'; import { DatabaseService } from 'src/services/database.service'; import { VectorExtension } from 'src/types'; import { mockEnvData } from 'test/repositories/config.repository.mock'; @@ -19,16 +19,20 @@ describe(DatabaseService.name, () => { ({ sut, mocks } = newTestService(DatabaseService)); extensionRange = '0.2.x'; + mocks.database.getVectorExtension.mockResolvedValue(DatabaseExtension.VECTORCHORD); mocks.database.getExtensionVersionRange.mockReturnValue(extensionRange); versionBelowRange = '0.1.0'; minVersionInRange = '0.2.0'; updateInRange = '0.2.1'; versionAboveRange = '0.3.0'; - mocks.database.getExtensionVersion.mockResolvedValue({ - installedVersion: minVersionInRange, - availableVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.VECTORCHORD, + installedVersion: null, + availableVersion: minVersionInRange, + }, + ]); }); it('should work', () => { @@ -47,28 +51,27 @@ describe(DatabaseService.name, () => { describe.each(>[ { extension: DatabaseExtension.VECTOR, extensionName: EXTENSION_NAMES[DatabaseExtension.VECTOR] }, { extension: DatabaseExtension.VECTORS, extensionName: EXTENSION_NAMES[DatabaseExtension.VECTORS] }, + { extension: DatabaseExtension.VECTORCHORD, extensionName: EXTENSION_NAMES[DatabaseExtension.VECTORCHORD] }, ])('should work with $extensionName', ({ extension, extensionName }) => { beforeEach(() => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + ]); + mocks.database.getVectorExtension.mockResolvedValue(extension); mocks.config.getEnv.mockReturnValue( mockEnvData({ database: { config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, + connectionType: 'parts', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'immich', }, skipMigrations: false, vectorExtension: extension, @@ -79,23 +82,26 @@ describe(DatabaseService.name, () => { it(`should start up successfully with ${extension}`, async () => { mocks.database.getPostgresVersion.mockResolvedValue('14.0.0'); - mocks.database.getExtensionVersion.mockResolvedValue({ - installedVersion: null, - availableVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + installedVersion: null, + availableVersion: minVersionInRange, + }, + ]); await expect(sut.onBootstrap()).resolves.toBeUndefined(); expect(mocks.database.getPostgresVersion).toHaveBeenCalled(); expect(mocks.database.createExtension).toHaveBeenCalledWith(extension); expect(mocks.database.createExtension).toHaveBeenCalledTimes(1); - expect(mocks.database.getExtensionVersion).toHaveBeenCalled(); + expect(mocks.database.getExtensionVersions).toHaveBeenCalled(); expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); expect(mocks.logger.fatal).not.toHaveBeenCalled(); }); it(`should throw an error if the ${extension} extension is not installed`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ installedVersion: null, availableVersion: null }); + mocks.database.getExtensionVersions.mockResolvedValue([]); const message = `The ${extensionName} extension is not available in this Postgres instance. If using a container image, ensure the image has the extension installed.`; await expect(sut.onBootstrap()).rejects.toThrow(message); @@ -105,10 +111,13 @@ describe(DatabaseService.name, () => { }); it(`should throw an error if the ${extension} extension version is below minimum supported version`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - installedVersion: versionBelowRange, - availableVersion: versionBelowRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + installedVersion: versionBelowRange, + availableVersion: versionBelowRange, + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow( `The ${extensionName} extension version is ${versionBelowRange}, but Immich only supports ${extensionRange}`, @@ -118,7 +127,13 @@ describe(DatabaseService.name, () => { }); it(`should throw an error if ${extension} extension version is a nightly`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ installedVersion: '0.0.0', availableVersion: '0.0.0' }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + installedVersion: '0.0.0', + availableVersion: '0.0.0', + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow( `The ${extensionName} extension version is 0.0.0, which means it is a nightly release.`, @@ -130,26 +145,32 @@ describe(DatabaseService.name, () => { }); it(`should do in-range update for ${extension} extension`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: updateInRange, - installedVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: updateInRange, + installedVersion: minVersionInRange, + }, + ]); mocks.database.updateVectorExtension.mockResolvedValue({ restartRequired: false }); await expect(sut.onBootstrap()).resolves.toBeUndefined(); expect(mocks.database.updateVectorExtension).toHaveBeenCalledWith(extension, updateInRange); expect(mocks.database.updateVectorExtension).toHaveBeenCalledTimes(1); - expect(mocks.database.getExtensionVersion).toHaveBeenCalled(); + expect(mocks.database.getExtensionVersions).toHaveBeenCalled(); expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); expect(mocks.logger.fatal).not.toHaveBeenCalled(); }); it(`should not upgrade ${extension} if same version`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: minVersionInRange, - installedVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: minVersionInRange, + installedVersion: minVersionInRange, + }, + ]); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -159,10 +180,13 @@ describe(DatabaseService.name, () => { }); it(`should throw error if ${extension} available version is below range`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: versionBelowRange, - installedVersion: null, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: versionBelowRange, + installedVersion: null, + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow(); @@ -173,10 +197,13 @@ describe(DatabaseService.name, () => { }); it(`should throw error if ${extension} available version is above range`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: versionAboveRange, - installedVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: versionAboveRange, + installedVersion: minVersionInRange, + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow(); @@ -187,10 +214,13 @@ describe(DatabaseService.name, () => { }); it('should throw error if available version is below installed version', async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: minVersionInRange, - installedVersion: updateInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: minVersionInRange, + installedVersion: updateInRange, + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow( `The database currently has ${extensionName} ${updateInRange} activated, but the Postgres instance only has ${minVersionInRange} available.`, @@ -202,10 +232,13 @@ describe(DatabaseService.name, () => { }); it('should throw error if installed version is not in version range', async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: minVersionInRange, - installedVersion: versionAboveRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: minVersionInRange, + installedVersion: versionAboveRange, + }, + ]); await expect(sut.onBootstrap()).rejects.toThrow( `The ${extensionName} extension version is ${versionAboveRange}, but Immich only supports`, @@ -217,10 +250,13 @@ describe(DatabaseService.name, () => { }); it(`should raise error if ${extension} extension upgrade failed`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: updateInRange, - installedVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: updateInRange, + installedVersion: minVersionInRange, + }, + ]); mocks.database.updateVectorExtension.mockRejectedValue(new Error('Failed to update extension')); await expect(sut.onBootstrap()).rejects.toThrow('Failed to update extension'); @@ -234,10 +270,13 @@ describe(DatabaseService.name, () => { }); it(`should warn if ${extension} extension update requires restart`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - availableVersion: updateInRange, - installedVersion: minVersionInRange, - }); + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: extension, + availableVersion: updateInRange, + installedVersion: minVersionInRange, + }, + ]); mocks.database.updateVectorExtension.mockResolvedValue({ restartRequired: true }); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -250,41 +289,32 @@ describe(DatabaseService.name, () => { }); it(`should reindex ${extension} indices if needed`, async () => { - mocks.database.shouldReindex.mockResolvedValue(true); - await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.database.shouldReindex).toHaveBeenCalledTimes(2); - expect(mocks.database.reindex).toHaveBeenCalledTimes(2); + expect(mocks.database.reindexVectorsIfNeeded).toHaveBeenCalledExactlyOnceWith([ + VectorIndex.CLIP, + VectorIndex.FACE, + ]); + expect(mocks.database.reindexVectorsIfNeeded).toHaveBeenCalledTimes(1); expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); expect(mocks.logger.fatal).not.toHaveBeenCalled(); }); it(`should throw an error if reindexing fails`, async () => { - mocks.database.shouldReindex.mockResolvedValue(true); - mocks.database.reindex.mockRejectedValue(new Error('Error reindexing')); + mocks.database.reindexVectorsIfNeeded.mockRejectedValue(new Error('Error reindexing')); await expect(sut.onBootstrap()).rejects.toBeDefined(); - expect(mocks.database.shouldReindex).toHaveBeenCalledTimes(1); - expect(mocks.database.reindex).toHaveBeenCalledTimes(1); + expect(mocks.database.reindexVectorsIfNeeded).toHaveBeenCalledExactlyOnceWith([ + VectorIndex.CLIP, + VectorIndex.FACE, + ]); expect(mocks.database.runMigrations).not.toHaveBeenCalled(); expect(mocks.logger.fatal).not.toHaveBeenCalled(); expect(mocks.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Could not run vector reindexing checks.'), ); }); - - it(`should not reindex ${extension} indices if not needed`, async () => { - mocks.database.shouldReindex.mockResolvedValue(false); - - await expect(sut.onBootstrap()).resolves.toBeUndefined(); - - expect(mocks.database.shouldReindex).toHaveBeenCalledTimes(2); - expect(mocks.database.reindex).toHaveBeenCalledTimes(0); - expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); - expect(mocks.logger.fatal).not.toHaveBeenCalled(); - }); }); it('should skip migrations if DB_SKIP_MIGRATIONS=true', async () => { @@ -292,22 +322,12 @@ describe(DatabaseService.name, () => { mockEnvData({ database: { config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, + connectionType: 'parts', + host: 'database', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'immich', }, skipMigrations: true, vectorExtension: DatabaseExtension.VECTORS, @@ -320,68 +340,80 @@ describe(DatabaseService.name, () => { expect(mocks.database.runMigrations).not.toHaveBeenCalled(); }); - it(`should throw error if pgvector extension could not be created`, async () => { - mocks.config.getEnv.mockReturnValue( - mockEnvData({ - database: { - config: { - kysely: { - host: 'database', - port: 5432, - user: 'postgres', - password: 'postgres', - database: 'immich', - }, - typeorm: { - connectionType: 'parts', - type: 'postgres', - host: 'database', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'immich', - }, - }, - skipMigrations: true, - vectorExtension: DatabaseExtension.VECTOR, - }, - }), - ); - mocks.database.getExtensionVersion.mockResolvedValue({ - installedVersion: null, - availableVersion: minVersionInRange, - }); + it(`should throw error if extension could not be created`, async () => { mocks.database.updateVectorExtension.mockResolvedValue({ restartRequired: false }); mocks.database.createExtension.mockRejectedValue(new Error('Failed to create extension')); await expect(sut.onBootstrap()).rejects.toThrow('Failed to create extension'); expect(mocks.logger.fatal).toHaveBeenCalledTimes(1); - expect(mocks.logger.fatal.mock.calls[0][0]).toContain( - `Alternatively, if your Postgres instance has pgvecto.rs, you may use this instead`, - ); + expect(mocks.logger.fatal.mock.calls[0][0]).toContain('CREATE EXTENSION IF NOT EXISTS vchord CASCADE'); expect(mocks.database.createExtension).toHaveBeenCalledTimes(1); expect(mocks.database.updateVectorExtension).not.toHaveBeenCalled(); expect(mocks.database.runMigrations).not.toHaveBeenCalled(); }); - it(`should throw error if pgvecto.rs extension could not be created`, async () => { - mocks.database.getExtensionVersion.mockResolvedValue({ - installedVersion: null, - availableVersion: minVersionInRange, - }); - mocks.database.updateVectorExtension.mockResolvedValue({ restartRequired: false }); - mocks.database.createExtension.mockRejectedValue(new Error('Failed to create extension')); + it(`should drop unused extension`, async () => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.VECTORS, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + { + name: DatabaseExtension.VECTORCHORD, + installedVersion: null, + availableVersion: minVersionInRange, + }, + ]); - await expect(sut.onBootstrap()).rejects.toThrow('Failed to create extension'); + await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.logger.fatal).toHaveBeenCalledTimes(1); - expect(mocks.logger.fatal.mock.calls[0][0]).toContain( - `Alternatively, if your Postgres instance has pgvector, you may use this instead`, - ); - expect(mocks.database.createExtension).toHaveBeenCalledTimes(1); - expect(mocks.database.updateVectorExtension).not.toHaveBeenCalled(); - expect(mocks.database.runMigrations).not.toHaveBeenCalled(); + expect(mocks.database.createExtension).toHaveBeenCalledExactlyOnceWith(DatabaseExtension.VECTORCHORD); + expect(mocks.database.dropExtension).toHaveBeenCalledExactlyOnceWith(DatabaseExtension.VECTORS); + }); + + it(`should warn if unused extension could not be dropped`, async () => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.VECTORS, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + { + name: DatabaseExtension.VECTORCHORD, + installedVersion: null, + availableVersion: minVersionInRange, + }, + ]); + mocks.database.dropExtension.mockRejectedValue(new Error('Failed to drop extension')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + + expect(mocks.database.createExtension).toHaveBeenCalledExactlyOnceWith(DatabaseExtension.VECTORCHORD); + expect(mocks.database.dropExtension).toHaveBeenCalledExactlyOnceWith(DatabaseExtension.VECTORS); + expect(mocks.logger.warn).toHaveBeenCalledTimes(1); + expect(mocks.logger.warn.mock.calls[0][0]).toContain('DROP EXTENSION vectors'); + }); + + it(`should not try to drop pgvector when using vectorchord`, async () => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.VECTOR, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + { + name: DatabaseExtension.VECTORCHORD, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + ]); + mocks.database.dropExtension.mockRejectedValue(new Error('Failed to drop extension')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + + expect(mocks.database.dropExtension).not.toHaveBeenCalled(); }); }); }); diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index d71dc25104..7470c0bf8f 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -1,13 +1,14 @@ import { Injectable } from '@nestjs/common'; import semver from 'semver'; -import { EXTENSION_NAMES } from 'src/constants'; +import { EXTENSION_NAMES, VECTOR_EXTENSIONS } from 'src/constants'; import { OnEvent } from 'src/decorators'; import { BootstrapEventPriority, DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { VectorExtension } from 'src/types'; -type CreateFailedArgs = { name: string; extension: string; otherName: string }; +type CreateFailedArgs = { name: string; extension: string }; type UpdateFailedArgs = { name: string; extension: string; availableVersion: string }; +type DropFailedArgs = { name: string; extension: string }; type RestartRequiredArgs = { name: string; availableVersion: string }; type NightlyVersionArgs = { name: string; extension: string; version: string }; type OutOfRangeArgs = { name: string; extension: string; version: string; range: string }; @@ -25,18 +26,13 @@ const messages = { outOfRange: ({ name, version, range }: OutOfRangeArgs) => `The ${name} extension version is ${version}, but Immich only supports ${range}. Please change ${name} to a compatible version in the Postgres instance.`, - createFailed: ({ name, extension, otherName }: CreateFailedArgs) => + createFailed: ({ name, extension }: CreateFailedArgs) => `Failed to activate ${name} extension. Please ensure the Postgres instance has ${name} installed. If the Postgres instance already has ${name} installed, Immich may not have the necessary permissions to activate it. - In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension}' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database. - - Alternatively, if your Postgres instance has ${otherName}, you may use this instead by setting the environment variable 'DB_VECTOR_EXTENSION=${otherName}'. - Note that switching between the two extensions after a successful startup is not supported. - The exception is if your version of Immich prior to upgrading was 1.90.2 or earlier. - In this case, you may set either extension now, but you will not be able to switch to the other extension following a successful startup.`, + In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension} CASCADE' manually as a superuser. + See https://immich.app/docs/guides/database-queries for how to query the database.`, updateFailed: ({ name, extension, availableVersion }: UpdateFailedArgs) => `The ${name} extension can be updated to ${availableVersion}. Immich attempted to update the extension, but failed to do so. @@ -44,6 +40,12 @@ const messages = { Please run 'ALTER EXTENSION ${extension} UPDATE' manually as a superuser. See https://immich.app/docs/guides/database-queries for how to query the database.`, + dropFailed: ({ name, extension }: DropFailedArgs) => + `The ${name} extension is no longer needed, but could not be dropped. + This may be because Immich does not have the necessary permissions to drop the extension. + + Please run 'DROP EXTENSION ${extension};' manually as a superuser. + See https://immich.app/docs/guides/database-queries for how to query the database.`, restartRequired: ({ name, availableVersion }: RestartRequiredArgs) => `The ${name} extension has been updated to ${availableVersion}. Please restart the Postgres instance to complete the update.`, @@ -67,12 +69,12 @@ export class DatabaseService extends BaseService { } await this.databaseRepository.withLock(DatabaseLock.Migrations, async () => { - const envData = this.configRepository.getEnv(); - const extension = envData.database.vectorExtension; + const extension = await this.databaseRepository.getVectorExtension(); const name = EXTENSION_NAMES[extension]; const extensionRange = this.databaseRepository.getExtensionVersionRange(extension); - const { availableVersion, installedVersion } = await this.databaseRepository.getExtensionVersion(extension); + const extensionVersions = await this.databaseRepository.getExtensionVersions(VECTOR_EXTENSIONS); + const { installedVersion, availableVersion } = extensionVersions.find((v) => v.name === extension) ?? {}; if (!availableVersion) { throw new Error(messages.notInstalled(name)); } @@ -97,12 +99,30 @@ export class DatabaseService extends BaseService { throw new Error(messages.invalidDowngrade({ name, extension, availableVersion, installedVersion })); } - await this.checkReindexing(); + try { + await this.databaseRepository.reindexVectorsIfNeeded([VectorIndex.CLIP, VectorIndex.FACE]); + } catch (error) { + this.logger.warn( + 'Could not run vector reindexing checks. If the extension was updated, please restart the Postgres instance. If you are upgrading directly from a version below 1.107.2, please upgrade to 1.107.2 first.', + ); + throw error; + } + + for (const { name: dbName, installedVersion } of extensionVersions) { + const isDepended = dbName === DatabaseExtension.VECTOR && extension === DatabaseExtension.VECTORCHORD; + if (dbName !== extension && installedVersion && !isDepended) { + await this.dropExtension(dbName); + } + } const { database } = this.configRepository.getEnv(); if (!database.skipMigrations) { await this.databaseRepository.runMigrations(); } + await Promise.all([ + this.databaseRepository.prewarm(VectorIndex.CLIP), + this.databaseRepository.prewarm(VectorIndex.FACE), + ]); }); } @@ -110,10 +130,8 @@ export class DatabaseService extends BaseService { try { await this.databaseRepository.createExtension(extension); } catch (error) { - const otherExtension = - extension === DatabaseExtension.VECTORS ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS; const name = EXTENSION_NAMES[extension]; - this.logger.fatal(messages.createFailed({ name, extension, otherName: EXTENSION_NAMES[otherExtension] })); + this.logger.fatal(messages.createFailed({ name, extension })); throw error; } } @@ -131,20 +149,12 @@ export class DatabaseService extends BaseService { } } - private async checkReindexing() { + private async dropExtension(extension: DatabaseExtension) { try { - if (await this.databaseRepository.shouldReindex(VectorIndex.CLIP)) { - await this.databaseRepository.reindex(VectorIndex.CLIP); - } - - if (await this.databaseRepository.shouldReindex(VectorIndex.FACE)) { - await this.databaseRepository.reindex(VectorIndex.FACE); - } + await this.databaseRepository.dropExtension(extension); } catch (error) { - this.logger.warn( - 'Could not run vector reindexing checks. If the extension was updated, please restart the Postgres instance.', - ); - throw error; + const name = EXTENSION_NAMES[extension]; + this.logger.warn(messages.dropFailed({ name, extension }), error); } } } diff --git a/server/src/services/download.service.ts b/server/src/services/download.service.ts index cb664aea32..02711b9bfd 100644 --- a/server/src/services/download.service.ts +++ b/server/src/services/download.service.ts @@ -33,7 +33,7 @@ export class DownloadService extends BaseService { const targetSize = dto.archiveSize || HumanReadableSize.GiB * 4; const metadata = await this.userRepository.getMetadata(auth.user.id); - const preferences = getPreferences(auth.user.email, metadata); + const preferences = getPreferences(metadata); const motionIds = new Set(); const archives: DownloadArchiveInfo[] = []; let archive: DownloadArchiveInfo = { size: 0, assetIds: [] }; diff --git a/server/src/services/duplicate.service.spec.ts b/server/src/services/duplicate.service.spec.ts index 57ab955514..d23144babe 100644 --- a/server/src/services/duplicate.service.spec.ts +++ b/server/src/services/duplicate.service.spec.ts @@ -1,10 +1,9 @@ -import { AssetFileType, AssetType, JobName, JobStatus } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; +import { AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum'; import { DuplicateService } from 'src/services/duplicate.service'; import { SearchService } from 'src/services/search.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; import { beforeEach, vitest } from 'vitest'; vitest.useFakeTimers(); @@ -12,22 +11,11 @@ vitest.useFakeTimers(); const hasEmbedding = { id: 'asset-1', ownerId: 'user-id', - files: [ - { - assetId: 'asset-1', - createdAt: new Date(), - id: 'file-1', - path: 'preview.jpg', - type: AssetFileType.PREVIEW, - updatedAt: new Date(), - updateId: 'update-1', - }, - ], - isVisible: true, stackId: null, type: AssetType.IMAGE, duplicateId: null, embedding: '[1, 2, 3, 4]', + visibility: AssetVisibility.TIMELINE, }; const hasDupe = { @@ -113,14 +101,11 @@ describe(SearchService.name, () => { }); it('should queue missing assets', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForSearchDuplicates.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSearchDuplicates({}); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.DUPLICATE); + expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(undefined); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.DUPLICATE_DETECTION, @@ -130,14 +115,11 @@ describe(SearchService.name, () => { }); it('should queue all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForSearchDuplicates.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSearchDuplicates({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.DUPLICATE_DETECTION, @@ -214,7 +196,10 @@ describe(SearchService.name, () => { it('should skip if asset is not visible', async () => { const id = assetStub.livePhotoMotionAsset.id; - mocks.assetJob.getForSearchDuplicatesJob.mockResolvedValue({ ...hasEmbedding, isVisible: false }); + mocks.assetJob.getForSearchDuplicatesJob.mockResolvedValue({ + ...hasEmbedding, + visibility: AssetVisibility.HIDDEN, + }); const result = await sut.handleSearchDuplicates({ id }); @@ -222,15 +207,6 @@ describe(SearchService.name, () => { expect(mocks.logger.debug).toHaveBeenCalledWith(`Asset ${id} is not visible, skipping`); }); - it('should fail if asset is missing preview image', async () => { - mocks.assetJob.getForSearchDuplicatesJob.mockResolvedValue({ ...hasEmbedding, files: [] }); - - const result = await sut.handleSearchDuplicates({ id: assetStub.noResizePath.id }); - - expect(result).toBe(JobStatus.FAILED); - expect(mocks.logger.warn).toHaveBeenCalledWith(`Asset ${assetStub.noResizePath.id} is missing preview image`); - }); - it('should fail if asset is missing embedding', async () => { mocks.assetJob.getForSearchDuplicatesJob.mockResolvedValue({ ...hasEmbedding, embedding: null }); diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts index 9f1ac3d4ce..617f5c5d0d 100644 --- a/server/src/services/duplicate.service.ts +++ b/server/src/services/duplicate.service.ts @@ -4,14 +4,11 @@ import { OnJob } from 'src/decorators'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; -import { AssetFileType, JobName, JobStatus, QueueName } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; +import { AssetVisibility, JobName, JobStatus, QueueName } from 'src/enum'; import { AssetDuplicateResult } from 'src/repositories/search.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; -import { getAssetFile } from 'src/utils/asset.util'; +import { JobItem, JobOf } from 'src/types'; import { isDuplicateDetectionEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class DuplicateService extends BaseService { @@ -30,18 +27,22 @@ export class DuplicateService extends BaseService { return JobStatus.SKIPPED; } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { isVisible: true }) - : this.assetRepository.getWithout(pagination, WithoutProperty.DUPLICATE); - }); + let jobs: JobItem[] = []; + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.DUPLICATE_DETECTION, data: { id: asset.id } })), - ); + const assets = this.assetJobRepository.streamForSearchDuplicates(force); + for await (const asset of assets) { + jobs.push({ name: JobName.DUPLICATE_DETECTION, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } + await queueAll(); + return JobStatus.SUCCESS; } @@ -63,17 +64,11 @@ export class DuplicateService extends BaseService { return JobStatus.SKIPPED; } - if (!asset.isVisible) { + if (asset.visibility === AssetVisibility.HIDDEN) { this.logger.debug(`Asset ${id} is not visible, skipping`); return JobStatus.SKIPPED; } - const previewFile = getAssetFile(asset.files, AssetFileType.PREVIEW); - if (!previewFile) { - this.logger.warn(`Asset ${id} is missing preview image`); - return JobStatus.FAILED; - } - if (!asset.embedding) { this.logger.debug(`Asset ${id} is missing embedding`); return JobStatus.FAILED; diff --git a/server/src/services/index.ts b/server/src/services/index.ts index b214dd14f6..88b68d2c13 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -17,6 +17,7 @@ import { MapService } from 'src/services/map.service'; import { MediaService } from 'src/services/media.service'; import { MemoryService } from 'src/services/memory.service'; import { MetadataService } from 'src/services/metadata.service'; +import { NotificationAdminService } from 'src/services/notification-admin.service'; import { NotificationService } from 'src/services/notification.service'; import { PartnerService } from 'src/services/partner.service'; import { PersonService } from 'src/services/person.service'; @@ -60,6 +61,7 @@ export const services = [ MemoryService, MetadataService, NotificationService, + NotificationAdminService, PartnerService, PersonService, SearchService, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 134a86b69f..c9020ed96a 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -230,7 +230,7 @@ describe(JobService.name, () => { expect(mocks.logger.error).not.toHaveBeenCalled(); }); - const tests: Array<{ item: JobItem; jobs: JobName[] }> = [ + const tests: Array<{ item: JobItem; jobs: JobName[]; stub?: any }> = [ { item: { name: JobName.SIDECAR_SYNC, data: { id: 'asset-1' } }, jobs: [JobName.METADATA_EXTRACTION], @@ -239,10 +239,6 @@ describe(JobService.name, () => { item: { name: JobName.SIDECAR_DISCOVERY, data: { id: 'asset-1' } }, jobs: [JobName.METADATA_EXTRACTION], }, - { - item: { name: JobName.METADATA_EXTRACTION, data: { id: 'asset-1' } }, - jobs: [JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE], - }, { item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1', source: 'upload' } }, jobs: [JobName.GENERATE_THUMBNAILS], @@ -258,14 +254,22 @@ describe(JobService.name, () => { { item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1' } }, jobs: [], + stub: [assetStub.image], + }, + { + item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1' } }, + jobs: [], + stub: [assetStub.video], + }, + { + item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1', source: 'upload' } }, + jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION], + stub: [assetStub.livePhotoStillAsset], }, { item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-1', source: 'upload' } }, jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION, JobName.VIDEO_CONVERSION], - }, - { - item: { name: JobName.GENERATE_THUMBNAILS, data: { id: 'asset-live-image', source: 'upload' } }, - jobs: [JobName.SMART_SEARCH, JobName.FACE_DETECTION, JobName.VIDEO_CONVERSION], + stub: [assetStub.video], }, { item: { name: JobName.SMART_SEARCH, data: { id: 'asset-1' } }, @@ -281,14 +285,10 @@ describe(JobService.name, () => { }, ]; - for (const { item, jobs } of tests) { + for (const { item, jobs, stub } of tests) { it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => { - if (item.name === JobName.GENERATE_THUMBNAILS && item.data.source === 'upload') { - if (item.data.id === 'asset-live-image') { - mocks.asset.getByIdsWithAllRelations.mockResolvedValue([assetStub.livePhotoStillAsset]); - } else { - mocks.asset.getByIdsWithAllRelations.mockResolvedValue([assetStub.livePhotoMotionAsset]); - } + if (stub) { + mocks.asset.getByIdsWithAllRelationsButStacks.mockResolvedValue(stub); } mocks.job.run.mockResolvedValue(JobStatus.SUCCESS); diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 2f180edd40..fd573d9b97 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -1,10 +1,13 @@ import { BadRequestException, Injectable } from '@nestjs/common'; +import { ClassConstructor } from 'class-transformer'; import { snakeCase } from 'lodash'; import { OnEvent } from 'src/decorators'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto'; import { AssetType, + AssetVisibility, + BootstrapEventPriority, ImmichWorker, JobCommand, JobName, @@ -51,6 +54,8 @@ const asJobItem = (dto: JobCreateDto): JobItem => { @Injectable() export class JobService extends BaseService { + private services: ClassConstructor[] = []; + @OnEvent({ name: 'config.init', workers: [ImmichWorker.MICROSERVICES] }) onConfigInit({ newConfig: config }: ArgOf<'config.init'>) { this.logger.debug(`Updating queue concurrency settings`); @@ -69,6 +74,18 @@ export class JobService extends BaseService { this.onConfigInit({ newConfig: config }); } + @OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.JobService }) + onBootstrap() { + this.jobRepository.setup(this.services); + if (this.worker === ImmichWorker.MICROSERVICES) { + this.jobRepository.startWorkers(); + } + } + + setServices(services: ClassConstructor[]) { + this.services = services; + } + async create(dto: JobCreateDto): Promise { await this.jobRepository.queue(asJobItem(dto)); } @@ -199,11 +216,7 @@ export class JobService extends BaseService { await this.onDone(job); } } catch (error: Error | any) { - this.logger.error( - `Unable to run job handler (${queueName}/${job.name}): ${error}`, - error?.stack, - JSON.stringify(job.data), - ); + await this.eventRepository.emit('job.failed', { job, error }); } finally { this.telemetryRepository.jobs.addToGauge(queueMetric, -1); } @@ -252,17 +265,6 @@ export class JobService extends BaseService { break; } - case JobName.METADATA_EXTRACTION: { - if (item.data.source === 'sidecar-write') { - const [asset] = await this.assetRepository.getByIdsWithAllRelations([item.data.id]); - if (asset) { - this.eventRepository.clientSend('on_asset_update', asset.ownerId, mapAsset(asset)); - } - } - await this.jobRepository.queue({ name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: item.data }); - break; - } - case JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE: { if (item.data.source === 'upload' || item.data.source === 'copy') { await this.jobRepository.queue({ name: JobName.GENERATE_THUMBNAILS, data: item.data }); @@ -284,7 +286,7 @@ export class JobService extends BaseService { break; } - const [asset] = await this.assetRepository.getByIdsWithAllRelations([item.data.id]); + const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([item.data.id]); if (!asset) { this.logger.warn(`Could not find asset ${item.data.id} after generating thumbnails`); break; @@ -297,12 +299,10 @@ export class JobService extends BaseService { if (asset.type === AssetType.VIDEO) { jobs.push({ name: JobName.VIDEO_CONVERSION, data: item.data }); - } else if (asset.livePhotoVideoId) { - jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); } await this.jobRepository.queueAll(jobs); - if (asset.isVisible) { + if (asset.visibility === AssetVisibility.TIMELINE || asset.visibility === AssetVisibility.ARCHIVE) { this.eventRepository.clientSend('on_upload_success', asset.ownerId, mapAsset(asset)); } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index aef02b7244..6b0817dd3b 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -273,7 +273,6 @@ describe(LibraryService.name, () => { mocks.library.get.mockResolvedValue(library); mocks.storage.walk.mockImplementation(async function* generator() {}); - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.external], hasNextPage: false }); mocks.asset.getLibraryAssetCount.mockResolvedValue(1); mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) }); @@ -292,7 +291,6 @@ describe(LibraryService.name, () => { mocks.library.get.mockResolvedValue(library); mocks.storage.walk.mockImplementation(async function* generator() {}); - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.external], hasNextPage: false }); mocks.asset.getLibraryAssetCount.mockResolvedValue(0); mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: BigInt(1) }); @@ -350,7 +348,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.external]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]); mocks.storage.stat.mockRejectedValue(new Error('ENOENT, no such file or directory')); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -371,7 +369,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.external]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]); mocks.storage.stat.mockRejectedValue(new Error('Could not read file')); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -392,7 +390,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.trashedOffline]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]); mocks.storage.stat.mockRejectedValue(new Error('Could not read file')); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -410,7 +408,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.trashedOffline]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]); mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -431,7 +429,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.trashedOffline]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]); mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -451,7 +449,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.trashedOffline]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]); mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -471,7 +469,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.external]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]); mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -489,7 +487,7 @@ describe(LibraryService.name, () => { progressCounter: 0, }; - mocks.asset.getByIds.mockResolvedValue([assetStub.trashedOffline]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]); mocks.storage.stat.mockResolvedValue({ mtime: assetStub.trashedOffline.fileModifiedAt } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); @@ -518,7 +516,7 @@ describe(LibraryService.name, () => { const mtime = new Date(assetStub.external.fileModifiedAt.getDate() + 1); - mocks.asset.getByIds.mockResolvedValue([assetStub.external]); + mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]); mocks.storage.stat.mockResolvedValue({ mtime } as Stats); await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.SUCCESS); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 8cc2cf48ff..2add5f484b 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -18,7 +18,6 @@ import { ValidateLibraryImportPathResponseDto, ValidateLibraryResponseDto, } from 'src/dtos/library.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; import { AssetStatus, AssetType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { AssetSyncResult } from 'src/repositories/library.repository'; @@ -467,7 +466,7 @@ export class LibraryService extends BaseService { @OnJob({ name: JobName.LIBRARY_SYNC_ASSETS, queue: QueueName.LIBRARY }) async handleSyncAssets(job: JobOf): Promise { - const assets = await this.assetRepository.getByIds(job.assetIds); + const assets = await this.assetJobRepository.getForSyncAssets(job.assetIds); const assetIdsToOffline: string[] = []; const trashedAssetIdsToOffline: string[] = []; @@ -561,7 +560,16 @@ export class LibraryService extends BaseService { return JobStatus.SUCCESS; } - private checkExistingAsset(asset: AssetEntity, stat: Stats | null): AssetSyncResult { + private checkExistingAsset( + asset: { + isOffline: boolean; + libraryId: string | null; + originalPath: string; + status: AssetStatus; + fileModifiedAt: Date; + }, + stat: Stats | null, + ): AssetSyncResult { if (!stat) { // File not found on disk or permission error if (asset.isOffline) { diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 8990ad86a6..3b9eafde8f 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,27 +1,28 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; import { Exif } from 'src/database'; -import { AssetMediaSize } from 'src/dtos/asset-media.dto'; import { AssetFileType, AssetPathType, AssetType, AudioCodec, Colorspace, + ExifOrientation, ImageFormat, JobName, JobStatus, + RawExtractedFormat, TranscodeHWAccel, TranscodePolicy, VideoCodec, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { MediaService } from 'src/services/media.service'; import { JobCounts, RawImageInfo } from 'src/types'; import { assetStub } from 'test/fixtures/asset.stub'; import { faceStub } from 'test/fixtures/face.stub'; import { probeStub } from 'test/fixtures/media.stub'; -import { personStub } from 'test/fixtures/person.stub'; +import { personStub, personThumbnailStub } from 'test/fixtures/person.stub'; +import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; describe(MediaService.name, () => { @@ -39,10 +40,6 @@ describe(MediaService.name, () => { describe('handleQueueGenerateThumbnails', () => { it('should queue all assets', async () => { mocks.assetJob.streamForThumbnailJob.mockReturnValue(makeStream([assetStub.image])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); mocks.person.getAll.mockReturnValue(makeStream([personStub.newThumbnail])); mocks.person.getFacesByIds.mockResolvedValue([faceStub.face1]); @@ -68,10 +65,6 @@ describe(MediaService.name, () => { it('should queue trashed assets when force is true', async () => { mocks.assetJob.streamForThumbnailJob.mockReturnValue(makeStream([assetStub.archived])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.trashed], - hasNextPage: false, - }); mocks.person.getAll.mockReturnValue(makeStream()); await sut.handleQueueGenerateThumbnails({ force: true }); @@ -172,7 +165,7 @@ describe(MediaService.name, () => { describe('handleQueueMigration', () => { it('should remove empty directories and queue jobs', async () => { - mocks.asset.getAll.mockResolvedValue({ hasNextPage: false, items: [assetStub.image] }); + mocks.assetJob.streamForMigrationJob.mockReturnValue(makeStream([assetStub.image])); mocks.job.getJobCounts.mockResolvedValue({ active: 1, waiting: 0 } as JobCounts); mocks.person.getAll.mockReturnValue(makeStream([personStub.withName])); @@ -232,17 +225,19 @@ describe(MediaService.name, () => { describe('handleGenerateThumbnails', () => { let rawBuffer: Buffer; let fullsizeBuffer: Buffer; + let extractedBuffer: Buffer; let rawInfo: RawImageInfo; beforeEach(() => { fullsizeBuffer = Buffer.from('embedded image data'); - rawBuffer = Buffer.from('image data'); + rawBuffer = Buffer.from('raw image data'); + extractedBuffer = Buffer.from('embedded image file'); rawInfo = { width: 100, height: 100, channels: 3 }; - mocks.media.decodeImage.mockImplementation((path) => + mocks.media.decodeImage.mockImplementation((input) => Promise.resolve( - path.includes(AssetMediaSize.FULLSIZE) - ? { data: fullsizeBuffer, info: rawInfo as OutputInfo } - : { data: rawBuffer, info: rawInfo as OutputInfo }, + typeof input === 'string' + ? { data: rawBuffer, info: rawInfo as OutputInfo } // string implies original file + : { data: fullsizeBuffer, info: rawInfo as OutputInfo }, // buffer implies embedded image extracted ), ); }); @@ -585,16 +580,15 @@ describe(MediaService.name, () => { }); it('should extract embedded image if enabled and available', async () => { - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const convertedPath = mocks.media.extract.mock.lastCall?.[1].toString(); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(convertedPath, { + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { colorspace: Colorspace.P3, processInvalidImages: false, size: 1440, @@ -602,16 +596,13 @@ describe(MediaService.name, () => { }); it('should resize original image if embedded image is too small', async () => { - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 1000, height: 1000 }); mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const extractedPath = mocks.media.extract.mock.lastCall?.[1].toString(); - expect(extractedPath).toMatch(/-fullsize\.jpeg$/); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.imageDng.originalPath, { colorspace: Colorspace.P3, processInvalidImages: false, @@ -666,38 +657,40 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(2); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); expect(mocks.media.generateThumbhash).toHaveBeenCalledWith( rawBuffer, - expect.objectContaining({ processInvalidImages: true }), + expect.objectContaining({ processInvalidImages: false }), ); expect(mocks.media.getImageDimensions).not.toHaveBeenCalled(); vi.unstubAllEnvs(); }); - it('should generate full-size preview using embedded JPEG from RAW images when extractEmbedded is true', async () => { - mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true }, extractEmbedded: true } }); - mocks.media.extract.mockResolvedValue(true); + it('should extract full-size JPEG preview from RAW', async () => { + mocks.systemMetadata.get.mockResolvedValue({ + image: { fullsize: { enabled: true, format: ImageFormat.WEBP }, extractEmbedded: true }, + }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - const extractedPath = mocks.media.extract.mock.lastCall?.[1].toString(); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); - expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedPath, { + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { colorspace: Colorspace.P3, processInvalidImages: false, + size: 1440, // capped to preview size as fullsize conversion is skipped }); expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(2); @@ -715,9 +708,51 @@ describe(MediaService.name, () => { ); }); + it('should convert full-size WEBP preview from JXL preview of RAW', async () => { + mocks.systemMetadata.get.mockResolvedValue({ + image: { fullsize: { enabled: true, format: ImageFormat.WEBP }, extractEmbedded: true }, + }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JXL }); + mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); + + await sut.handleGenerateThumbnails({ id: assetStub.image.id }); + + expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extractedBuffer, { + colorspace: Colorspace.P3, + processInvalidImages: false, + }); + + expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(3); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + fullsizeBuffer, + { + colorspace: Colorspace.P3, + format: ImageFormat.WEBP, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + ); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + fullsizeBuffer, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + size: 1440, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + ); + }); + it('should generate full-size preview directly from RAW images when extractEmbedded is false', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true }, extractEmbedded: false } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageDng); @@ -757,7 +792,7 @@ describe(MediaService.name, () => { it('should generate full-size preview from non-web-friendly images', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true } } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); // HEIF/HIF image taken by cameras are not web-friendly, only has limited support on Safari. mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageHif); @@ -786,7 +821,7 @@ describe(MediaService.name, () => { it('should skip generating full-size preview for web-friendly images', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true } } }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); @@ -811,7 +846,7 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true, format: ImageFormat.WEBP, quality: 90 } }, }); - mocks.media.extract.mockResolvedValue(true); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.JPEG }); mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); // HEIF/HIF image taken by cameras are not web-friendly, only has limited support on Safari. mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.imageHif); @@ -839,18 +874,373 @@ describe(MediaService.name, () => { }); }); + describe('handleGeneratePersonThumbnail', () => { + it('should skip if machine learning is disabled', async () => { + mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.machineLearningDisabled); + + await expect(sut.handleGeneratePersonThumbnail({ id: 'person-1' })).resolves.toBe(JobStatus.SKIPPED); + expect(mocks.asset.getByIds).not.toHaveBeenCalled(); + expect(mocks.systemMetadata.get).toHaveBeenCalled(); + }); + + it('should skip a person not found', async () => { + await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); + expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); + }); + + it('should skip a person without a face asset id', async () => { + mocks.person.getById.mockResolvedValue(personStub.noThumbnail); + await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); + expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); + }); + + it('should skip a person with face not found', async () => { + await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); + expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); + }); + + it('should generate a thumbnail', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailMiddle); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 1000, height: 1000 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 238, + top: 163, + width: 274, + height: 274, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + expect(mocks.person.update).toHaveBeenCalledWith({ + id: 'person-1', + thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + }); + }); + + it('should use preview path if video', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.videoThumbnail); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 1000, height: 1000 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.previewPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 238, + top: 163, + width: 274, + height: 274, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + expect(mocks.person.update).toHaveBeenCalledWith({ + id: 'person-1', + thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + }); + }); + + it('should generate a thumbnail without going negative', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailStart); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 2160, height: 3840 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailStart.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 0, + top: 85, + width: 510, + height: 510, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + }); + + it('should generate a thumbnail without overflowing', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailEnd); + mocks.person.update.mockResolvedValue(personStub.primaryPerson); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 1000, height: 1000 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailEnd.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 591, + top: 591, + width: 408, + height: 408, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + }); + + it('should handle negative coordinates', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.negativeCoordinate); + mocks.person.update.mockResolvedValue(personStub.primaryPerson); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 4624, height: 3080 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.negativeCoordinate.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 0, + top: 62, + width: 412, + height: 412, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + }); + + it('should handle overflowing coordinate', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.overflowingCoordinate); + mocks.person.update.mockResolvedValue(personStub.primaryPerson); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 4624, height: 3080 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.overflowingCoordinate.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + left: 4485, + top: 94, + width: 138, + height: 138, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + }); + + it('should use embedded preview if enabled and raw image', async () => { + mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.rawEmbeddedThumbnail); + mocks.person.update.mockResolvedValue(personStub.primaryPerson); + mocks.media.generateThumbnail.mockResolvedValue(); + const extracted = Buffer.from(''); + const data = Buffer.from(''); + const info = { width: 2160, height: 3840 } as OutputInfo; + mocks.media.extract.mockResolvedValue({ buffer: extracted, format: RawExtractedFormat.JPEG }); + mocks.media.decodeImage.mockResolvedValue({ data, info }); + mocks.media.getImageDimensions.mockResolvedValue(info); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.extract).toHaveBeenCalledWith(personThumbnailStub.rawEmbeddedThumbnail.originalPath); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(extracted, { + colorspace: Colorspace.P3, + orientation: ExifOrientation.Horizontal, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + data, + { + colorspace: Colorspace.P3, + format: ImageFormat.JPEG, + quality: 80, + crop: { + height: 844, + left: 388, + top: 730, + width: 844, + }, + raw: info, + processInvalidImages: false, + size: 250, + }, + 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + ); + }); + + it('should not use embedded preview if enabled and not raw image', async () => { + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailMiddle); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 2160, height: 3840 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.extract).not.toHaveBeenCalled(); + expect(mocks.media.generateThumbnail).toHaveBeenCalled(); + }); + + it('should not use embedded preview if enabled and raw image if not exists', async () => { + mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.rawEmbeddedThumbnail); + mocks.media.generateThumbnail.mockResolvedValue(); + const data = Buffer.from(''); + const info = { width: 2160, height: 3840 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.extract).toHaveBeenCalledWith(personThumbnailStub.rawEmbeddedThumbnail.originalPath); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.rawEmbeddedThumbnail.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalled(); + }); + + it('should not use embedded preview if enabled and raw image if low resolution', async () => { + mocks.systemMetadata.get.mockResolvedValue({ image: { extractEmbedded: true } }); + mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.rawEmbeddedThumbnail); + mocks.media.generateThumbnail.mockResolvedValue(); + const extracted = Buffer.from(''); + const data = Buffer.from(''); + const info = { width: 1000, height: 1000 } as OutputInfo; + mocks.media.decodeImage.mockResolvedValue({ data, info }); + mocks.media.extract.mockResolvedValue({ buffer: extracted, format: RawExtractedFormat.JPEG }); + mocks.media.getImageDimensions.mockResolvedValue(info); + + await expect(sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id })).resolves.toBe( + JobStatus.SUCCESS, + ); + + expect(mocks.media.extract).toHaveBeenCalledWith(personThumbnailStub.rawEmbeddedThumbnail.originalPath); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.rawEmbeddedThumbnail.originalPath, { + colorspace: Colorspace.P3, + orientation: undefined, + processInvalidImages: false, + }); + expect(mocks.media.generateThumbnail).toHaveBeenCalled(); + }); + }); + describe('handleQueueVideoConversion', () => { it('should queue all video assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.video], - hasNextPage: false, - }); + mocks.assetJob.streamForVideoConversion.mockReturnValue(makeStream([assetStub.video])); mocks.person.getAll.mockReturnValue(makeStream()); await sut.handleQueueVideoConversion({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalledWith({ skip: 0, take: 1000 }, { type: AssetType.VIDEO }); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.VIDEO_CONVERSION, @@ -860,15 +1250,11 @@ describe(MediaService.name, () => { }); it('should queue all video assets without encoded videos', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.video], - hasNextPage: false, - }); + mocks.assetJob.streamForVideoConversion.mockReturnValue(makeStream([assetStub.video])); await sut.handleQueueVideoConversion({}); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO); + expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(void 0); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.VIDEO_CONVERSION, @@ -880,26 +1266,18 @@ describe(MediaService.name, () => { describe('handleVideoConversion', () => { beforeEach(() => { - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(assetStub.video); sut.videoInterfaces = { dri: ['renderD128'], mali: true }; }); it('should skip transcoding if asset not found', async () => { - mocks.asset.getByIds.mockResolvedValue([]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(void 0); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).not.toHaveBeenCalled(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); - it('should skip transcoding if non-video asset', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - await sut.handleVideoConversion({ id: assetStub.image.id }); - expect(mocks.media.probe).not.toHaveBeenCalled(); - expect(mocks.media.transcode).not.toHaveBeenCalled(); - }); - - it('should transcode the longest stream', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); + it('should transcode the highest bitrate video stream', async () => { mocks.logger.isLevelEnabled.mockReturnValue(false); mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams); @@ -913,7 +1291,27 @@ describe(MediaService.name, () => { 'upload/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), - outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:1']), + outputOptions: expect.arrayContaining(['-map 0:1', '-map 0:3']), + twoPass: false, + }), + ); + }); + + it('should transcode the highest bitrate audio stream', async () => { + mocks.logger.isLevelEnabled.mockReturnValue(false); + mocks.media.probe.mockResolvedValue(probeStub.multipleAudioStreams); + + await sut.handleVideoConversion({ id: assetStub.video.id }); + + expect(mocks.media.probe).toHaveBeenCalledWith('/original/path.ext', { countFrames: false }); + expect(mocks.systemMetadata.get).toHaveBeenCalled(); + expect(mocks.storage.mkdirSync).toHaveBeenCalled(); + expect(mocks.media.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.objectContaining({ + inputOptions: expect.any(Array), + outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:2']), twoPass: false, }), ); @@ -921,14 +1319,12 @@ describe(MediaService.name, () => { it('should skip a video without any streams', async () => { mocks.media.probe.mockResolvedValue(probeStub.noVideoStreams); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); it('should skip a video without any height', async () => { mocks.media.probe.mockResolvedValue(probeStub.noHeight); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -936,7 +1332,6 @@ describe(MediaService.name, () => { it('should throw an error if an unknown transcode policy is configured', async () => { mocks.media.probe.mockResolvedValue(probeStub.noAudioStreams); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: 'foo' } } as never as SystemConfig); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); @@ -947,7 +1342,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, accel: TranscodeHWAccel.DISABLED }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValue(new Error('Error transcoding video')); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).resolves.toBe(JobStatus.FAILED); @@ -957,7 +1351,6 @@ describe(MediaService.name, () => { it('should transcode when set to all', async () => { mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1035,7 +1428,6 @@ describe(MediaService.name, () => { it('should scale horizontally when video is horizontal', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1051,7 +1443,6 @@ describe(MediaService.name, () => { it('should scale vertically when video is vertical', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVertical2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1069,7 +1460,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, targetResolution: 'original' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1087,7 +1477,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.ALL, targetResolution: 'original' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1105,7 +1494,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.HEVC, acceptedAudioCodecs: [AudioCodec.AAC] }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1127,7 +1515,6 @@ describe(MediaService.name, () => { acceptedAudioCodecs: [AudioCodec.AAC], }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1149,7 +1536,6 @@ describe(MediaService.name, () => { acceptedAudioCodecs: [AudioCodec.AAC], }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1165,7 +1551,6 @@ describe(MediaService.name, () => { it('should copy audio stream when audio matches target', async () => { mocks.media.probe.mockResolvedValue(probeStub.audioStreamAac); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1180,7 +1565,6 @@ describe(MediaService.name, () => { it('should remux when input is not an accepted container', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamAvi); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1204,7 +1588,6 @@ describe(MediaService.name, () => { it('should not transcode if transcoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1212,7 +1595,6 @@ describe(MediaService.name, () => { it('should not remux when input is not an accepted container and transcoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1220,7 +1602,6 @@ describe(MediaService.name, () => { it('should not transcode if target codec is invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: 'invalid' as any } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1229,7 +1610,7 @@ describe(MediaService.name, () => { const asset = assetStub.hasEncodedVideo; mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.DISABLED } }); - mocks.asset.getByIds.mockResolvedValue([asset]); + mocks.assetJob.getForVideoConversion.mockResolvedValue(asset); await sut.handleVideoConversion({ id: asset.id }); @@ -1243,7 +1624,6 @@ describe(MediaService.name, () => { it('should set max bitrate if above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { maxBitrate: '4500k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1259,7 +1639,6 @@ describe(MediaService.name, () => { it('should default max bitrate to kbps if no unit is provided', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { maxBitrate: '4500' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1275,7 +1654,6 @@ describe(MediaService.name, () => { it('should transcode in two passes for h264/h265 when enabled and max bitrate is above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { twoPass: true, maxBitrate: '4500k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1291,7 +1669,6 @@ describe(MediaService.name, () => { it('should fallback to one pass for h264/h265 if two-pass is enabled but no max bitrate is set', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { twoPass: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1313,7 +1690,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.VP9, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1335,7 +1711,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.VP9, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1351,7 +1726,6 @@ describe(MediaService.name, () => { it('should configure preset for vp9', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.VP9, preset: 'slow' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1367,7 +1741,6 @@ describe(MediaService.name, () => { it('should not configure preset for vp9 if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { preset: 'invalid', targetVideoCodec: VideoCodec.VP9 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1383,7 +1756,6 @@ describe(MediaService.name, () => { it('should configure threads if above 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.VP9, threads: 2 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1399,7 +1771,6 @@ describe(MediaService.name, () => { it('should disable thread pooling for h264 if thread limit is 1', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 1 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1415,7 +1786,6 @@ describe(MediaService.name, () => { it('should omit thread flags for h264 if thread limit is at or below 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 0 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1431,7 +1801,6 @@ describe(MediaService.name, () => { it('should disable thread pooling for hevc if thread limit is 1', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 1, targetVideoCodec: VideoCodec.HEVC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1447,7 +1816,6 @@ describe(MediaService.name, () => { it('should omit thread flags for hevc if thread limit is at or below 0', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { threads: 0, targetVideoCodec: VideoCodec.HEVC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1463,7 +1831,6 @@ describe(MediaService.name, () => { it('should use av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1475,7 +1842,7 @@ describe(MediaService.name, () => { '-movflags faststart', '-fps_mode passthrough', '-map 0:0', - '-map 0:1', + '-map 0:3', '-v verbose', '-vf scale=-2:720', '-preset 12', @@ -1489,7 +1856,6 @@ describe(MediaService.name, () => { it('should map `veryslow` preset to 4 for av1', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, preset: 'veryslow' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1505,7 +1871,6 @@ describe(MediaService.name, () => { it('should set max bitrate for av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, maxBitrate: '2M' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1521,7 +1886,6 @@ describe(MediaService.name, () => { it('should set threads for av1 if specified', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamVp9); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, threads: 4 } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1539,7 +1903,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { targetVideoCodec: VideoCodec.AV1, threads: 4, maxBitrate: '2M' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1561,7 +1924,6 @@ describe(MediaService.name, () => { targetResolution: '1080p', }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1571,7 +1933,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, targetVideoCodec: VideoCodec.VP9 }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1579,7 +1940,6 @@ describe(MediaService.name, () => { it('should fail if hwaccel option is invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: 'invalid' as any } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -1587,7 +1947,6 @@ describe(MediaService.name, () => { it('should set options for nvenc', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1604,7 +1963,7 @@ describe(MediaService.name, () => { '-movflags faststart', '-fps_mode passthrough', '-map 0:0', - '-map 0:1', + '-map 0:3', '-g 256', '-v verbose', '-vf hwupload_cuda,scale_cuda=-2:720:format=nv12', @@ -1625,7 +1984,6 @@ describe(MediaService.name, () => { twoPass: true, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1641,7 +1999,6 @@ describe(MediaService.name, () => { it('should set vbr options for nvenc when max bitrate is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1657,7 +2014,6 @@ describe(MediaService.name, () => { it('should set cq options for nvenc when max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1673,7 +2029,6 @@ describe(MediaService.name, () => { it('should omit preset for nvenc if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1689,7 +2044,6 @@ describe(MediaService.name, () => { it('should ignore two pass for nvenc if max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1707,7 +2061,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1730,7 +2083,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1752,7 +2104,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.NVENC, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1768,7 +2119,6 @@ describe(MediaService.name, () => { it('should set options for qsv', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1784,7 +2134,7 @@ describe(MediaService.name, () => { '-movflags faststart', '-fps_mode passthrough', '-map 0:0', - '-map 0:1', + '-map 0:3', '-bf 7', '-refs 5', '-g 256', @@ -1809,7 +2159,6 @@ describe(MediaService.name, () => { preferredHwDevice: '/dev/dri/renderD128', }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1828,7 +2177,6 @@ describe(MediaService.name, () => { it('should omit preset for qsv if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1849,7 +2197,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, targetVideoCodec: VideoCodec.VP9 }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1869,7 +2216,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: [], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); @@ -1880,7 +2226,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: ['card1', 'renderD129', 'card0', 'renderD128'], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -1901,7 +2246,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -1928,7 +2272,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -1958,7 +2301,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true, preferredHwDevice: 'renderD129' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( @@ -1977,7 +2319,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.QSV, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2000,7 +2341,6 @@ describe(MediaService.name, () => { it('should set options for vaapi', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2016,7 +2356,7 @@ describe(MediaService.name, () => { '-movflags faststart', '-fps_mode passthrough', '-map 0:0', - '-map 0:1', + '-map 0:3', '-g 256', '-v verbose', '-vf hwupload=extra_hw_frames=64,scale_vaapi=-2:720:mode=hq:out_range=pc:format=nv12', @@ -2031,7 +2371,6 @@ describe(MediaService.name, () => { it('should set vbr options for vaapi when max bitrate is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, maxBitrate: '10000k' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2056,7 +2395,6 @@ describe(MediaService.name, () => { it('should set cq options for vaapi when max bitrate is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2081,7 +2419,6 @@ describe(MediaService.name, () => { it('should omit preset for vaapi if invalid', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, preset: 'invalid' } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2101,7 +2438,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: ['card1', 'renderD129', 'card0', 'renderD128'], mali: false }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2123,7 +2459,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, preferredHwDevice: '/dev/dri/renderD128' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2144,7 +2479,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2170,7 +2504,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2194,7 +2527,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2215,7 +2547,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true, preferredHwDevice: 'renderD129' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( @@ -2232,7 +2563,6 @@ describe(MediaService.name, () => { it('should fallback to hw encoding and sw decoding if hw transcoding fails and hw decoding is enabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledTimes(2); @@ -2253,7 +2583,6 @@ describe(MediaService.name, () => { it('should fallback to sw decoding if fallback to sw decoding + hw encoding fails', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2272,7 +2601,6 @@ describe(MediaService.name, () => { it('should fallback to sw transcoding if hw transcoding fails and hw decoding is disabled', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); mocks.media.transcode.mockRejectedValueOnce(new Error('error')); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledTimes(2); @@ -2291,7 +2619,6 @@ describe(MediaService.name, () => { sut.videoInterfaces = { dri: [], mali: true }; mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.VAAPI } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrowError(); expect(mocks.media.transcode).not.toHaveBeenCalled(); }); @@ -2299,7 +2626,6 @@ describe(MediaService.name, () => { it('should set options for rkmpp', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2317,7 +2643,7 @@ describe(MediaService.name, () => { '-movflags faststart', '-fps_mode passthrough', '-map 0:0', - '-map 0:1', + '-map 0:3', '-g 256', '-v verbose', '-vf scale_rkrga=-2:720:format=nv12:afbc=1:async_depth=4', @@ -2340,7 +2666,6 @@ describe(MediaService.name, () => { targetVideoCodec: VideoCodec.HEVC, }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2358,7 +2683,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2376,7 +2700,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2399,7 +2722,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2419,7 +2741,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: false, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2442,7 +2763,6 @@ describe(MediaService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHWAccel.RKMPP, accelDecode: true, crf: 30, maxBitrate: '0' }, }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2462,7 +2782,6 @@ describe(MediaService.name, () => { it('should tonemap when policy is required and video is hdr', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamHDR); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2482,7 +2801,6 @@ describe(MediaService.name, () => { it('should tonemap when policy is optimal and video is hdr', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamHDR); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.OPTIMAL } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2502,7 +2820,6 @@ describe(MediaService.name, () => { it('should transcode when policy is required and video is not yuv420p', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream10Bit); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2518,7 +2835,6 @@ describe(MediaService.name, () => { it('should convert to yuv420p when scaling without tone-mapping', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream4K10Bit); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { transcode: TranscodePolicy.REQUIRED } }); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', @@ -2534,7 +2850,6 @@ describe(MediaService.name, () => { it('should count frames for progress when log level is debug', async () => { mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); mocks.logger.isLevelEnabled.mockReturnValue(true); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); @@ -2557,7 +2872,6 @@ describe(MediaService.name, () => { it('should not count frames for progress when log level is not debug', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.logger.isLevelEnabled.mockReturnValue(false); - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).toHaveBeenCalledWith(assetStub.video.originalPath, { countFrames: false }); @@ -2582,48 +2896,39 @@ describe(MediaService.name, () => { describe('isSRGB', () => { it('should return true for srgb colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'sRGB' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ colorspace: 'sRGB' } as Exif)).toEqual(true); }); it('should return true for srgb profile description', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sRGB v1.31' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ profileDescription: 'sRGB v1.31' } as Exif)).toEqual(true); }); it('should return true for 8-bit image with no colorspace metadata', () => { - const asset = { ...assetStub.image, exifInfo: { bitsPerSample: 8 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ bitsPerSample: 8 } as Exif)).toEqual(true); }); it('should return true for image with no colorspace or bit depth metadata', () => { - const asset = { ...assetStub.image, exifInfo: {} as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({} as Exif)).toEqual(true); }); it('should return false for non-srgb colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'Adobe RGB' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ colorspace: 'Adobe RGB' } as Exif)).toEqual(false); }); it('should return false for non-srgb profile description', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sP3C' } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ profileDescription: 'sP3C' } as Exif)).toEqual(false); }); it('should return false for 16-bit image with no colorspace metadata', () => { - const asset = { ...assetStub.image, exifInfo: { bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(false); + expect(sut.isSRGB({ bitsPerSample: 16 } as Exif)).toEqual(false); }); it('should return true for 16-bit image with sRGB colorspace', () => { - const asset = { ...assetStub.image, exifInfo: { colorspace: 'sRGB', bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ colorspace: 'sRGB', bitsPerSample: 16 } as Exif)).toEqual(true); }); it('should return true for 16-bit image with sRGB profile', () => { - const asset = { ...assetStub.image, exifInfo: { profileDescription: 'sRGB', bitsPerSample: 16 } as Exif }; - expect(sut.isSRGB(asset)).toEqual(true); + expect(sut.isSRGB({ profileDescription: 'sRGB', bitsPerSample: 16 } as Exif)).toEqual(true); }); }); }); diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 59d708772b..bd419f0b34 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; +import { FACE_THUMBNAIL_SIZE, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { StorageCore, ThumbnailPathEntity } from 'src/cores/storage.core'; import { Exif } from 'src/database'; import { OnEvent, OnJob } from 'src/decorators'; @@ -8,6 +8,7 @@ import { AssetFileType, AssetPathType, AssetType, + AssetVisibility, AudioCodec, Colorspace, ImageFormat, @@ -15,6 +16,7 @@ import { JobStatus, LogLevel, QueueName, + RawExtractedFormat, StorageFolder, TranscodeHWAccel, TranscodePolicy, @@ -22,12 +24,14 @@ import { VideoCodec, VideoContainer, } from 'src/enum'; -import { UpsertFileOptions, WithoutProperty } from 'src/repositories/asset.repository'; +import { UpsertFileOptions } from 'src/repositories/asset.repository'; +import { BoundingBox } from 'src/repositories/machine-learning.repository'; import { BaseService } from 'src/services/base.service'; import { AudioStreamInfo, + CropOptions, DecodeToBufferOptions, - GenerateThumbnailOptions, + ImageDimensions, JobItem, JobOf, VideoFormat, @@ -37,7 +41,7 @@ import { import { getAssetFiles } from 'src/utils/asset.util'; import { BaseConfig, ThumbnailConfig } from 'src/utils/media'; import { mimeTypes } from 'src/utils/mime-types'; -import { usePagination } from 'src/utils/pagination'; +import { clamp, isFaceImportEnabled, isFacialRecognitionEnabled } from 'src/utils/misc'; @Injectable() export class MediaService extends BaseService { @@ -51,18 +55,26 @@ export class MediaService extends BaseService { @OnJob({ name: JobName.QUEUE_GENERATE_THUMBNAILS, queue: QueueName.THUMBNAIL_GENERATION }) async handleQueueGenerateThumbnails({ force }: JobOf): Promise { - const thumbJobs: JobItem[] = []; + let jobs: JobItem[] = []; + + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; + for await (const asset of this.assetJobRepository.streamForThumbnailJob(!!force)) { const { previewFile, thumbnailFile } = getAssetFiles(asset.files); if (!previewFile || !thumbnailFile || !asset.thumbhash || force) { - thumbJobs.push({ name: JobName.GENERATE_THUMBNAILS, data: { id: asset.id } }); - continue; + jobs.push({ name: JobName.GENERATE_THUMBNAILS, data: { id: asset.id } }); + } + + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); } } - await this.jobRepository.queueAll(thumbJobs); - const jobs: JobItem[] = []; + await queueAll(); const people = this.personRepository.getAll(force ? undefined : { thumbnailPath: '' }); @@ -77,32 +89,36 @@ export class MediaService extends BaseService { } jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } - await this.jobRepository.queueAll(jobs); + await queueAll(); return JobStatus.SUCCESS; } @OnJob({ name: JobName.QUEUE_MIGRATION, queue: QueueName.MIGRATION }) async handleQueueMigration(): Promise { - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => - this.assetRepository.getAll(pagination), - ); - const { active, waiting } = await this.jobRepository.getJobCounts(QueueName.MIGRATION); if (active === 1 && waiting === 0) { await this.storageCore.removeEmptyDirs(StorageFolder.THUMBNAILS); await this.storageCore.removeEmptyDirs(StorageFolder.ENCODED_VIDEO); } - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } })), - ); + let jobs: JobItem[] = []; + const assets = this.assetJobRepository.streamForMigrationJob(); + for await (const asset of assets) { + jobs.push({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(jobs); + jobs = []; + } } - let jobs: { name: JobName.MIGRATE_PERSON; data: { id: string } }[] = []; + await this.jobRepository.queueAll(jobs); + jobs = []; for await (const person of this.personRepository.getAll()) { jobs.push({ name: JobName.MIGRATE_PERSON, data: { id: person.id } }); @@ -142,7 +158,7 @@ export class MediaService extends BaseService { return JobStatus.FAILED; } - if (!asset.isVisible) { + if (asset.visibility === AssetVisibility.HIDDEN) { this.logger.verbose(`Thumbnail generation skipped for asset ${id}: not visible`); return JobStatus.SKIPPED; } @@ -213,6 +229,29 @@ export class MediaService extends BaseService { return JobStatus.SUCCESS; } + private async extractImage(originalPath: string, minSize: number) { + let extracted = await this.mediaRepository.extract(originalPath); + if (extracted && !(await this.shouldUseExtractedImage(extracted.buffer, minSize))) { + extracted = null; + } + + return extracted; + } + + private async decodeImage(thumbSource: string | Buffer, exifInfo: Exif, targetSize?: number) { + const { image } = await this.getConfig({ withCache: true }); + const colorspace = this.isSRGB(exifInfo) ? Colorspace.SRGB : image.colorspace; + const decodeOptions: DecodeToBufferOptions = { + colorspace, + processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true', + size: targetSize, + orientation: exifInfo.orientation ? Number(exifInfo.orientation) : undefined, + }; + + const { info, data } = await this.mediaRepository.decodeImage(thumbSource, decodeOptions); + return { info, data, colorspace }; + } + private async generateImageThumbnails(asset: { id: string; ownerId: string; @@ -225,73 +264,147 @@ export class MediaService extends BaseService { const thumbnailPath = StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL, image.thumbnail.format); this.storageCore.ensureFolders(previewPath); - const processInvalidImages = process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true'; - const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : image.colorspace; + // Handle embedded preview extraction for RAW files + const extractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); + const extracted = extractEmbedded ? await this.extractImage(asset.originalPath, image.preview.size) : null; + const generateFullsize = image.fullsize.enabled && !mimeTypes.isWebSupportedImage(asset.originalPath); + const convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); - // prevents this extra "enabled" from leaking into fullsizeOptions later - const { enabled: imageFullsizeEnabled, ...imageFullsizeConfig } = image.fullsize; + const { info, data, colorspace } = await this.decodeImage( + extracted ? extracted.buffer : asset.originalPath, + // only specify orientation to extracted images which don't have EXIF orientation data + // or it can double rotate the image + extracted ? asset.exifInfo : { ...asset.exifInfo, orientation: null }, + convertFullsize ? undefined : image.preview.size, + ); - const shouldConvertFullsize = imageFullsizeEnabled && !mimeTypes.isWebSupportedImage(asset.originalFileName); - const shouldExtractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); - const decodeOptions: DecodeToBufferOptions = { colorspace, processInvalidImages, size: image.preview.size }; - - let useExtracted = false; - let decodeInputPath: string = asset.originalPath; - // Converted or extracted image from non-web-supported formats (e.g. RAW) - let fullsizePath: string | undefined; - - if (shouldConvertFullsize) { - // unset size to decode fullsize image - decodeOptions.size = undefined; - fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, image.fullsize.format); - } - - if (shouldExtractEmbedded) { - // For RAW files, try extracting embedded preview first - // Assume extracted image from RAW always in JPEG format, as implied from the `jpgFromRaw` tag name - const extractedPath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, ImageFormat.JPEG); - const didExtract = await this.mediaRepository.extract(asset.originalPath, extractedPath); - useExtracted = didExtract && (await this.shouldUseExtractedImage(extractedPath, image.preview.size)); - - if (useExtracted) { - if (shouldConvertFullsize) { - // skip re-encoding and directly use extracted as fullsize preview - // as usually the extracted image is already heavily compressed, no point doing lossy conversion again - fullsizePath = extractedPath; - } - // use this as origin of preview and thumbnail - decodeInputPath = extractedPath; - if (asset.exifInfo) { - // write essential orientation and colorspace EXIF for correct fullsize preview and subsequent processing - const exif = { orientation: asset.exifInfo.orientation, colorspace: asset.exifInfo.colorspace }; - await this.mediaRepository.writeExif(exif, extractedPath); - } - } - } - - const { info, data } = await this.mediaRepository.decodeImage(decodeInputPath, decodeOptions); - - const thumbnailOptions = { colorspace, processInvalidImages, raw: info }; + // generate final images + const thumbnailOptions = { colorspace, processInvalidImages: false, raw: info }; const promises = [ this.mediaRepository.generateThumbhash(data, thumbnailOptions), this.mediaRepository.generateThumbnail(data, { ...image.thumbnail, ...thumbnailOptions }, thumbnailPath), this.mediaRepository.generateThumbnail(data, { ...image.preview, ...thumbnailOptions }, previewPath), ]; - // did not extract a usable image from RAW - if (fullsizePath && !useExtracted) { - const fullsizeOptions: GenerateThumbnailOptions = { - ...imageFullsizeConfig, - ...thumbnailOptions, - size: undefined, - }; + let fullsizePath: string | undefined; + + if (convertFullsize) { + // convert a new fullsize image from the same source as the thumbnail + fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, image.fullsize.format); + const fullsizeOptions = { format: image.fullsize.format, quality: image.fullsize.quality, ...thumbnailOptions }; promises.push(this.mediaRepository.generateThumbnail(data, fullsizeOptions, fullsizePath)); + } else if (generateFullsize && extracted && extracted.format === RawExtractedFormat.JPEG) { + fullsizePath = StorageCore.getImagePath(asset, AssetPathType.FULLSIZE, extracted.format); + this.storageCore.ensureFolders(fullsizePath); + + // Write the buffer to disk with essential EXIF data + await this.storageRepository.createOrOverwriteFile(fullsizePath, extracted.buffer); + await this.mediaRepository.writeExif( + { + orientation: asset.exifInfo.orientation, + colorspace: asset.exifInfo.colorspace, + }, + fullsizePath, + ); } + const outputs = await Promise.all(promises); return { previewPath, thumbnailPath, fullsizePath, thumbhash: outputs[0] as Buffer }; } + @OnJob({ name: JobName.GENERATE_PERSON_THUMBNAIL, queue: QueueName.THUMBNAIL_GENERATION }) + async handleGeneratePersonThumbnail({ id }: JobOf): Promise { + const { machineLearning, metadata, image } = await this.getConfig({ withCache: true }); + if (!isFacialRecognitionEnabled(machineLearning) && !isFaceImportEnabled(metadata)) { + return JobStatus.SKIPPED; + } + + const data = await this.personRepository.getDataForThumbnailGenerationJob(id); + if (!data) { + this.logger.error(`Could not generate person thumbnail for ${id}: missing data`); + return JobStatus.FAILED; + } + + const { ownerId, x1, y1, x2, y2, oldWidth, oldHeight, exifOrientation, previewPath, originalPath } = data; + let inputImage: string | Buffer; + if (data.type === AssetType.VIDEO) { + if (!previewPath) { + this.logger.error(`Could not generate person thumbnail for video ${id}: missing preview path`); + return JobStatus.FAILED; + } + inputImage = previewPath; + } else if (image.extractEmbedded && mimeTypes.isRaw(originalPath)) { + const extracted = await this.extractImage(originalPath, image.preview.size); + inputImage = extracted ? extracted.buffer : originalPath; + } else { + inputImage = originalPath; + } + + const { data: decodedImage, info } = await this.mediaRepository.decodeImage(inputImage, { + colorspace: image.colorspace, + processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true', + // if this is an extracted image, it may not have orientation metadata + orientation: Buffer.isBuffer(inputImage) && exifOrientation ? Number(exifOrientation) : undefined, + }); + + const thumbnailPath = StorageCore.getPersonThumbnailPath({ id, ownerId }); + this.storageCore.ensureFolders(thumbnailPath); + + const thumbnailOptions = { + colorspace: image.colorspace, + format: ImageFormat.JPEG, + raw: info, + quality: image.thumbnail.quality, + crop: this.getCrop( + { old: { width: oldWidth, height: oldHeight }, new: { width: info.width, height: info.height } }, + { x1, y1, x2, y2 }, + ), + processInvalidImages: false, + size: FACE_THUMBNAIL_SIZE, + }; + + await this.mediaRepository.generateThumbnail(decodedImage, thumbnailOptions, thumbnailPath); + await this.personRepository.update({ id, thumbnailPath }); + + return JobStatus.SUCCESS; + } + + private getCrop(dims: { old: ImageDimensions; new: ImageDimensions }, { x1, y1, x2, y2 }: BoundingBox): CropOptions { + // face bounding boxes can spill outside the image dimensions + const clampedX1 = clamp(x1, 0, dims.old.width); + const clampedY1 = clamp(y1, 0, dims.old.height); + const clampedX2 = clamp(x2, 0, dims.old.width); + const clampedY2 = clamp(y2, 0, dims.old.height); + + const widthScale = dims.new.width / dims.old.width; + const heightScale = dims.new.height / dims.old.height; + + const halfWidth = (widthScale * (clampedX2 - clampedX1)) / 2; + const halfHeight = (heightScale * (clampedY2 - clampedY1)) / 2; + + const middleX = Math.round(widthScale * clampedX1 + halfWidth); + const middleY = Math.round(heightScale * clampedY1 + halfHeight); + + // zoom out 10% + const targetHalfSize = Math.floor(Math.max(halfWidth, halfHeight) * 1.1); + + // get the longest distance from the center of the image without overflowing + const newHalfSize = Math.min( + middleX - Math.max(0, middleX - targetHalfSize), + middleY - Math.max(0, middleY - targetHalfSize), + Math.min(dims.new.width - 1, middleX + targetHalfSize) - middleX, + Math.min(dims.new.height - 1, middleY + targetHalfSize) - middleY, + ); + + return { + left: middleX - newHalfSize, + top: middleY - newHalfSize, + width: newHalfSize * 2, + height: newHalfSize * 2, + }; + } + private async generateVideoThumbnails(asset: ThumbnailPathEntity & { originalPath: string }) { const { image, ffmpeg } = await this.getConfig({ withCache: true }); const previewPath = StorageCore.getImagePath(asset, AssetPathType.PREVIEW, image.preview.format); @@ -330,25 +443,25 @@ export class MediaService extends BaseService { async handleQueueVideoConversion(job: JobOf): Promise { const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { type: AssetType.VIDEO }) - : this.assetRepository.getWithout(pagination, WithoutProperty.ENCODED_VIDEO); - }); + let queue: { name: JobName.VIDEO_CONVERSION; data: { id: string } }[] = []; + for await (const asset of this.assetJobRepository.streamForVideoConversion(force)) { + queue.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } })), - ); + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); + return JobStatus.SUCCESS; } @OnJob({ name: JobName.VIDEO_CONVERSION, queue: QueueName.VIDEO_CONVERSION }) async handleVideoConversion({ id }: JobOf): Promise { - const [asset] = await this.assetRepository.getByIds([id]); - if (!asset || asset.type !== AssetType.VIDEO) { + const asset = await this.assetJobRepository.getForVideoConversion(id); + if (!asset) { return JobStatus.FAILED; } @@ -432,7 +545,7 @@ export class MediaService extends BaseService { private getMainStream(streams: T[]): T { return streams .filter((stream) => stream.codecName !== 'unknown') - .sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0]; + .sort((stream1, stream2) => stream2.bitrate - stream1.bitrate)[0]; } private getTranscodeTarget( @@ -521,8 +634,7 @@ export class MediaService extends BaseService { return name !== VideoContainer.MP4 && !ffmpegConfig.acceptedContainers.includes(name); } - isSRGB(asset: { exifInfo: Exif }): boolean { - const { colorspace, profileDescription, bitsPerSample } = asset.exifInfo; + isSRGB({ colorspace, profileDescription, bitsPerSample }: Exif): boolean { if (colorspace || profileDescription) { return [colorspace, profileDescription].some((s) => s?.toLowerCase().includes('srgb')); } else if (bitsPerSample) { @@ -550,10 +662,9 @@ export class MediaService extends BaseService { } } - private async shouldUseExtractedImage(extractedPath: string, targetSize: number) { - const { width, height } = await this.mediaRepository.getImageDimensions(extractedPath); + private async shouldUseExtractedImage(extractedPathOrBuffer: string | Buffer, targetSize: number) { + const { width, height } = await this.mediaRepository.getImageDimensions(extractedPathOrBuffer); const extractedSize = Math.min(width, height); - return extractedSize >= targetSize; } diff --git a/server/src/services/memory.service.spec.ts b/server/src/services/memory.service.spec.ts index 7ce8b1ab46..d55c58d9af 100644 --- a/server/src/services/memory.service.spec.ts +++ b/server/src/services/memory.service.spec.ts @@ -15,6 +15,14 @@ describe(MemoryService.name, () => { expect(sut).toBeDefined(); }); + describe('onMemoryCleanup', () => { + it('should clean up memories', async () => { + mocks.memory.cleanup.mockResolvedValue([]); + await sut.onMemoriesCleanup(); + expect(mocks.memory.cleanup).toHaveBeenCalled(); + }); + }); + describe('search', () => { it('should search memories', async () => { const [userId] = newUuids(); diff --git a/server/src/services/memory.service.ts b/server/src/services/memory.service.ts index 3d3d10540b..1ccd311790 100644 --- a/server/src/services/memory.service.ts +++ b/server/src/services/memory.service.ts @@ -4,9 +4,8 @@ import { OnJob } from 'src/decorators'; import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { MemoryCreateDto, MemoryResponseDto, MemorySearchDto, MemoryUpdateDto, mapMemory } from 'src/dtos/memory.dto'; -import { JobName, MemoryType, Permission, QueueName, SystemMetadataKey } from 'src/enum'; +import { DatabaseLock, JobName, MemoryType, Permission, QueueName, SystemMetadataKey } from 'src/enum'; import { BaseService } from 'src/services/base.service'; -import { OnThisDayData } from 'src/types'; import { addAssets, getMyPartnerIds, removeAssets } from 'src/utils/asset.util'; const DAYS = 3; @@ -16,55 +15,61 @@ export class MemoryService extends BaseService { @OnJob({ name: JobName.MEMORIES_CREATE, queue: QueueName.BACKGROUND_TASK }) async onMemoriesCreate() { const users = await this.userRepository.getList({ withDeleted: false }); - const userMap: Record = {}; - for (const user of users) { - const partnerIds = await getMyPartnerIds({ - userId: user.id, - repository: this.partnerRepository, - timelineEnabled: true, - }); - userMap[user.id] = [user.id, ...partnerIds]; - } + const usersIds = await Promise.all( + users.map((user) => + getMyPartnerIds({ + userId: user.id, + repository: this.partnerRepository, + timelineEnabled: true, + }), + ), + ); - const start = DateTime.utc().startOf('day').minus({ days: DAYS }); + await this.databaseRepository.withLock(DatabaseLock.MemoryCreation, async () => { + const state = await this.systemMetadataRepository.get(SystemMetadataKey.MEMORIES_STATE); + const start = DateTime.utc().startOf('day').minus({ days: DAYS }); + const lastOnThisDayDate = state?.lastOnThisDayDate ? DateTime.fromISO(state.lastOnThisDayDate) : start; - const state = await this.systemMetadataRepository.get(SystemMetadataKey.MEMORIES_STATE); - const lastOnThisDayDate = state?.lastOnThisDayDate ? DateTime.fromISO(state.lastOnThisDayDate) : start; - - // generate a memory +/- X days from today - for (let i = 0; i <= DAYS * 2; i++) { - const target = start.plus({ days: i }); - if (lastOnThisDayDate >= target) { - continue; - } - - const showAt = target.startOf('day').toISO(); - const hideAt = target.endOf('day').toISO(); - - for (const [userId, userIds] of Object.entries(userMap)) { - const memories = await this.assetRepository.getByDayOfYear(userIds, target); - - for (const { year, assets } of memories) { - const data: OnThisDayData = { year }; - await this.memoryRepository.create( - { - ownerId: userId, - type: MemoryType.ON_THIS_DAY, - data, - memoryAt: target.set({ year }).toISO(), - showAt, - hideAt, - }, - new Set(assets.map(({ id }) => id)), - ); + // generate a memory +/- X days from today + for (let i = 0; i <= DAYS * 2; i++) { + const target = start.plus({ days: i }); + if (lastOnThisDayDate >= target) { + continue; } - } - await this.systemMetadataRepository.set(SystemMetadataKey.MEMORIES_STATE, { - ...state, - lastOnThisDayDate: target.toISO(), - }); - } + try { + await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target))); + } catch (error) { + this.logger.error(`Failed to create memories for ${target.toISO()}`, error); + } + // update system metadata even when there is an error to minimize the chance of duplicates + await this.systemMetadataRepository.set(SystemMetadataKey.MEMORIES_STATE, { + ...state, + lastOnThisDayDate: target.toISO(), + }); + } + }); + } + + private async createOnThisDayMemories(ownerId: string, userIds: string[], target: DateTime) { + const showAt = target.startOf('day').toISO(); + const hideAt = target.endOf('day').toISO(); + const memories = await this.assetRepository.getByDayOfYear([ownerId, ...userIds], target); + await Promise.all( + memories.map(({ year, assets }) => + this.memoryRepository.create( + { + ownerId, + type: MemoryType.ON_THIS_DAY, + data: { year }, + memoryAt: target.set({ year }).toISO()!, + showAt, + hideAt, + }, + new Set(assets.map(({ id }) => id)), + ), + ), + ); } @OnJob({ name: JobName.MEMORIES_CLEANUP, queue: QueueName.BACKGROUND_TASK }) diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index ca1277a8c8..7b2cba1250 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -3,9 +3,8 @@ import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; import { defaults } from 'src/config'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { AssetType, ExifOrientation, ImmichWorker, JobName, JobStatus, SourceType } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; +import { MapAsset } from 'src/dtos/asset-response.dto'; +import { AssetType, AssetVisibility, ExifOrientation, ImmichWorker, JobName, JobStatus, SourceType } from 'src/enum'; import { ImmichTags } from 'src/repositories/metadata.repository'; import { MetadataService } from 'src/services/metadata.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -14,23 +13,20 @@ import { probeStub } from 'test/fixtures/media.stub'; import { personStub } from 'test/fixtures/person.stub'; import { tagStub } from 'test/fixtures/tag.stub'; import { factory } from 'test/small.factory'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -const makeFaceTags = (face: Partial<{ Name: string }> = {}) => ({ +const makeFaceTags = (face: Partial<{ Name: string }> = {}, orientation?: ImmichTags['Orientation']) => ({ + Orientation: orientation, RegionInfo: { - AppliedToDimensions: { - W: 100, - H: 100, - Unit: 'normalized', - }, + AppliedToDimensions: { W: 1000, H: 100, Unit: 'pixel' }, RegionList: [ { Type: 'face', Area: { - X: 0.05, - Y: 0.05, - W: 0.1, - H: 0.1, + X: 0.1, + Y: 0.4, + W: 0.2, + H: 0.4, Unit: 'normalized', }, ...face, @@ -104,10 +100,10 @@ describe(MetadataService.name, () => { describe('handleQueueMetadataExtraction', () => { it('should queue metadata extraction for all assets without exif values', async () => { - mocks.asset.getWithout.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForMetadataExtraction.mockReturnValue(makeStream([assetStub.image])); await expect(sut.handleQueueMetadataExtraction({ force: false })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.asset.getWithout).toHaveBeenCalled(); + expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.METADATA_EXTRACTION, @@ -117,10 +113,10 @@ describe(MetadataService.name, () => { }); it('should queue metadata extraction for all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForMetadataExtraction.mockReturnValue(makeStream([assetStub.image])); await expect(sut.handleQueueMetadataExtraction({ force: true })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.METADATA_EXTRACTION, @@ -144,7 +140,8 @@ describe(MetadataService.name, () => { it('should handle an asset that could not be found', async () => { mocks.assetJob.getForMetadataExtraction.mockResolvedValue(void 0); - await expect(sut.handleMetadataExtraction({ id: assetStub.image.id })).resolves.toBe(JobStatus.FAILED); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); expect(mocks.assetJob.getForMetadataExtraction).toHaveBeenCalledWith(assetStub.image.id); expect(mocks.asset.upsertExif).not.toHaveBeenCalled(); @@ -507,7 +504,10 @@ describe(MetadataService.name, () => { }); it('should not apply motion photos if asset is video', async () => { - mocks.assetJob.getForMetadataExtraction.mockResolvedValue({ ...assetStub.livePhotoMotionAsset, isVisible: true }); + mocks.assetJob.getForMetadataExtraction.mockResolvedValue({ + ...assetStub.livePhotoMotionAsset, + visibility: AssetVisibility.TIMELINE, + }); mocks.media.probe.mockResolvedValue(probeStub.matroskaContainer); await sut.handleMetadataExtraction({ id: assetStub.livePhotoMotionAsset.id }); @@ -516,7 +516,7 @@ describe(MetadataService.name, () => { expect(mocks.job.queue).not.toHaveBeenCalled(); expect(mocks.job.queueAll).not.toHaveBeenCalled(); expect(mocks.asset.update).not.toHaveBeenCalledWith( - expect.objectContaining({ assetType: AssetType.VIDEO, isVisible: false }), + expect.objectContaining({ assetType: AssetType.VIDEO, visibility: AssetVisibility.HIDDEN }), ); }); @@ -527,7 +527,7 @@ describe(MetadataService.name, () => { ContainerDirectory: [{ Foo: 100 }], }); - await expect(sut.handleMetadataExtraction({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); + await sut.handleMetadataExtraction({ id: assetStub.image.id }); }); it('should extract the correct video orientation', async () => { @@ -549,7 +549,6 @@ describe(MetadataService.name, () => { livePhotoVideoId: null, libraryId: null, }); - mocks.asset.getByIds.mockResolvedValue([{ ...assetStub.livePhotoWithOriginalFileName, livePhotoVideoId: null }]); mocks.storage.stat.mockResolvedValue({ size: 123_456, mtime: assetStub.livePhotoWithOriginalFileName.fileModifiedAt, @@ -584,7 +583,7 @@ describe(MetadataService.name, () => { fileCreatedAt: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, fileModifiedAt: assetStub.livePhotoWithOriginalFileName.fileModifiedAt, id: fileStub.livePhotoMotion.uuid, - isVisible: false, + visibility: AssetVisibility.HIDDEN, libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', @@ -599,6 +598,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should extract the EmbeddedVideo tag from Samsung JPEG motion photos', async () => { @@ -638,7 +641,7 @@ describe(MetadataService.name, () => { fileCreatedAt: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, fileModifiedAt: assetStub.livePhotoWithOriginalFileName.fileModifiedAt, id: fileStub.livePhotoMotion.uuid, - isVisible: false, + visibility: AssetVisibility.HIDDEN, libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', @@ -653,6 +656,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should extract the motion photo video from the XMP directory entry ', async () => { @@ -692,7 +699,7 @@ describe(MetadataService.name, () => { fileCreatedAt: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, fileModifiedAt: assetStub.livePhotoWithOriginalFileName.fileModifiedAt, id: fileStub.livePhotoMotion.uuid, - isVisible: false, + visibility: AssetVisibility.HIDDEN, libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', @@ -707,6 +714,10 @@ describe(MetadataService.name, () => { livePhotoVideoId: fileStub.livePhotoMotion.uuid, }); expect(mocks.asset.update).toHaveBeenCalledTimes(3); + expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({ + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.livePhotoMotionAsset.id }, + }); }); it('should delete old motion photo video assets if they do not match what is extracted', async () => { @@ -719,7 +730,7 @@ describe(MetadataService.name, () => { }); mocks.crypto.hashSha1.mockReturnValue(randomBytes(512)); mocks.asset.create.mockImplementation( - (asset) => Promise.resolve({ ...assetStub.livePhotoMotionAsset, ...asset }) as Promise, + (asset) => Promise.resolve({ ...assetStub.livePhotoMotionAsset, ...asset }) as Promise, ); const video = randomBytes(512); mocks.storage.readFile.mockResolvedValue(video); @@ -765,14 +776,17 @@ describe(MetadataService.name, () => { MicroVideoOffset: 1, }); mocks.crypto.hashSha1.mockReturnValue(randomBytes(512)); - mocks.asset.getByChecksum.mockResolvedValue({ ...assetStub.livePhotoMotionAsset, isVisible: true }); + mocks.asset.getByChecksum.mockResolvedValue({ + ...assetStub.livePhotoMotionAsset, + visibility: AssetVisibility.TIMELINE, + }); const video = randomBytes(512); mocks.storage.readFile.mockResolvedValue(video); await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, - isVisible: false, + visibility: AssetVisibility.HIDDEN, }); expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.livePhotoStillAsset.id, @@ -1087,11 +1101,11 @@ describe(MetadataService.name, () => { assetId: assetStub.primaryImage.id, personId: 'random-uuid', imageHeight: 100, - imageWidth: 100, + imageWidth: 1000, boundingBoxX1: 0, - boundingBoxX2: 10, - boundingBoxY1: 0, - boundingBoxY2: 10, + boundingBoxX2: 200, + boundingBoxY1: 20, + boundingBoxY2: 60, sourceType: SourceType.EXIF, }, ], @@ -1126,11 +1140,11 @@ describe(MetadataService.name, () => { assetId: assetStub.primaryImage.id, personId: personStub.withName.id, imageHeight: 100, - imageWidth: 100, + imageWidth: 1000, boundingBoxX1: 0, - boundingBoxX2: 10, - boundingBoxY1: 0, - boundingBoxY2: 10, + boundingBoxX2: 200, + boundingBoxY1: 20, + boundingBoxY2: 60, sourceType: SourceType.EXIF, }, ], @@ -1140,6 +1154,104 @@ describe(MetadataService.name, () => { expect(mocks.job.queueAll).not.toHaveBeenCalledWith(); }); + describe('handleFaceTagOrientation', () => { + const orientationTests = [ + { + description: 'undefined', + orientation: undefined, + expected: { imgW: 1000, imgH: 100, x1: 0, x2: 200, y1: 20, y2: 60 }, + }, + { + description: 'Horizontal = 1', + orientation: ExifOrientation.Horizontal, + expected: { imgW: 1000, imgH: 100, x1: 0, x2: 200, y1: 20, y2: 60 }, + }, + { + description: 'MirrorHorizontal = 2', + orientation: ExifOrientation.MirrorHorizontal, + expected: { imgW: 1000, imgH: 100, x1: 800, x2: 1000, y1: 20, y2: 60 }, + }, + { + description: 'Rotate180 = 3', + orientation: ExifOrientation.Rotate180, + expected: { imgW: 1000, imgH: 100, x1: 800, x2: 1000, y1: 40, y2: 80 }, + }, + { + description: 'MirrorVertical = 4', + orientation: ExifOrientation.MirrorVertical, + expected: { imgW: 1000, imgH: 100, x1: 0, x2: 200, y1: 40, y2: 80 }, + }, + { + description: 'MirrorHorizontalRotate270CW = 5', + orientation: ExifOrientation.MirrorHorizontalRotate270CW, + expected: { imgW: 100, imgH: 1000, x1: 20, x2: 60, y1: 0, y2: 200 }, + }, + { + description: 'Rotate90CW = 6', + orientation: ExifOrientation.Rotate90CW, + expected: { imgW: 100, imgH: 1000, x1: 40, x2: 80, y1: 0, y2: 200 }, + }, + { + description: 'MirrorHorizontalRotate90CW = 7', + orientation: ExifOrientation.MirrorHorizontalRotate90CW, + expected: { imgW: 100, imgH: 1000, x1: 40, x2: 80, y1: 800, y2: 1000 }, + }, + { + description: 'Rotate270CW = 8', + orientation: ExifOrientation.Rotate270CW, + expected: { imgW: 100, imgH: 1000, x1: 20, x2: 60, y1: 800, y2: 1000 }, + }, + ]; + + it.each(orientationTests)( + 'should transform RegionInfo geometry according to exif orientation $description', + async ({ orientation, expected }) => { + const { imgW, imgH, x1, x2, y1, y2 } = expected; + + mocks.assetJob.getForMetadataExtraction.mockResolvedValue(assetStub.primaryImage); + mocks.systemMetadata.get.mockResolvedValue({ metadata: { faces: { import: true } } }); + mockReadTags(makeFaceTags({ Name: personStub.withName.name }, orientation)); + mocks.person.getDistinctNames.mockResolvedValue([]); + mocks.person.createAll.mockResolvedValue([personStub.withName.id]); + mocks.person.update.mockResolvedValue(personStub.withName); + await sut.handleMetadataExtraction({ id: assetStub.primaryImage.id }); + expect(mocks.assetJob.getForMetadataExtraction).toHaveBeenCalledWith(assetStub.primaryImage.id); + expect(mocks.person.getDistinctNames).toHaveBeenCalledWith(assetStub.primaryImage.ownerId, { + withHidden: true, + }); + expect(mocks.person.createAll).toHaveBeenCalledWith([ + expect.objectContaining({ name: personStub.withName.name }), + ]); + expect(mocks.person.refreshFaces).toHaveBeenCalledWith( + [ + { + id: 'random-uuid', + assetId: assetStub.primaryImage.id, + personId: 'random-uuid', + imageWidth: imgW, + imageHeight: imgH, + boundingBoxX1: x1, + boundingBoxX2: x2, + boundingBoxY1: y1, + boundingBoxY2: y2, + sourceType: SourceType.EXIF, + }, + ], + [], + ); + expect(mocks.person.updateAll).toHaveBeenCalledWith([ + { id: 'random-uuid', ownerId: 'admin-id', faceAssetId: 'random-uuid' }, + ]); + expect(mocks.job.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_PERSON_THUMBNAIL, + data: { id: personStub.withName.id }, + }, + ]); + }, + ); + }); + it('should handle invalid modify date', async () => { mocks.assetJob.getForMetadataExtraction.mockResolvedValue(assetStub.image); mockReadTags({ ModifyDate: '00:00:00.000' }); @@ -1191,12 +1303,14 @@ describe(MetadataService.name, () => { it('should handle livePhotoCID not set', async () => { mocks.assetJob.getForMetadataExtraction.mockResolvedValue(assetStub.image); - await expect(sut.handleMetadataExtraction({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); + await sut.handleMetadataExtraction({ id: assetStub.image.id }); expect(mocks.assetJob.getForMetadataExtraction).toHaveBeenCalledWith(assetStub.image.id); expect(mocks.asset.findLivePhotoMatch).not.toHaveBeenCalled(); - expect(mocks.asset.update).not.toHaveBeenCalledWith(expect.objectContaining({ isVisible: false })); - expect(mocks.album.removeAsset).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalledWith( + expect.objectContaining({ visibility: AssetVisibility.HIDDEN }), + ); + expect(mocks.album.removeAssetsFromAll).not.toHaveBeenCalled(); }); it('should handle not finding a match', async () => { @@ -1204,9 +1318,7 @@ describe(MetadataService.name, () => { mocks.assetJob.getForMetadataExtraction.mockResolvedValue(assetStub.livePhotoMotionAsset); mockReadTags({ ContentIdentifier: 'CID' }); - await expect(sut.handleMetadataExtraction({ id: assetStub.livePhotoMotionAsset.id })).resolves.toBe( - JobStatus.SUCCESS, - ); + await sut.handleMetadataExtraction({ id: assetStub.livePhotoMotionAsset.id }); expect(mocks.assetJob.getForMetadataExtraction).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.id); expect(mocks.asset.findLivePhotoMatch).toHaveBeenCalledWith({ @@ -1216,8 +1328,10 @@ describe(MetadataService.name, () => { libraryId: null, type: AssetType.IMAGE, }); - expect(mocks.asset.update).not.toHaveBeenCalledWith(expect.objectContaining({ isVisible: false })); - expect(mocks.album.removeAsset).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalledWith( + expect.objectContaining({ visibility: AssetVisibility.HIDDEN }), + ); + expect(mocks.album.removeAssetsFromAll).not.toHaveBeenCalled(); }); it('should link photo and video', async () => { @@ -1225,9 +1339,7 @@ describe(MetadataService.name, () => { mocks.asset.findLivePhotoMatch.mockResolvedValue(assetStub.livePhotoMotionAsset); mockReadTags({ ContentIdentifier: 'CID' }); - await expect(sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id })).resolves.toBe( - JobStatus.SUCCESS, - ); + await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(mocks.assetJob.getForMetadataExtraction).toHaveBeenCalledWith(assetStub.livePhotoStillAsset.id); expect(mocks.asset.findLivePhotoMatch).toHaveBeenCalledWith({ @@ -1240,8 +1352,11 @@ describe(MetadataService.name, () => { id: assetStub.livePhotoStillAsset.id, livePhotoVideoId: assetStub.livePhotoMotionAsset.id, }); - expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.livePhotoMotionAsset.id, isVisible: false }); - expect(mocks.album.removeAsset).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.id); + expect(mocks.asset.update).toHaveBeenCalledWith({ + id: assetStub.livePhotoMotionAsset.id, + visibility: AssetVisibility.HIDDEN, + }); + expect(mocks.album.removeAssetsFromAll).toHaveBeenCalledWith([assetStub.livePhotoMotionAsset.id]); }); it('should notify clients on live photo link', async () => { @@ -1251,9 +1366,7 @@ describe(MetadataService.name, () => { mocks.asset.findLivePhotoMatch.mockResolvedValue(assetStub.livePhotoMotionAsset); mockReadTags({ ContentIdentifier: 'CID' }); - await expect(sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id })).resolves.toBe( - JobStatus.SUCCESS, - ); + await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(mocks.event.emit).toHaveBeenCalledWith('asset.hide', { userId: assetStub.livePhotoMotionAsset.ownerId, @@ -1269,10 +1382,12 @@ describe(MetadataService.name, () => { mocks.asset.findLivePhotoMatch.mockResolvedValue(assetStub.livePhotoMotionAsset); mockReadTags({ ContentIdentifier: 'CID' }); - await expect(sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id })).resolves.toBe( - JobStatus.SUCCESS, - ); + await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); + expect(mocks.event.emit).toHaveBeenCalledWith('asset.metadataExtracted', { + assetId: assetStub.livePhotoStillAsset.id, + userId: assetStub.livePhotoStillAsset.ownerId, + }); expect(mocks.asset.findLivePhotoMatch).toHaveBeenCalledWith({ ownerId: 'user-id', otherAssetId: 'live-photo-still-asset', @@ -1335,12 +1450,11 @@ describe(MetadataService.name, () => { describe('handleQueueSidecar', () => { it('should queue assets with sidecar files', async () => { - mocks.asset.getAll.mockResolvedValue({ items: [assetStub.sidecar], hasNextPage: false }); + mocks.assetJob.streamForSidecar.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSidecar({ force: true }); + expect(mocks.assetJob.streamForSidecar).toHaveBeenCalledWith(true); - expect(mocks.asset.getAll).toHaveBeenCalledWith({ take: 1000, skip: 0 }); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SIDECAR_SYNC, @@ -1350,12 +1464,11 @@ describe(MetadataService.name, () => { }); it('should queue assets without sidecar files', async () => { - mocks.asset.getWithout.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); + mocks.assetJob.streamForSidecar.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueSidecar({ force: false }); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithoutProperty.SIDECAR); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForSidecar).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SIDECAR_DISCOVERY, @@ -1394,7 +1507,7 @@ describe(MetadataService.name, () => { }); it('should set sidecar path if exists (sidecar named photo.xmp)', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecarWithoutExt]); + mocks.asset.getByIds.mockResolvedValue([assetStub.sidecarWithoutExt as any]); mocks.storage.checkFileExists.mockResolvedValueOnce(false); mocks.storage.checkFileExists.mockResolvedValueOnce(true); @@ -1446,7 +1559,7 @@ describe(MetadataService.name, () => { describe('handleSidecarDiscovery', () => { it('should skip hidden assets', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]); + mocks.asset.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset as any]); await sut.handleSidecarDiscovery({ id: assetStub.livePhotoMotionAsset.id }); expect(mocks.storage.checkFileExists).not.toHaveBeenCalled(); }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index ab62c38ed0..109f5f6936 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -14,6 +14,7 @@ import { AssetFaces, Exif, Person } from 'src/db'; import { OnEvent, OnJob } from 'src/decorators'; import { AssetType, + AssetVisibility, DatabaseLock, ExifOrientation, ImmichWorker, @@ -22,14 +23,12 @@ import { QueueName, SourceType, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { ArgOf } from 'src/repositories/event.repository'; import { ReverseGeocodeResult } from 'src/repositories/map.repository'; import { ImmichTags } from 'src/repositories/metadata.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { isFaceImportEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; import { upsertTags } from 'src/utils/tag'; /** look for a date from these tags (in order) */ @@ -158,8 +157,8 @@ export class MetadataService extends BaseService { const [photoAsset, motionAsset] = asset.type === AssetType.IMAGE ? [asset, match] : [match, asset]; await Promise.all([ this.assetRepository.update({ id: photoAsset.id, livePhotoVideoId: motionAsset.id }), - this.assetRepository.update({ id: motionAsset.id, isVisible: false }), - this.albumRepository.removeAsset(motionAsset.id), + this.assetRepository.update({ id: motionAsset.id, visibility: AssetVisibility.HIDDEN }), + this.albumRepository.removeAssetsFromAll([motionAsset.id]), ]); await this.eventRepository.emit('asset.hide', { assetId: motionAsset.id, userId: motionAsset.ownerId }); @@ -168,30 +167,30 @@ export class MetadataService extends BaseService { @OnJob({ name: JobName.QUEUE_METADATA_EXTRACTION, queue: QueueName.METADATA_EXTRACTION }) async handleQueueMetadataExtraction(job: JobOf): Promise { const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination) - : this.assetRepository.getWithout(pagination, WithoutProperty.EXIF); - }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } })), - ); + let queue: { name: JobName.METADATA_EXTRACTION; data: { id: string } }[] = []; + for await (const asset of this.assetJobRepository.streamForMetadataExtraction(force)) { + queue.push({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } }); + + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); return JobStatus.SUCCESS; } @OnJob({ name: JobName.METADATA_EXTRACTION, queue: QueueName.METADATA_EXTRACTION }) - async handleMetadataExtraction(data: JobOf): Promise { + async handleMetadataExtraction(data: JobOf) { const [{ metadata, reverseGeocoding }, asset] = await Promise.all([ this.getConfig({ withCache: true }), this.assetJobRepository.getForMetadataExtraction(data.id), ]); if (!asset) { - return JobStatus.FAILED; + return; } const [exifTags, stats] = await Promise.all([ @@ -271,7 +270,7 @@ export class MetadataService extends BaseService { ]; if (this.isMotionPhoto(asset, exifTags)) { - promises.push(this.applyMotionPhotos(asset as unknown as Asset, exifTags, dates, stats)); + promises.push(this.applyMotionPhotos(asset, exifTags, dates, stats)); } if (isFaceImportEnabled(metadata) && this.hasTaggedFaces(exifTags)) { @@ -285,27 +284,31 @@ export class MetadataService extends BaseService { await this.assetRepository.upsertJobStatus({ assetId: asset.id, metadataExtractedAt: new Date() }); - return JobStatus.SUCCESS; + await this.eventRepository.emit('asset.metadataExtracted', { + assetId: asset.id, + userId: asset.ownerId, + source: data.source, + }); } @OnJob({ name: JobName.QUEUE_SIDECAR, queue: QueueName.SIDECAR }) - async handleQueueSidecar(job: JobOf): Promise { - const { force } = job; - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination) - : this.assetRepository.getWithout(pagination, WithoutProperty.SIDECAR); - }); + async handleQueueSidecar({ force }: JobOf): Promise { + let jobs: JobItem[] = []; + const queueAll = async () => { + await this.jobRepository.queueAll(jobs); + jobs = []; + }; - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ - name: force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY, - data: { id: asset.id }, - })), - ); + const assets = this.assetJobRepository.streamForSidecar(force); + for await (const asset of assets) { + jobs.push({ name: force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY, data: { id: asset.id } }); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await queueAll(); + } } + await queueAll(); + return JobStatus.SUCCESS; } @@ -525,8 +528,11 @@ export class MetadataService extends BaseService { }); // Hide the motion photo video asset if it's not already hidden to prepare for linking - if (motionAsset.isVisible) { - await this.assetRepository.update({ id: motionAsset.id, isVisible: false }); + if (motionAsset.visibility === AssetVisibility.TIMELINE) { + await this.assetRepository.update({ + id: motionAsset.id, + visibility: AssetVisibility.HIDDEN, + }); this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); } } else { @@ -542,7 +548,7 @@ export class MetadataService extends BaseService { ownerId: asset.ownerId, originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), originalFileName: `${path.parse(asset.originalFileName).name}.mp4`, - isVisible: false, + visibility: AssetVisibility.HIDDEN, deviceAssetId: 'NONE', deviceId: 'NONE', }); @@ -576,6 +582,7 @@ export class MetadataService extends BaseService { this.logger.log(`Wrote motion photo video to ${motionAsset.originalPath}`); await this.handleMetadataExtraction({ id: motionAsset.id }); + await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: { id: motionAsset.id } }); } this.logger.debug(`Finished motion photo video extraction for asset ${asset.id}: ${asset.originalPath}`); @@ -593,6 +600,80 @@ export class MetadataService extends BaseService { ); } + private orientRegionInfo( + regionInfo: ImmichTagsWithFaces['RegionInfo'], + orientation: ExifOrientation | undefined, + ): ImmichTagsWithFaces['RegionInfo'] { + // skip default Orientation + if (orientation === undefined || orientation === ExifOrientation.Horizontal) { + return regionInfo; + } + + const isSidewards = [ + ExifOrientation.MirrorHorizontalRotate270CW, + ExifOrientation.Rotate90CW, + ExifOrientation.MirrorHorizontalRotate90CW, + ExifOrientation.Rotate270CW, + ].includes(orientation); + + // swap image dimensions in AppliedToDimensions if orientation is sidewards + const adjustedAppliedToDimensions = isSidewards + ? { + ...regionInfo.AppliedToDimensions, + W: regionInfo.AppliedToDimensions.H, + H: regionInfo.AppliedToDimensions.W, + } + : regionInfo.AppliedToDimensions; + + // update area coordinates and dimensions in RegionList assuming "normalized" unit as per MWG guidelines + const adjustedRegionList = regionInfo.RegionList.map((region) => { + let { X, Y, W, H } = region.Area; + switch (orientation) { + case ExifOrientation.MirrorHorizontal: { + X = 1 - X; + break; + } + case ExifOrientation.Rotate180: { + [X, Y] = [1 - X, 1 - Y]; + break; + } + case ExifOrientation.MirrorVertical: { + Y = 1 - Y; + break; + } + case ExifOrientation.MirrorHorizontalRotate270CW: { + [X, Y] = [Y, X]; + break; + } + case ExifOrientation.Rotate90CW: { + [X, Y] = [1 - Y, X]; + break; + } + case ExifOrientation.MirrorHorizontalRotate90CW: { + [X, Y] = [1 - Y, 1 - X]; + break; + } + case ExifOrientation.Rotate270CW: { + [X, Y] = [Y, 1 - X]; + break; + } + } + if (isSidewards) { + [W, H] = [H, W]; + } + return { + ...region, + Area: { ...region.Area, X, Y, W, H }, + }; + }); + + return { + ...regionInfo, + AppliedToDimensions: adjustedAppliedToDimensions, + RegionList: adjustedRegionList, + }; + } + private async applyTaggedFaces( asset: { id: string; ownerId: string; faces: AssetFace[]; originalPath: string }, tags: ImmichTags, @@ -606,13 +687,16 @@ export class MetadataService extends BaseService { const existingNameMap = new Map(existingNames.map(({ id, name }) => [name.toLowerCase(), id])); const missing: (Insertable & { ownerId: string })[] = []; const missingWithFaceAsset: { id: string; ownerId: string; faceAssetId: string }[] = []; - for (const region of tags.RegionInfo.RegionList) { + + const adjustedRegionInfo = this.orientRegionInfo(tags.RegionInfo, tags.Orientation); + const imageWidth = adjustedRegionInfo.AppliedToDimensions.W; + const imageHeight = adjustedRegionInfo.AppliedToDimensions.H; + + for (const region of adjustedRegionInfo.RegionList) { if (!region.Name) { continue; } - const imageWidth = tags.RegionInfo.AppliedToDimensions.W; - const imageHeight = tags.RegionInfo.AppliedToDimensions.H; const loweredName = region.Name.toLowerCase(); const personId = existingNameMap.get(loweredName) || this.cryptoRepository.randomUUID(); @@ -783,7 +867,7 @@ export class MetadataService extends BaseService { return JobStatus.FAILED; } - if (!isSync && (!asset.isVisible || asset.sidecarPath) && !asset.isExternal) { + if (!isSync && (asset.visibility === AssetVisibility.HIDDEN || asset.sidecarPath) && !asset.isExternal) { return JobStatus.FAILED; } diff --git a/server/src/services/notification-admin.service.spec.ts b/server/src/services/notification-admin.service.spec.ts new file mode 100644 index 0000000000..4a747d41a3 --- /dev/null +++ b/server/src/services/notification-admin.service.spec.ts @@ -0,0 +1,111 @@ +import { defaults, SystemConfig } from 'src/config'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { NotificationService } from 'src/services/notification.service'; +import { userStub } from 'test/fixtures/user.stub'; +import { newTestService, ServiceMocks } from 'test/utils'; + +const smtpTransport = Object.freeze({ + ...defaults, + notifications: { + smtp: { + ...defaults.notifications.smtp, + enabled: true, + transport: { + ignoreCert: false, + host: 'localhost', + port: 587, + username: 'test', + password: 'test', + }, + }, + }, +}); + +describe(NotificationService.name, () => { + let sut: NotificationService; + let mocks: ServiceMocks; + + beforeEach(() => { + ({ sut, mocks } = newTestService(NotificationService)); + }); + + it('should work', () => { + expect(sut).toBeDefined(); + }); + + describe('sendTestEmail', () => { + it('should throw error if user could not be found', async () => { + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).rejects.toThrow('User not found'); + }); + + it('should throw error if smtp validation fails', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockRejectedValue(''); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).rejects.toThrow( + 'Failed to verify SMTP configuration', + ); + }); + + it('should send email to default domain', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + }), + ); + }); + + it('should send email to external domain', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect(sut.sendTestEmail('', smtpTransport.notifications.smtp)).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://demo.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + }), + ); + }); + + it('should send email with replyTo', async () => { + mocks.user.get.mockResolvedValue(userStub.admin); + mocks.email.verifySmtp.mockResolvedValue(true); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); + + await expect( + sut.sendTestEmail('', { ...smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }), + ).resolves.not.toThrow(); + expect(mocks.email.renderEmail).toHaveBeenCalledWith({ + template: EmailTemplate.TEST_EMAIL, + data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, + }); + expect(mocks.email.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + subject: 'Test email from Immich', + smtp: smtpTransport.notifications.smtp.transport, + replyTo: 'demo@immich.app', + }), + ); + }); + }); +}); diff --git a/server/src/services/notification-admin.service.ts b/server/src/services/notification-admin.service.ts new file mode 100644 index 0000000000..bf0d2bba41 --- /dev/null +++ b/server/src/services/notification-admin.service.ts @@ -0,0 +1,120 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { mapNotification, NotificationCreateDto } from 'src/dtos/notification.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; +import { NotificationLevel, NotificationType } from 'src/enum'; +import { EmailTemplate } from 'src/repositories/email.repository'; +import { BaseService } from 'src/services/base.service'; +import { getExternalDomain } from 'src/utils/misc'; + +@Injectable() +export class NotificationAdminService extends BaseService { + async create(auth: AuthDto, dto: NotificationCreateDto) { + const item = await this.notificationRepository.create({ + userId: dto.userId, + type: dto.type ?? NotificationType.Custom, + level: dto.level ?? NotificationLevel.Info, + title: dto.title, + description: dto.description, + data: dto.data, + }); + + return mapNotification(item); + } + + async sendTestEmail(id: string, dto: SystemConfigSmtpDto, tempTemplate?: string) { + const user = await this.userRepository.get(id, { withDeleted: false }); + if (!user) { + throw new Error('User not found'); + } + + try { + await this.emailRepository.verifySmtp(dto.transport); + } catch (error) { + throw new BadRequestException('Failed to verify SMTP configuration', { cause: error }); + } + + const { server } = await this.getConfig({ withCache: false }); + const { html, text } = await this.emailRepository.renderEmail({ + template: EmailTemplate.TEST_EMAIL, + data: { + baseUrl: getExternalDomain(server), + displayName: user.name, + }, + customTemplate: tempTemplate!, + }); + const { messageId } = await this.emailRepository.sendEmail({ + to: user.email, + subject: 'Test email from Immich', + html, + text, + from: dto.from, + replyTo: dto.replyTo || dto.from, + smtp: dto.transport, + }); + + return { messageId }; + } + + async getTemplate(name: EmailTemplate, customTemplate: string) { + const { server, templates } = await this.getConfig({ withCache: false }); + + let templateResponse = ''; + + switch (name) { + case EmailTemplate.WELCOME: { + const { html: _welcomeHtml } = await this.emailRepository.renderEmail({ + template: EmailTemplate.WELCOME, + data: { + baseUrl: getExternalDomain(server), + displayName: 'John Doe', + username: 'john@doe.com', + password: 'thisIsAPassword123', + }, + customTemplate: customTemplate || templates.email.welcomeTemplate, + }); + + templateResponse = _welcomeHtml; + break; + } + case EmailTemplate.ALBUM_UPDATE: { + const { html: _updateAlbumHtml } = await this.emailRepository.renderEmail({ + template: EmailTemplate.ALBUM_UPDATE, + data: { + baseUrl: getExternalDomain(server), + albumId: '1', + albumName: 'Favorite Photos', + recipientName: 'Jane Doe', + cid: undefined, + }, + customTemplate: customTemplate || templates.email.albumInviteTemplate, + }); + templateResponse = _updateAlbumHtml; + break; + } + + case EmailTemplate.ALBUM_INVITE: { + const { html } = await this.emailRepository.renderEmail({ + template: EmailTemplate.ALBUM_INVITE, + data: { + baseUrl: getExternalDomain(server), + albumId: '1', + albumName: "John Doe's Favorites", + senderName: 'John Doe', + recipientName: 'Jane Doe', + cid: undefined, + }, + customTemplate: customTemplate || templates.email.albumInviteTemplate, + }); + templateResponse = html; + break; + } + default: { + templateResponse = ''; + break; + } + } + + return { name, html: templateResponse }; + } +} diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 85e425b11f..b0f2a3ab62 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -3,7 +3,6 @@ import { defaults, SystemConfig } from 'src/config'; import { AlbumUser } from 'src/database'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { NotificationService } from 'src/services/notification.service'; import { INotifyAlbumUpdateJob } from 'src/types'; import { albumStub } from 'test/fixtures/album.stub'; @@ -74,18 +73,18 @@ describe(NotificationService.name, () => { const oldConfig = configs.smtpDisabled; const newConfig = configs.smtpEnabled; - mocks.notification.verifySmtp.mockResolvedValue(true); + mocks.email.verifySmtp.mockResolvedValue(true); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); + expect(mocks.email.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); }); it('validates smtp config when transport changes', async () => { const oldConfig = configs.smtpEnabled; const newConfig = configs.smtpTransport; - mocks.notification.verifySmtp.mockResolvedValue(true); + mocks.email.verifySmtp.mockResolvedValue(true); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); + expect(mocks.email.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport); }); it('skips smtp validation when there are no changes', async () => { @@ -93,7 +92,7 @@ describe(NotificationService.name, () => { const newConfig = { ...configs.smtpEnabled }; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('skips smtp validation with DTO when there are no changes', async () => { @@ -101,7 +100,7 @@ describe(NotificationService.name, () => { const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled); await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('skips smtp validation when smtp is disabled', async () => { @@ -109,14 +108,14 @@ describe(NotificationService.name, () => { const newConfig = { ...configs.smtpDisabled }; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); - expect(mocks.notification.verifySmtp).not.toHaveBeenCalled(); + expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); }); it('should fail if smtp configuration is invalid', async () => { const oldConfig = configs.smtpDisabled; const newConfig = configs.smtpEnabled; - mocks.notification.verifySmtp.mockRejectedValue(new Error('Failed validating smtp')); + mocks.email.verifySmtp.mockRejectedValue(new Error('Failed validating smtp')); await expect(sut.onConfigValidate({ oldConfig, newConfig })).rejects.toBeInstanceOf(Error); }); }); @@ -155,10 +154,10 @@ describe(NotificationService.name, () => { describe('onAlbumUpdateEvent', () => { it('should queue notify album update event', async () => { - await sut.onAlbumUpdate({ id: 'album', recipientIds: ['42'] }); + await sut.onAlbumUpdate({ id: 'album', recipientId: '42' }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.NOTIFY_ALBUM_UPDATE, - data: { id: 'album', recipientIds: ['42'], delay: 300_000 }, + data: { id: 'album', recipientId: '42', delay: 300_000 }, }); }); }); @@ -241,82 +240,6 @@ describe(NotificationService.name, () => { }); }); - describe('sendTestEmail', () => { - it('should throw error if user could not be found', async () => { - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).rejects.toThrow('User not found'); - }); - - it('should throw error if smtp validation fails', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockRejectedValue(''); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).rejects.toThrow( - 'Failed to verify SMTP configuration', - ); - }); - - it('should send email to default domain', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - }), - ); - }); - - it('should send email to external domain', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://demo.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - }), - ); - }); - - it('should send email with replyTo', async () => { - mocks.user.get.mockResolvedValue(userStub.admin); - mocks.notification.verifySmtp.mockResolvedValue(true); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' }); - - await expect( - sut.sendTestEmail('', { ...configs.smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }), - ).resolves.not.toThrow(); - expect(mocks.notification.renderEmail).toHaveBeenCalledWith({ - template: EmailTemplate.TEST_EMAIL, - data: { baseUrl: 'https://my.immich.app', displayName: userStub.admin.name }, - }); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - subject: 'Test email from Immich', - smtp: configs.smtpTransport.notifications.smtp.transport, - replyTo: 'demo@immich.app', - }), - ); - }); - }); - describe('handleUserSignup', () => { it('should skip if user could not be found', async () => { await expect(sut.handleUserSignup({ id: '' })).resolves.toBe(JobStatus.SKIPPED); @@ -325,7 +248,7 @@ describe(NotificationService.name, () => { it('should be successful', async () => { mocks.user.get.mockResolvedValue(userStub.admin); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); await expect(sut.handleUserSignup({ id: '' })).resolves.toBe(JobStatus.SUCCESS); expect(mocks.job.queue).toHaveBeenCalledWith({ @@ -390,7 +313,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); expect(mocks.job.queue).toHaveBeenCalledWith({ @@ -411,7 +334,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); @@ -440,7 +363,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([ { id: '1', type: AssetFileType.THUMBNAIL, path: 'path-to-thumb.jpg' }, ]); @@ -471,7 +394,7 @@ describe(NotificationService.name, () => { ], }); mocks.systemMetadata.get.mockResolvedValue({ server: {} }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([assetStub.image.files[2]]); await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS); @@ -491,14 +414,14 @@ describe(NotificationService.name, () => { describe('handleAlbumUpdate', () => { it('should skip if album could not be found', async () => { - await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED); + await expect(sut.handleAlbumUpdate({ id: '', recipientId: '1' })).resolves.toBe(JobStatus.SKIPPED); expect(mocks.user.get).not.toHaveBeenCalled(); }); it('should skip if owner could not be found', async () => { mocks.album.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail); - await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED); + await expect(sut.handleAlbumUpdate({ id: '', recipientId: '1' })).resolves.toBe(JobStatus.SKIPPED); expect(mocks.systemMetadata.get).not.toHaveBeenCalled(); }); @@ -508,12 +431,12 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValueOnce(userStub.user1); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); - await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); + await sut.handleAlbumUpdate({ id: '', recipientId: userStub.user1.id }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should skip recipient with disabled email notifications', async () => { @@ -530,12 +453,12 @@ describe(NotificationService.name, () => { }, ], }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); - await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); + await sut.handleAlbumUpdate({ id: '', recipientId: userStub.user1.id }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should skip recipient with disabled email notifications for the album update event', async () => { @@ -552,12 +475,12 @@ describe(NotificationService.name, () => { }, ], }); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); - await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); + await sut.handleAlbumUpdate({ id: '', recipientId: userStub.user1.id }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).not.toHaveBeenCalled(); + expect(mocks.email.renderEmail).not.toHaveBeenCalled(); }); it('should send email', async () => { @@ -566,24 +489,24 @@ describe(NotificationService.name, () => { albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUser], }); mocks.user.get.mockResolvedValue(userStub.user1); - mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' }); + mocks.email.renderEmail.mockResolvedValue({ html: '', text: '' }); mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]); - await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] }); + await sut.handleAlbumUpdate({ id: '', recipientId: userStub.user1.id }); expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false }); - expect(mocks.notification.renderEmail).toHaveBeenCalled(); + expect(mocks.email.renderEmail).toHaveBeenCalled(); expect(mocks.job.queue).toHaveBeenCalled(); }); it('should add new recipients for new images if job is already queued', async () => { - mocks.job.removeJob.mockResolvedValue({ id: '1', recipientIds: ['2', '3', '4'] } as INotifyAlbumUpdateJob); - await sut.onAlbumUpdate({ id: '1', recipientIds: ['1', '2', '3'] } as INotifyAlbumUpdateJob); + await sut.onAlbumUpdate({ id: '1', recipientId: '2' } as INotifyAlbumUpdateJob); + expect(mocks.job.removeJob).toHaveBeenCalledWith(JobName.NOTIFY_ALBUM_UPDATE, '1/2'); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.NOTIFY_ALBUM_UPDATE, data: { id: '1', delay: 300_000, - recipientIds: ['1', '2', '3', '4'], + recipientId: '2', }, }); }); @@ -599,24 +522,20 @@ describe(NotificationService.name, () => { mocks.systemMetadata.get.mockResolvedValue({ notifications: { smtp: { enabled: true, from: 'test@immich.app' } }, }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: '', response: '' }); await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ replyTo: 'test@immich.app' }), - ); + expect(mocks.email.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'test@immich.app' })); }); it('should send mail with replyTo successfully', async () => { mocks.systemMetadata.get.mockResolvedValue({ notifications: { smtp: { enabled: true, from: 'test@immich.app', replyTo: 'demo@immich.app' } }, }); - mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' }); + mocks.email.sendEmail.mockResolvedValue({ messageId: '', response: '' }); await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS); - expect(mocks.notification.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ replyTo: 'demo@immich.app' }), - ); + expect(mocks.email.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'demo@immich.app' })); }); }); }); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 2c4cc76756..e72f77ad4f 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -1,11 +1,29 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { OnEvent, OnJob } from 'src/decorators'; +import { mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { + mapNotification, + NotificationDeleteAllDto, + NotificationDto, + NotificationSearchDto, + NotificationUpdateAllDto, + NotificationUpdateDto, +} from 'src/dtos/notification.dto'; import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; -import { AssetFileType, JobName, JobStatus, QueueName } from 'src/enum'; +import { + AssetFileType, + JobName, + JobStatus, + NotificationLevel, + NotificationType, + Permission, + QueueName, +} from 'src/enum'; +import { EmailTemplate } from 'src/repositories/email.repository'; import { ArgOf } from 'src/repositories/event.repository'; -import { EmailTemplate } from 'src/repositories/notification.repository'; import { BaseService } from 'src/services/base.service'; -import { EmailImageAttachment, IEntityJob, INotifyAlbumUpdateJob, JobItem, JobOf } from 'src/types'; +import { EmailImageAttachment, JobOf } from 'src/types'; import { getFilenameExtension } from 'src/utils/file'; import { getExternalDomain } from 'src/utils/misc'; import { isEqualObject } from 'src/utils/object'; @@ -15,6 +33,80 @@ import { getPreferences } from 'src/utils/preferences'; export class NotificationService extends BaseService { private static albumUpdateEmailDelayMs = 300_000; + async search(auth: AuthDto, dto: NotificationSearchDto): Promise { + const items = await this.notificationRepository.search(auth.user.id, dto); + return items.map((item) => mapNotification(item)); + } + + async updateAll(auth: AuthDto, dto: NotificationUpdateAllDto) { + await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NOTIFICATION_UPDATE }); + await this.notificationRepository.updateAll(dto.ids, { + readAt: dto.readAt, + }); + } + + async deleteAll(auth: AuthDto, dto: NotificationDeleteAllDto) { + await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NOTIFICATION_DELETE }); + await this.notificationRepository.deleteAll(dto.ids); + } + + async get(auth: AuthDto, id: string) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_READ }); + const item = await this.notificationRepository.get(id); + if (!item) { + throw new BadRequestException('Notification not found'); + } + return mapNotification(item); + } + + async update(auth: AuthDto, id: string, dto: NotificationUpdateDto) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_UPDATE }); + const item = await this.notificationRepository.update(id, { + readAt: dto.readAt, + }); + return mapNotification(item); + } + + async delete(auth: AuthDto, id: string) { + await this.requireAccess({ auth, ids: [id], permission: Permission.NOTIFICATION_DELETE }); + await this.notificationRepository.delete(id); + } + + @OnJob({ name: JobName.NOTIFICATIONS_CLEANUP, queue: QueueName.BACKGROUND_TASK }) + async onNotificationsCleanup() { + await this.notificationRepository.cleanup(); + } + + @OnEvent({ name: 'job.failed' }) + async onJobFailed({ job, error }: ArgOf<'job.failed'>) { + const admin = await this.userRepository.getAdmin(); + if (!admin) { + return; + } + + this.logger.error(`Unable to run job handler (${job.name}): ${error}`, error?.stack, JSON.stringify(job.data)); + + switch (job.name) { + case JobName.BACKUP_DATABASE: { + const errorMessage = error instanceof Error ? error.message : error; + const item = await this.notificationRepository.create({ + userId: admin.id, + type: NotificationType.JobFailed, + level: NotificationLevel.Error, + title: 'Job Failed', + description: `Job ${[job.name]} failed with error: ${errorMessage}`, + }); + + this.eventRepository.clientSend('on_notification', admin.id, mapNotification(item)); + break; + } + + default: { + return; + } + } + } + @OnEvent({ name: 'config.update' }) onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) { this.eventRepository.clientBroadcast('on_config_update'); @@ -28,7 +120,7 @@ export class NotificationService extends BaseService { newConfig.notifications.smtp.enabled && !isEqualObject(oldConfig.notifications.smtp, newConfig.notifications.smtp) ) { - await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport); + await this.emailRepository.verifySmtp(newConfig.notifications.smtp.transport); } } catch (error: Error | any) { this.logger.error(`Failed to validate SMTP configuration: ${error}`, error?.stack); @@ -61,6 +153,18 @@ export class NotificationService extends BaseService { this.eventRepository.clientSend('on_asset_trash', userId, assetIds); } + @OnEvent({ name: 'asset.metadataExtracted' }) + async onAssetMetadataExtracted({ assetId, userId, source }: ArgOf<'asset.metadataExtracted'>) { + if (source !== 'sidecar-write') { + return; + } + + const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([assetId]); + if (asset) { + this.eventRepository.clientSend('on_asset_update', userId, mapAsset(asset)); + } + } + @OnEvent({ name: 'assets.restore' }) onAssetsRestore({ assetIds, userId }: ArgOf<'assets.restore'>) { this.eventRepository.clientSend('on_asset_restore', userId, assetIds); @@ -94,30 +198,12 @@ export class NotificationService extends BaseService { } @OnEvent({ name: 'album.update' }) - async onAlbumUpdate({ id, recipientIds }: ArgOf<'album.update'>) { - // if recipientIds is empty, album likely only has one user part of it, don't queue notification if so - if (recipientIds.length === 0) { - return; - } - - const job: JobItem = { + async onAlbumUpdate({ id, recipientId }: ArgOf<'album.update'>) { + await this.jobRepository.removeJob(JobName.NOTIFY_ALBUM_UPDATE, `${id}/${recipientId}`); + await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_UPDATE, - data: { id, recipientIds, delay: NotificationService.albumUpdateEmailDelayMs }, - }; - - const previousJobData = await this.jobRepository.removeJob(id, JobName.NOTIFY_ALBUM_UPDATE); - if (previousJobData && this.isAlbumUpdateJob(previousJobData)) { - for (const id of previousJobData.recipientIds) { - if (!recipientIds.includes(id)) { - recipientIds.push(id); - } - } - } - await this.jobRepository.queue(job); - } - - private isAlbumUpdateJob(job: IEntityJob): job is INotifyAlbumUpdateJob { - return 'recipientIds' in job; + data: { id, recipientId, delay: NotificationService.albumUpdateEmailDelayMs }, + }); } @OnEvent({ name: 'album.invite' }) @@ -138,13 +224,13 @@ export class NotificationService extends BaseService { } try { - await this.notificationRepository.verifySmtp(dto.transport); + await this.emailRepository.verifySmtp(dto.transport); } catch (error) { throw new BadRequestException('Failed to verify SMTP configuration', { cause: error }); } const { server } = await this.getConfig({ withCache: false }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.TEST_EMAIL, data: { baseUrl: getExternalDomain(server), @@ -152,7 +238,7 @@ export class NotificationService extends BaseService { }, customTemplate: tempTemplate!, }); - const { messageId } = await this.notificationRepository.sendEmail({ + const { messageId } = await this.emailRepository.sendEmail({ to: user.email, subject: 'Test email from Immich', html, @@ -172,7 +258,7 @@ export class NotificationService extends BaseService { switch (name) { case EmailTemplate.WELCOME: { - const { html: _welcomeHtml } = await this.notificationRepository.renderEmail({ + const { html: _welcomeHtml } = await this.emailRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { baseUrl: getExternalDomain(server), @@ -187,7 +273,7 @@ export class NotificationService extends BaseService { break; } case EmailTemplate.ALBUM_UPDATE: { - const { html: _updateAlbumHtml } = await this.notificationRepository.renderEmail({ + const { html: _updateAlbumHtml } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_UPDATE, data: { baseUrl: getExternalDomain(server), @@ -203,7 +289,7 @@ export class NotificationService extends BaseService { } case EmailTemplate.ALBUM_INVITE: { - const { html } = await this.notificationRepository.renderEmail({ + const { html } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { baseUrl: getExternalDomain(server), @@ -235,7 +321,7 @@ export class NotificationService extends BaseService { } const { server, templates } = await this.getConfig({ withCache: true }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.WELCOME, data: { baseUrl: getExternalDomain(server), @@ -271,7 +357,7 @@ export class NotificationService extends BaseService { return JobStatus.SKIPPED; } - const { emailNotifications } = getPreferences(recipient.email, recipient.metadata); + const { emailNotifications } = getPreferences(recipient.metadata); if (!emailNotifications.enabled || !emailNotifications.albumInvite) { return JobStatus.SKIPPED; @@ -280,7 +366,7 @@ export class NotificationService extends BaseService { const attachment = await this.getAlbumThumbnailAttachment(album); const { server, templates } = await this.getConfig({ withCache: false }); - const { html, text } = await this.notificationRepository.renderEmail({ + const { html, text } = await this.emailRepository.renderEmail({ template: EmailTemplate.ALBUM_INVITE, data: { baseUrl: getExternalDomain(server), @@ -308,7 +394,7 @@ export class NotificationService extends BaseService { } @OnJob({ name: JobName.NOTIFY_ALBUM_UPDATE, queue: QueueName.NOTIFICATION }) - async handleAlbumUpdate({ id, recipientIds }: JobOf) { + async handleAlbumUpdate({ id, recipientId }: JobOf) { const album = await this.albumRepository.getById(id, { withAssets: false }); if (!album) { @@ -320,49 +406,44 @@ export class NotificationService extends BaseService { return JobStatus.SKIPPED; } - const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) => - recipientIds.includes(user.id), - ); const attachment = await this.getAlbumThumbnailAttachment(album); const { server, templates } = await this.getConfig({ withCache: false }); - for (const recipient of recipients) { - const user = await this.userRepository.get(recipient.id, { withDeleted: false }); - if (!user) { - continue; - } - - const { emailNotifications } = getPreferences(user.email, user.metadata); - - if (!emailNotifications.enabled || !emailNotifications.albumUpdate) { - continue; - } - - const { html, text } = await this.notificationRepository.renderEmail({ - template: EmailTemplate.ALBUM_UPDATE, - data: { - baseUrl: getExternalDomain(server), - albumId: album.id, - albumName: album.albumName, - recipientName: recipient.name, - cid: attachment ? attachment.cid : undefined, - }, - customTemplate: templates.email.albumUpdateTemplate, - }); - - await this.jobRepository.queue({ - name: JobName.SEND_EMAIL, - data: { - to: recipient.email, - subject: `New media has been added to an album - ${album.albumName}`, - html, - text, - imageAttachments: attachment ? [attachment] : undefined, - }, - }); + const user = await this.userRepository.get(recipientId, { withDeleted: false }); + if (!user) { + return JobStatus.SKIPPED; } + const { emailNotifications } = getPreferences(user.metadata); + + if (!emailNotifications.enabled || !emailNotifications.albumUpdate) { + return JobStatus.SKIPPED; + } + + const { html, text } = await this.emailRepository.renderEmail({ + template: EmailTemplate.ALBUM_UPDATE, + data: { + baseUrl: getExternalDomain(server), + albumId: album.id, + albumName: album.albumName, + recipientName: user.name, + cid: attachment ? attachment.cid : undefined, + }, + customTemplate: templates.email.albumUpdateTemplate, + }); + + await this.jobRepository.queue({ + name: JobName.SEND_EMAIL, + data: { + to: user.email, + subject: `New media has been added to an album - ${album.albumName}`, + html, + text, + imageAttachments: attachment ? [attachment] : undefined, + }, + }); + return JobStatus.SUCCESS; } @@ -374,7 +455,7 @@ export class NotificationService extends BaseService { } const { to, subject, html, text: plain } = data; - const response = await this.notificationRepository.sendEmail({ + const response = await this.emailRepository.sendEmail({ to, subject, html, diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 9808522434..d9df2225f4 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -1,8 +1,7 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { mapFaces, mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; -import { CacheControl, Colorspace, ImageFormat, JobName, JobStatus, SourceType, SystemMetadataKey } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; +import { CacheControl, JobName, JobStatus, SourceType, SystemMetadataKey } from 'src/enum'; import { DetectedFaces } from 'src/repositories/machine-learning.repository'; import { FaceSearchResult } from 'src/repositories/search.repository'; import { PersonService } from 'src/services/person.service'; @@ -10,7 +9,7 @@ import { ImmichFileResponse } from 'src/utils/file'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { faceStub } from 'test/fixtures/face.stub'; -import { personStub, personThumbnailStub } from 'test/fixtures/person.stub'; +import { personStub } from 'test/fixtures/person.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { factory } from 'test/small.factory'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; @@ -455,14 +454,12 @@ describe(PersonService.name, () => { }); it('should queue missing assets', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueDetectFaces({ force: false }); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(false); + expect(mocks.person.vacuum).not.toHaveBeenCalled(); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -472,18 +469,16 @@ describe(PersonService.name, () => { }); it('should queue all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.withName]); await sut.handleQueueDetectFaces({ force: true }); expect(mocks.person.deleteFaces).toHaveBeenCalledWith({ sourceType: SourceType.MACHINE_LEARNING }); expect(mocks.person.delete).toHaveBeenCalledWith([personStub.withName.id]); + expect(mocks.person.vacuum).toHaveBeenCalledWith({ reindexVectors: true }); expect(mocks.storage.unlink).toHaveBeenCalledWith(personStub.withName.thumbnailPath); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -493,17 +488,15 @@ describe(PersonService.name, () => { }); it('should refresh all assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueDetectFaces({ force: undefined }); expect(mocks.person.delete).not.toHaveBeenCalled(); expect(mocks.person.deleteFaces).not.toHaveBeenCalled(); + expect(mocks.person.vacuum).not.toHaveBeenCalled(); expect(mocks.storage.unlink).not.toHaveBeenCalled(); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(undefined); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -516,16 +509,13 @@ describe(PersonService.name, () => { it('should delete existing people and faces if forced', async () => { mocks.person.getAll.mockReturnValue(makeStream([faceStub.face1.person, personStub.randomPerson])); mocks.person.getAllFaces.mockReturnValue(makeStream([faceStub.face1])); - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForDetectFacesJob.mockReturnValue(makeStream([assetStub.image])); mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.randomPerson]); mocks.person.deleteFaces.mockResolvedValue(); await sut.handleQueueDetectFaces({ force: true }); - expect(mocks.asset.getAll).toHaveBeenCalled(); + expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACE_DETECTION, @@ -534,6 +524,7 @@ describe(PersonService.name, () => { ]); expect(mocks.person.delete).toHaveBeenCalledWith([personStub.randomPerson.id]); expect(mocks.storage.unlink).toHaveBeenCalledWith(personStub.randomPerson.thumbnailPath); + expect(mocks.person.vacuum).toHaveBeenCalledWith({ reindexVectors: true }); }); }); @@ -597,6 +588,7 @@ describe(PersonService.name, () => { expect(mocks.systemMetadata.set).toHaveBeenCalledWith(SystemMetadataKey.FACIAL_RECOGNITION_STATE, { lastRun: expect.any(String), }); + expect(mocks.person.vacuum).not.toHaveBeenCalled(); }); it('should queue all assets', async () => { @@ -624,6 +616,7 @@ describe(PersonService.name, () => { expect(mocks.systemMetadata.set).toHaveBeenCalledWith(SystemMetadataKey.FACIAL_RECOGNITION_STATE, { lastRun: expect.any(String), }); + expect(mocks.person.vacuum).toHaveBeenCalledWith({ reindexVectors: false }); }); it('should run nightly if new face has been added since last run', async () => { @@ -642,11 +635,14 @@ describe(PersonService.name, () => { mocks.person.getAllWithoutFaces.mockResolvedValue([]); mocks.person.unassignFaces.mockResolvedValue(); - await sut.handleQueueRecognizeFaces({ force: true, nightly: true }); + await sut.handleQueueRecognizeFaces({ force: false, nightly: true }); expect(mocks.systemMetadata.get).toHaveBeenCalledWith(SystemMetadataKey.FACIAL_RECOGNITION_STATE); expect(mocks.person.getLatestFaceDate).toHaveBeenCalledOnce(); - expect(mocks.person.getAllFaces).toHaveBeenCalledWith(undefined); + expect(mocks.person.getAllFaces).toHaveBeenCalledWith({ + personId: null, + sourceType: SourceType.MACHINE_LEARNING, + }); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.FACIAL_RECOGNITION, @@ -656,6 +652,7 @@ describe(PersonService.name, () => { expect(mocks.systemMetadata.set).toHaveBeenCalledWith(SystemMetadataKey.FACIAL_RECOGNITION_STATE, { lastRun: expect.any(String), }); + expect(mocks.person.vacuum).not.toHaveBeenCalled(); }); it('should skip nightly if no new face has been added since last run', async () => { @@ -673,6 +670,7 @@ describe(PersonService.name, () => { expect(mocks.person.getAllFaces).not.toHaveBeenCalled(); expect(mocks.job.queueAll).not.toHaveBeenCalled(); expect(mocks.systemMetadata.set).not.toHaveBeenCalled(); + expect(mocks.person.vacuum).not.toHaveBeenCalled(); }); it('should delete existing people if forced', async () => { @@ -701,6 +699,7 @@ describe(PersonService.name, () => { ]); expect(mocks.person.delete).toHaveBeenCalledWith([personStub.randomPerson.id]); expect(mocks.storage.unlink).toHaveBeenCalledWith(personStub.randomPerson.thumbnailPath); + expect(mocks.person.vacuum).toHaveBeenCalledWith({ reindexVectors: false }); }); }); @@ -1037,114 +1036,6 @@ describe(PersonService.name, () => { }); }); - describe('handleGeneratePersonThumbnail', () => { - it('should skip if machine learning is disabled', async () => { - mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.machineLearningDisabled); - - await expect(sut.handleGeneratePersonThumbnail({ id: 'person-1' })).resolves.toBe(JobStatus.SKIPPED); - expect(mocks.asset.getByIds).not.toHaveBeenCalled(); - expect(mocks.systemMetadata.get).toHaveBeenCalled(); - }); - - it('should skip a person not found', async () => { - await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); - expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); - }); - - it('should skip a person without a face asset id', async () => { - mocks.person.getById.mockResolvedValue(personStub.noThumbnail); - await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); - expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); - }); - - it('should skip a person with face not found', async () => { - await sut.handleGeneratePersonThumbnail({ id: 'person-1' }); - expect(mocks.media.generateThumbnail).not.toHaveBeenCalled(); - }); - - it('should generate a thumbnail', async () => { - mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailMiddle); - mocks.media.generateThumbnail.mockResolvedValue(); - - await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id }); - - expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); - expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( - assetStub.primaryImage.originalPath, - { - colorspace: Colorspace.P3, - format: ImageFormat.JPEG, - size: 250, - quality: 80, - crop: { - left: 238, - top: 163, - width: 274, - height: 274, - }, - processInvalidImages: false, - }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - ); - expect(mocks.person.update).toHaveBeenCalledWith({ - id: 'person-1', - thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - }); - }); - - it('should generate a thumbnail without going negative', async () => { - mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailStart); - mocks.media.generateThumbnail.mockResolvedValue(); - - await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id }); - - expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( - assetStub.primaryImage.originalPath, - { - colorspace: Colorspace.P3, - format: ImageFormat.JPEG, - size: 250, - quality: 80, - crop: { - left: 0, - top: 85, - width: 510, - height: 510, - }, - processInvalidImages: false, - }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - ); - }); - - it('should generate a thumbnail without overflowing', async () => { - mocks.person.getDataForThumbnailGenerationJob.mockResolvedValue(personThumbnailStub.newThumbnailEnd); - mocks.person.update.mockResolvedValue(personStub.primaryPerson); - mocks.media.generateThumbnail.mockResolvedValue(); - - await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id }); - - expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( - assetStub.primaryImage.originalPath, - { - colorspace: Colorspace.P3, - format: ImageFormat.JPEG, - size: 250, - quality: 80, - crop: { - left: 591, - top: 591, - width: 408, - height: 408, - }, - processInvalidImages: false, - }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - ); - }); - }); - describe('mergePerson', () => { it('should require person.write and person.merge permission', async () => { mocks.person.getById.mockResolvedValueOnce(personStub.primaryPerson); diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index a413c688f0..cd484c230b 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -1,8 +1,8 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { Insertable, Updateable } from 'kysely'; -import { FACE_THUMBNAIL_SIZE, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; -import { StorageCore } from 'src/cores/storage.core'; -import { AssetFaces, FaceSearch, Person } from 'src/db'; +import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; +import { Person } from 'src/database'; +import { AssetFaces, FaceSearch } from 'src/db'; import { Chunked, OnJob } from 'src/decorators'; import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -24,9 +24,8 @@ import { PersonUpdateDto, } from 'src/dtos/person.dto'; import { - AssetType, + AssetVisibility, CacheControl, - ImageFormat, JobName, JobStatus, Permission, @@ -34,16 +33,15 @@ import { QueueName, SourceType, SystemMetadataKey, + VectorIndex, } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { BoundingBox } from 'src/repositories/machine-learning.repository'; import { UpdateFacesData } from 'src/repositories/person.repository'; import { BaseService } from 'src/services/base.service'; -import { CropOptions, ImageDimensions, InputDimensions, JobItem, JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { ImmichFileResponse } from 'src/utils/file'; import { mimeTypes } from 'src/utils/mime-types'; -import { isFaceImportEnabled, isFacialRecognitionEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; +import { isFacialRecognitionEnabled } from 'src/utils/misc'; @Injectable() export class PersonService extends BaseService { @@ -262,25 +260,22 @@ export class PersonService extends BaseService { if (force) { await this.personRepository.deleteFaces({ sourceType: SourceType.MACHINE_LEARNING }); await this.handlePersonCleanup(); + await this.personRepository.vacuum({ reindexVectors: true }); } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force === false - ? this.assetRepository.getWithout(pagination, WithoutProperty.FACES) - : this.assetRepository.getAll(pagination, { - orderDirection: 'desc', - withFaces: true, - withArchived: true, - isVisible: true, - }); - }); + let jobs: JobItem[] = []; + const assets = this.assetJobRepository.streamForDetectFacesJob(force); + for await (const asset of assets) { + jobs.push({ name: JobName.FACE_DETECTION, data: { id: asset.id } }); - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.FACE_DETECTION, data: { id: asset.id } })), - ); + if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(jobs); + jobs = []; + } } + await this.jobRepository.queueAll(jobs); + if (force === undefined) { await this.jobRepository.queue({ name: JobName.PERSON_CLEANUP }); } @@ -301,7 +296,7 @@ export class PersonService extends BaseService { return JobStatus.FAILED; } - if (!asset.isVisible) { + if (asset.visibility === AssetVisibility.HIDDEN) { return JobStatus.SKIPPED; } @@ -315,6 +310,7 @@ export class PersonService extends BaseService { const facesToAdd: (Insertable & { id: string })[] = []; const embeddings: FaceSearch[] = []; const mlFaceIds = new Set(); + for (const face of asset.faces) { if (face.sourceType === SourceType.MACHINE_LEARNING) { mlFaceIds.add(face.id); @@ -415,6 +411,7 @@ export class PersonService extends BaseService { if (force) { await this.personRepository.unassignFaces({ sourceType: SourceType.MACHINE_LEARNING }); await this.handlePersonCleanup(); + await this.personRepository.vacuum({ reindexVectors: false }); } else if (waiting) { this.logger.debug( `Skipping facial recognition queueing because ${waiting} job${waiting > 1 ? 's are' : ' is'} already queued`, @@ -422,6 +419,8 @@ export class PersonService extends BaseService { return JobStatus.SKIPPED; } + await this.databaseRepository.prewarm(VectorIndex.FACE); + const lastRun = new Date().toISOString(); const facePagination = this.personRepository.getAllFaces( force ? undefined : { personId: null, sourceType: SourceType.MACHINE_LEARNING }, @@ -477,7 +476,7 @@ export class PersonService extends BaseService { embedding: face.faceSearch.embedding, maxDistance: machineLearning.facialRecognition.maxDistance, numResults: machineLearning.facialRecognition.minFaces, - minBirthDate: face.asset.fileCreatedAt, + minBirthDate: face.asset.fileCreatedAt ?? undefined, }); // `matches` also includes the face itself @@ -488,7 +487,9 @@ export class PersonService extends BaseService { this.logger.debug(`Face ${id} has ${matches.length} matches`); - const isCore = matches.length >= machineLearning.facialRecognition.minFaces && !face.asset.isArchived; + const isCore = + matches.length >= machineLearning.facialRecognition.minFaces && + face.asset.visibility === AssetVisibility.TIMELINE; if (!isCore && !deferred) { this.logger.debug(`Deferring non-core face ${id} for later processing`); await this.jobRepository.queue({ name: JobName.FACIAL_RECOGNITION, data: { id, deferred: true } }); @@ -503,7 +504,7 @@ export class PersonService extends BaseService { maxDistance: machineLearning.facialRecognition.maxDistance, numResults: 1, hasPerson: true, - minBirthDate: face.asset.fileCreatedAt, + minBirthDate: face.asset.fileCreatedAt ?? undefined, }); if (matchWithPerson.length > 0) { @@ -538,41 +539,6 @@ export class PersonService extends BaseService { return JobStatus.SUCCESS; } - @OnJob({ name: JobName.GENERATE_PERSON_THUMBNAIL, queue: QueueName.THUMBNAIL_GENERATION }) - async handleGeneratePersonThumbnail({ id }: JobOf): Promise { - const { machineLearning, metadata, image } = await this.getConfig({ withCache: true }); - if (!isFacialRecognitionEnabled(machineLearning) && !isFaceImportEnabled(metadata)) { - return JobStatus.SKIPPED; - } - - const data = await this.personRepository.getDataForThumbnailGenerationJob(id); - if (!data) { - this.logger.error(`Could not generate person thumbnail for ${id}: missing data`); - return JobStatus.FAILED; - } - - const { ownerId, x1, y1, x2, y2, oldWidth, oldHeight } = data; - - const { width, height, inputPath } = await this.getInputDimensions(data); - - const thumbnailPath = StorageCore.getPersonThumbnailPath({ id, ownerId }); - this.storageCore.ensureFolders(thumbnailPath); - - const thumbnailOptions = { - colorspace: image.colorspace, - format: ImageFormat.JPEG, - size: FACE_THUMBNAIL_SIZE, - quality: image.thumbnail.quality, - crop: this.getCrop({ old: { width: oldWidth, height: oldHeight }, new: { width, height } }, { x1, y1, x2, y2 }), - processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true', - }; - - await this.mediaRepository.generateThumbnail(inputPath, thumbnailOptions, thumbnailPath); - await this.personRepository.update({ id, thumbnailPath }); - - return JobStatus.SUCCESS; - } - async mergePerson(auth: AuthDto, id: string, dto: MergePersonDto): Promise { const mergeIds = dto.ids; if (mergeIds.includes(id)) { @@ -643,57 +609,6 @@ export class PersonService extends BaseService { return person; } - private async getInputDimensions(asset: { - type: AssetType; - exifImageWidth: number; - exifImageHeight: number; - previewPath: string; - originalPath: string; - oldWidth: number; - oldHeight: number; - }): Promise { - if (asset.type === AssetType.IMAGE) { - let { exifImageWidth: width, exifImageHeight: height } = asset; - if (asset.oldHeight > asset.oldWidth !== height > width) { - [width, height] = [height, width]; - } - - return { width, height, inputPath: asset.originalPath }; - } - - const { width, height } = await this.mediaRepository.getImageDimensions(asset.previewPath); - return { width, height, inputPath: asset.previewPath }; - } - - private getCrop(dims: { old: ImageDimensions; new: ImageDimensions }, { x1, y1, x2, y2 }: BoundingBox): CropOptions { - const widthScale = dims.new.width / dims.old.width; - const heightScale = dims.new.height / dims.old.height; - - const halfWidth = (widthScale * (x2 - x1)) / 2; - const halfHeight = (heightScale * (y2 - y1)) / 2; - - const middleX = Math.round(widthScale * x1 + halfWidth); - const middleY = Math.round(heightScale * y1 + halfHeight); - - // zoom out 10% - const targetHalfSize = Math.floor(Math.max(halfWidth, halfHeight) * 1.1); - - // get the longest distance from the center of the image without overflowing - const newHalfSize = Math.min( - middleX - Math.max(0, middleX - targetHalfSize), - middleY - Math.max(0, middleY - targetHalfSize), - Math.min(dims.new.width - 1, middleX + targetHalfSize) - middleX, - Math.min(dims.new.height - 1, middleY + targetHalfSize) - middleY, - ); - - return { - left: middleX - newHalfSize, - top: middleY - newHalfSize, - width: newHalfSize * 2, - height: newHalfSize * 2, - }; - } - // TODO return a asset face response async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise { await Promise.all([ diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index 51c6b55e11..d87ccbde1d 100644 --- a/server/src/services/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -39,13 +39,36 @@ describe(SearchService.name, () => { }); }); + describe('searchPlaces', () => { + it('should search places', async () => { + mocks.search.searchPlaces.mockResolvedValue([ + { + id: 42, + name: 'my place', + latitude: 420, + longitude: 69, + admin1Code: null, + admin1Name: null, + admin2Code: null, + admin2Name: null, + alternateNames: null, + countryCode: 'US', + modificationDate: new Date(), + }, + ]); + + await sut.searchPlaces({ name: 'place' }); + expect(mocks.search.searchPlaces).toHaveBeenCalledWith('place'); + }); + }); + describe('getExploreData', () => { it('should get assets by city and tag', async () => { mocks.asset.getAssetIdByCity.mockResolvedValue({ fieldName: 'exifInfo.city', items: [{ value: 'test-city', data: assetStub.withLocation.id }], }); - mocks.asset.getByIdsWithAllRelations.mockResolvedValue([assetStub.withLocation]); + mocks.asset.getByIdsWithAllRelationsButStacks.mockResolvedValue([assetStub.withLocation]); const expectedResponse = [ { fieldName: 'exifInfo.city', items: [{ value: 'test-city', data: mapAsset(assetStub.withLocation) }] }, ]; diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 1c0c0ad490..df286d1809 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { AssetMapOptions, AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { @@ -14,9 +14,7 @@ import { SearchSuggestionType, SmartSearchDto, } from 'src/dtos/search.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; import { AssetOrder } from 'src/enum'; -import { SearchExploreItem } from 'src/repositories/search.repository'; import { BaseService } from 'src/services/base.service'; import { getMyPartnerIds } from 'src/utils/asset.util'; import { isSmartSearchEnabled } from 'src/utils/misc'; @@ -33,10 +31,10 @@ export class SearchService extends BaseService { return places.map((place) => mapPlaces(place)); } - async getExploreData(auth: AuthDto): Promise[]> { + async getExploreData(auth: AuthDto) { const options = { maxFields: 12, minAssetsPerField: 5 }; const cities = await this.assetRepository.getAssetIdByCity(auth.user.id, options); - const assets = await this.assetRepository.getByIdsWithAllRelations(cities.items.map(({ data }) => data)); + const assets = await this.assetRepository.getByIdsWithAllRelationsButStacks(cities.items.map(({ data }) => data)); const items = assets.map((asset) => ({ value: asset.exifInfo!.city!, data: mapAsset(asset, { auth }) })); return [{ fieldName: cities.fieldName, items }]; } @@ -139,7 +137,7 @@ export class SearchService extends BaseService { return [auth.user.id, ...partnerIds]; } - private mapResponse(assets: AssetEntity[], nextPage: string | null, options: AssetMapOptions): SearchResponseDto { + private mapResponse(assets: MapAsset[], nextPage: string | null, options: AssetMapOptions): SearchResponseDto { return { albums: { total: 0, count: 0, items: [], facets: [] }, assets: { diff --git a/server/src/services/session.service.spec.ts b/server/src/services/session.service.spec.ts index c3ab5619be..7ac338da80 100644 --- a/server/src/services/session.service.spec.ts +++ b/server/src/services/session.service.spec.ts @@ -17,29 +17,9 @@ describe('SessionService', () => { }); describe('handleCleanup', () => { - it('should return skipped if nothing is to be deleted', async () => { - mocks.session.search.mockResolvedValue([]); - await expect(sut.handleCleanup()).resolves.toEqual(JobStatus.SKIPPED); - expect(mocks.session.search).toHaveBeenCalled(); - }); - - it('should delete sessions', async () => { - mocks.session.search.mockResolvedValue([ - { - createdAt: new Date('1970-01-01T00:00:00.00Z'), - updatedAt: new Date('1970-01-02T00:00:00.00Z'), - deviceOS: '', - deviceType: '', - id: '123', - token: '420', - userId: '42', - updateId: 'uuid-v7', - }, - ]); - mocks.session.delete.mockResolvedValue(); - + it('should clean sessions', async () => { + mocks.session.cleanup.mockResolvedValue([]); await expect(sut.handleCleanup()).resolves.toEqual(JobStatus.SUCCESS); - expect(mocks.session.delete).toHaveBeenCalledWith('123'); }); }); diff --git a/server/src/services/session.service.ts b/server/src/services/session.service.ts index 6b0632cd44..059ff00e16 100644 --- a/server/src/services/session.service.ts +++ b/server/src/services/session.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; import { DateTime } from 'luxon'; import { OnJob } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; -import { SessionResponseDto, mapSession } from 'src/dtos/session.dto'; +import { SessionCreateDto, SessionCreateResponseDto, SessionResponseDto, mapSession } from 'src/dtos/session.dto'; import { JobName, JobStatus, Permission, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; @@ -10,16 +10,8 @@ import { BaseService } from 'src/services/base.service'; export class SessionService extends BaseService { @OnJob({ name: JobName.CLEAN_OLD_SESSION_TOKENS, queue: QueueName.BACKGROUND_TASK }) async handleCleanup(): Promise { - const sessions = await this.sessionRepository.search({ - updatedBefore: DateTime.now().minus({ days: 90 }).toJSDate(), - }); - - if (sessions.length === 0) { - return JobStatus.SKIPPED; - } - + const sessions = await this.sessionRepository.cleanup(); for (const session of sessions) { - await this.sessionRepository.delete(session.id); this.logger.verbose(`Deleted expired session token: ${session.deviceOS}/${session.deviceType}`); } @@ -28,6 +20,25 @@ export class SessionService extends BaseService { return JobStatus.SUCCESS; } + async create(auth: AuthDto, dto: SessionCreateDto): Promise { + if (!auth.session) { + throw new BadRequestException('This endpoint can only be used with a session token'); + } + + const token = this.cryptoRepository.randomBytesAsText(32); + const tokenHashed = this.cryptoRepository.hashSha256(token); + const session = await this.sessionRepository.create({ + parentId: auth.session.id, + userId: auth.user.id, + expiresAt: dto.duration ? DateTime.now().plus({ seconds: dto.duration }).toJSDate() : null, + deviceType: dto.deviceType, + deviceOS: dto.deviceOS, + token: tokenHashed, + }); + + return { ...mapSession(session), token }; + } + async getAll(auth: AuthDto): Promise { const sessions = await this.sessionRepository.getByUserId(auth.user.id); return sessions.map((session) => mapSession(session, auth.session?.id)); @@ -38,6 +49,11 @@ export class SessionService extends BaseService { await this.sessionRepository.delete(id); } + async lock(auth: AuthDto, id: string): Promise { + await this.requireAccess({ auth, permission: Permission.SESSION_LOCK, ids: [id] }); + await this.sessionRepository.update(id, { pinExpiresAt: null }); + } + async deleteAll(auth: AuthDto): Promise { const sessions = await this.sessionRepository.getByUserId(auth.user.id); for (const session of sessions) { diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 4d084d6e67..b3b4c4b1cf 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -156,6 +156,7 @@ describe(SharedLinkService.name, () => { expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( authStub.admin.user.id, new Set([assetStub.image.id]), + false, ); expect(mocks.sharedLink.create).toHaveBeenCalledWith({ type: SharedLinkType.INDIVIDUAL, @@ -186,6 +187,7 @@ describe(SharedLinkService.name, () => { expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( authStub.admin.user.id, new Set([assetStub.image.id]), + false, ); expect(mocks.sharedLink.create).toHaveBeenCalledWith({ type: SharedLinkType.INDIVIDUAL, @@ -244,7 +246,7 @@ describe(SharedLinkService.name, () => { await sut.remove(authStub.user1, sharedLinkStub.valid.id); expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); - expect(mocks.sharedLink.remove).toHaveBeenCalledWith(sharedLinkStub.valid); + expect(mocks.sharedLink.remove).toHaveBeenCalledWith(sharedLinkStub.valid.id); }); }); @@ -333,8 +335,7 @@ describe(SharedLinkService.name, () => { }); it('should return metadata tags with a default image path if the asset id is not set', async () => { - mocks.sharedLink.get.mockResolvedValue({ ...sharedLinkStub.individual, album: undefined, assets: [] }); - + mocks.sharedLink.get.mockResolvedValue({ ...sharedLinkStub.individual, album: null, assets: [] }); await expect(sut.getMetadataTags(authStub.adminSharedLink)).resolves.toEqual({ description: '0 shared photos & videos', imageUrl: `https://my.immich.app/feature-panel.png`, diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 95f8cef5f8..17f1b974d2 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { SharedLink } from 'src/database'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -11,7 +12,6 @@ import { SharedLinkResponseDto, SharedLinkSearchDto, } from 'src/dtos/shared-link.dto'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { Permission, SharedLinkType } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { getExternalDomain, OpenGraphTags } from 'src/utils/misc'; @@ -98,7 +98,7 @@ export class SharedLinkService extends BaseService { async remove(auth: AuthDto, id: string): Promise { const sharedLink = await this.findOrFail(auth.user.id, id); - await this.sharedLinkRepository.remove(sharedLink); + await this.sharedLinkRepository.remove(sharedLink.id); } // TODO: replace `userId` with permissions and access control checks @@ -182,7 +182,7 @@ export class SharedLinkService extends BaseService { const config = await this.getConfig({ withCache: true }); const sharedLink = await this.findOrFail(auth.sharedLink.userId, auth.sharedLink.id); const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; - const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets.length || 0; + const assetCount = sharedLink.assets.length > 0 ? sharedLink.assets.length : sharedLink.album?.assets?.length || 0; const imagePath = assetId ? `/api/assets/${assetId}/thumbnail?key=${sharedLink.key.toString('base64url')}` : '/feature-panel.png'; @@ -194,11 +194,11 @@ export class SharedLinkService extends BaseService { }; } - private mapToSharedLink(sharedLink: SharedLinkEntity, { withExif }: { withExif: boolean }) { + private mapToSharedLink(sharedLink: SharedLink, { withExif }: { withExif: boolean }) { return withExif ? mapSharedLink(sharedLink) : mapSharedLinkWithoutMetadata(sharedLink); } - private validateAndRefreshToken(sharedLink: SharedLinkEntity, dto: SharedLinkPasswordDto): string { + private validateAndRefreshToken(sharedLink: SharedLink, dto: SharedLinkPasswordDto): string { const token = this.cryptoRepository.hashSha256(`${sharedLink.id}-${sharedLink.password}`); const sharedLinkTokens = dto.token?.split(',') || []; if (sharedLink.password !== dto.password && !sharedLinkTokens.includes(token)) { diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index df26e69108..a6529fa623 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,11 +1,10 @@ import { SystemConfig } from 'src/config'; import { ImmichWorker, JobName, JobStatus } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; import { SmartInfoService } from 'src/services/smart-info.service'; import { getCLIPModelInfo } from 'src/utils/misc'; import { assetStub } from 'test/fixtures/asset.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; -import { newTestService, ServiceMocks } from 'test/utils'; +import { makeStream, newTestService, ServiceMocks } from 'test/utils'; describe(SmartInfoService.name, () => { let sut: SmartInfoService; @@ -55,55 +54,28 @@ describe(SmartInfoService.name, () => { it('should return if machine learning is disabled', async () => { await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningDisabled as SystemConfig }); - expect(mocks.search.getDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); }); it('should return if model and DB dimension size are equal', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); + mocks.database.getDimensionSize.mockResolvedValue(512); await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningEnabled as SystemConfig }); - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).toHaveBeenCalledTimes(1); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); }); it('should update DB dimension size if model and DB have different values', async () => { - mocks.search.getDimensionSize.mockResolvedValue(768); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); + mocks.database.getDimensionSize.mockResolvedValue(768); await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningEnabled as SystemConfig }); - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(512); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); - }); - - it('should skip pausing and resuming queue if already paused', async () => { - mocks.search.getDimensionSize.mockResolvedValue(768); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: true }); - - await sut.onConfigInit({ newConfig: systemConfigStub.machineLearningEnabled as SystemConfig }); - - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(512); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).toHaveBeenCalledTimes(1); + expect(mocks.database.setDimensionSize).toHaveBeenCalledWith(512); }); }); @@ -117,17 +89,13 @@ describe(SmartInfoService.name, () => { }); expect(mocks.systemMetadata.get).not.toHaveBeenCalled(); - expect(mocks.search.getDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); }); it('should return if model and DB dimension size are equal', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); + mocks.database.getDimensionSize.mockResolvedValue(512); await sut.onConfigUpdate({ newConfig: { @@ -138,18 +106,13 @@ describe(SmartInfoService.name, () => { } as SystemConfig, }); - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).not.toHaveBeenCalled(); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).not.toHaveBeenCalled(); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).toHaveBeenCalledTimes(1); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); + expect(mocks.database.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); }); it('should update DB dimension size if model and DB have different values', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); + mocks.database.getDimensionSize.mockResolvedValue(512); await sut.onConfigUpdate({ newConfig: { @@ -160,17 +123,12 @@ describe(SmartInfoService.name, () => { } as SystemConfig, }); - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).toHaveBeenCalledWith(768); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); + expect(mocks.database.getDimensionSize).toHaveBeenCalledTimes(1); + expect(mocks.database.setDimensionSize).toHaveBeenCalledWith(768); }); it('should clear embeddings if old and new models are different', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); + mocks.database.getDimensionSize.mockResolvedValue(512); await sut.onConfigUpdate({ newConfig: { @@ -181,34 +139,9 @@ describe(SmartInfoService.name, () => { } as SystemConfig, }); - expect(mocks.search.deleteAllSearchEmbeddings).toHaveBeenCalled(); - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).toHaveBeenCalledTimes(1); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).toHaveBeenCalledTimes(1); - }); - - it('should skip pausing and resuming queue if already paused', async () => { - mocks.search.getDimensionSize.mockResolvedValue(512); - mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: true }); - - await sut.onConfigUpdate({ - newConfig: { - machineLearning: { clip: { modelName: 'ViT-B-32__openai', enabled: true }, enabled: true }, - } as SystemConfig, - oldConfig: { - machineLearning: { clip: { modelName: 'ViT-B-16__openai', enabled: true }, enabled: true }, - } as SystemConfig, - }); - - expect(mocks.search.getDimensionSize).toHaveBeenCalledTimes(1); - expect(mocks.search.setDimensionSize).not.toHaveBeenCalled(); - expect(mocks.job.getQueueStatus).toHaveBeenCalledTimes(1); - expect(mocks.job.pause).not.toHaveBeenCalled(); - expect(mocks.job.waitForQueueCompletion).toHaveBeenCalledTimes(1); - expect(mocks.job.resume).not.toHaveBeenCalled(); + expect(mocks.database.deleteAllSearchEmbeddings).toHaveBeenCalled(); + expect(mocks.database.getDimensionSize).toHaveBeenCalledTimes(1); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); }); }); @@ -218,38 +151,31 @@ describe(SmartInfoService.name, () => { await sut.handleQueueEncodeClip({}); - expect(mocks.asset.getAll).not.toHaveBeenCalled(); - expect(mocks.asset.getWithout).not.toHaveBeenCalled(); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); }); it('should queue the assets without clip embeddings', async () => { - mocks.asset.getWithout.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForEncodeClip.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueEncodeClip({ force: false }); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }, ]); - expect(mocks.asset.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.SMART_SEARCH); - expect(mocks.search.deleteAllSearchEmbeddings).not.toHaveBeenCalled(); + expect(mocks.assetJob.streamForEncodeClip).toHaveBeenCalledWith(false); + expect(mocks.database.setDimensionSize).not.toHaveBeenCalled(); }); it('should queue all the assets', async () => { - mocks.asset.getAll.mockResolvedValue({ - items: [assetStub.image], - hasNextPage: false, - }); + mocks.assetJob.streamForEncodeClip.mockReturnValue(makeStream([assetStub.image])); await sut.handleQueueEncodeClip({ force: true }); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { name: JobName.SMART_SEARCH, data: { id: assetStub.image.id } }, ]); - expect(mocks.asset.getAll).toHaveBeenCalled(); - expect(mocks.search.deleteAllSearchEmbeddings).toHaveBeenCalled(); + expect(mocks.assetJob.streamForEncodeClip).toHaveBeenCalledWith(true); + expect(mocks.database.setDimensionSize).toHaveBeenCalledExactlyOnceWith(512); }); }); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index 411114eb17..705e8ed2e5 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -2,13 +2,11 @@ import { Injectable } from '@nestjs/common'; import { SystemConfig } from 'src/config'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { OnEvent, OnJob } from 'src/decorators'; -import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; -import { WithoutProperty } from 'src/repositories/asset.repository'; +import { AssetVisibility, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; -import { JobOf } from 'src/types'; +import { JobItem, JobOf } from 'src/types'; import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc'; -import { usePagination } from 'src/utils/pagination'; @Injectable() export class SmartInfoService extends BaseService { @@ -40,7 +38,7 @@ export class SmartInfoService extends BaseService { await this.databaseRepository.withLock(DatabaseLock.CLIPDimSize, async () => { const { dimSize } = getCLIPModelInfo(newConfig.machineLearning.clip.modelName); - const dbDimSize = await this.searchRepository.getDimensionSize(); + const dbDimSize = await this.databaseRepository.getDimensionSize('smart_search'); this.logger.verbose(`Current database CLIP dimension size is ${dbDimSize}`); const modelChange = @@ -50,26 +48,19 @@ export class SmartInfoService extends BaseService { return; } - const { isPaused } = await this.jobRepository.getQueueStatus(QueueName.SMART_SEARCH); - if (!isPaused) { - await this.jobRepository.pause(QueueName.SMART_SEARCH); - } - await this.jobRepository.waitForQueueCompletion(QueueName.SMART_SEARCH); - if (dimSizeChange) { this.logger.log( `Dimension size of model ${newConfig.machineLearning.clip.modelName} is ${dimSize}, but database expects ${dbDimSize}.`, ); this.logger.log(`Updating database CLIP dimension size to ${dimSize}.`); - await this.searchRepository.setDimensionSize(dimSize); + await this.databaseRepository.setDimensionSize(dimSize); this.logger.log(`Successfully updated database CLIP dimension size from ${dbDimSize} to ${dimSize}.`); } else { - await this.searchRepository.deleteAllSearchEmbeddings(); + await this.databaseRepository.deleteAllSearchEmbeddings(); } - if (!isPaused) { - await this.jobRepository.resume(QueueName.SMART_SEARCH); - } + // TODO: A job to reindex all assets should be scheduled, though user + // confirmation should probably be requested before doing that. }); } @@ -81,21 +72,23 @@ export class SmartInfoService extends BaseService { } if (force) { - await this.searchRepository.deleteAllSearchEmbeddings(); + const { dimSize } = getCLIPModelInfo(machineLearning.clip.modelName); + // in addition to deleting embeddings, update the dimension size in case it failed earlier + await this.databaseRepository.setDimensionSize(dimSize); } - const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => { - return force - ? this.assetRepository.getAll(pagination, { isVisible: true }) - : this.assetRepository.getWithout(pagination, WithoutProperty.SMART_SEARCH); - }); - - for await (const assets of assetPagination) { - await this.jobRepository.queueAll( - assets.map((asset) => ({ name: JobName.SMART_SEARCH, data: { id: asset.id } })), - ); + let queue: JobItem[] = []; + const assets = this.assetJobRepository.streamForEncodeClip(force); + for await (const asset of assets) { + queue.push({ name: JobName.SMART_SEARCH, data: { id: asset.id } }); + if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) { + await this.jobRepository.queueAll(queue); + queue = []; + } } + await this.jobRepository.queueAll(queue); + return JobStatus.SUCCESS; } @@ -111,7 +104,7 @@ export class SmartInfoService extends BaseService { return JobStatus.FAILED; } - if (!asset.isVisible) { + if (asset.visibility === AssetVisibility.HIDDEN) { return JobStatus.SKIPPED; } @@ -126,6 +119,12 @@ export class SmartInfoService extends BaseService { await this.databaseRepository.wait(DatabaseLock.CLIPDimSize); } + const newConfig = await this.getConfig({ withCache: true }); + if (machineLearning.clip.modelName !== newConfig.machineLearning.clip.modelName) { + // Skip the job if the the model has changed since the embedding was generated. + return JobStatus.SKIPPED; + } + await this.searchRepository.upsert(asset.id, embedding); return JobStatus.SUCCESS; diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 971a9e8302..9c4fe02f3e 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -69,6 +69,7 @@ describe(StorageTemplateService.name, () => { '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', '{{y}}/{{MM}}/{{filename}}', '{{y}}/{{#if album}}{{album}}{{else}}Other/{{MM}}{{/if}}/{{filename}}', + '{{#if album}}{{album-startDate-y}}/{{album}}{{else}}{{y}}/Other/{{MM}}{{/if}}/{{filename}}', '{{y}}/{{MMM}}/{{filename}}', '{{y}}/{{MMMM}}/{{filename}}', '{{y}}/{{MM}}/{{dd}}/{{filename}}', @@ -182,6 +183,63 @@ describe(StorageTemplateService.name, () => { }); }); + it('should handle album startDate', async () => { + const asset = assetStub.storageAsset(); + const user = userStub.user1; + const album = albumStub.oneAsset; + const config = structuredClone(defaults); + config.storageTemplate.template = + '{{#if album}}{{album-startDate-y}}/{{album-startDate-MM}} - {{album}}{{else}}{{y}}/{{MM}}/{{/if}}/{{filename}}'; + + sut.onConfigInit({ newConfig: config }); + + mocks.user.get.mockResolvedValue(user); + mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(asset); + mocks.album.getByAssetId.mockResolvedValueOnce([album]); + mocks.album.getMetadataForIds.mockResolvedValueOnce([ + { + startDate: asset.fileCreatedAt, + endDate: asset.fileCreatedAt, + albumId: album.id, + assetCount: 1, + lastModifiedAssetTimestamp: null, + }, + ]); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); + expect(mocks.move.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); + + it('should handle else condition from album startDate', async () => { + const asset = assetStub.storageAsset(); + const user = userStub.user1; + const config = structuredClone(defaults); + config.storageTemplate.template = + '{{#if album}}{{album-startDate-y}}/{{album-startDate-MM}} - {{album}}{{else}}{{y}}/{{MM}}/{{/if}}/{{filename}}'; + + sut.onConfigInit({ newConfig: config }); + + mocks.user.get.mockResolvedValue(user); + mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(asset); + + expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS); + + const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); + expect(mocks.move.create).toHaveBeenCalledWith({ + entityId: asset.id, + newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + oldPath: asset.originalPath, + pathType: AssetPathType.ORIGINAL, + }); + }); + it('should migrate previously failed move from original path when it still exists', async () => { mocks.user.get.mockResolvedValue(userStub.user1); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 71a0160ee2..fcba497fa6 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -28,6 +28,7 @@ const storagePresets = [ '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', '{{y}}/{{MM}}/{{filename}}', '{{y}}/{{#if album}}{{album}}{{else}}Other/{{MM}}{{/if}}/{{filename}}', + '{{#if album}}{{album-startDate-y}}/{{album}}{{else}}{{y}}/Other/{{MM}}{{/if}}/{{filename}}', '{{y}}/{{MMM}}/{{filename}}', '{{y}}/{{MMMM}}/{{filename}}', '{{y}}/{{MM}}/{{dd}}/{{filename}}', @@ -54,6 +55,8 @@ interface RenderMetadata { filename: string; extension: string; albumName: string | null; + albumStartDate: Date | null; + albumEndDate: Date | null; } @Injectable() @@ -62,6 +65,7 @@ export class StorageTemplateService extends BaseService { compiled: HandlebarsTemplateDelegate; raw: string; needsAlbum: boolean; + needsAlbumMetadata: boolean; } | null = null; private get template() { @@ -99,6 +103,8 @@ export class StorageTemplateService extends BaseService { filename: 'IMG_123', extension: 'jpg', albumName: 'album', + albumStartDate: new Date(), + albumEndDate: new Date(), }); } catch (error) { this.logger.warn(`Storage template validation failed: ${JSON.stringify(error)}`); @@ -110,6 +116,11 @@ export class StorageTemplateService extends BaseService { return { ...storageTokens, presetOptions: storagePresets }; } + @OnEvent({ name: 'asset.metadataExtracted' }) + async onAssetMetadataExtracted({ source, assetId }: ArgOf<'asset.metadataExtracted'>) { + await this.jobRepository.queue({ name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { source, id: assetId } }); + } + @OnJob({ name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, queue: QueueName.STORAGE_TEMPLATE_MIGRATION }) async handleMigrationSingle({ id }: JobOf): Promise { const config = await this.getConfig({ withCache: true }); @@ -255,9 +266,20 @@ export class StorageTemplateService extends BaseService { } let albumName = null; + let albumStartDate = null; + let albumEndDate = null; if (this.template.needsAlbum) { const albums = await this.albumRepository.getByAssetId(asset.ownerId, asset.id); - albumName = albums?.[0]?.albumName || null; + const album = albums?.[0]; + if (album) { + albumName = album.albumName || null; + + if (this.template.needsAlbumMetadata) { + const [metadata] = await this.albumRepository.getMetadataForIds([album.id]); + albumStartDate = metadata?.startDate || null; + albumEndDate = metadata?.endDate || null; + } + } } const storagePath = this.render(this.template.compiled, { @@ -265,6 +287,8 @@ export class StorageTemplateService extends BaseService { filename: sanitized, extension, albumName, + albumStartDate, + albumEndDate, }); const fullPath = path.normalize(path.join(rootPath, storagePath)); let destination = `${fullPath}.${extension}`; @@ -323,12 +347,13 @@ export class StorageTemplateService extends BaseService { return { raw: template, compiled: handlebar.compile(template, { knownHelpers: undefined, strict: true }), - needsAlbum: template.includes('{{album}}'), + needsAlbum: template.includes('album'), + needsAlbumMetadata: template.includes('album-startDate') || template.includes('album-endDate'), }; } private render(template: HandlebarsTemplateDelegate, options: RenderMetadata) { - const { filename, extension, asset, albumName } = options; + const { filename, extension, asset, albumName, albumStartDate, albumEndDate } = options; const substitutions: Record = { filename, ext: extension, @@ -346,6 +371,15 @@ export class StorageTemplateService extends BaseService { for (const token of Object.values(storageTokens).flat()) { substitutions[token] = dt.toFormat(token); + if (albumName) { + // Use system time zone for album dates to ensure all assets get the exact same date. + substitutions['album-startDate-' + token] = albumStartDate + ? DateTime.fromJSDate(albumStartDate, { zone: systemTimeZone }).toFormat(token) + : ''; + substitutions['album-endDate-' + token] = albumEndDate + ? DateTime.fromJSDate(albumEndDate, { zone: systemTimeZone }).toFormat(token) + : ''; + } } return template(substitutions).replaceAll(/\/{2,}/gm, '/'); diff --git a/server/src/services/sync.service.spec.ts b/server/src/services/sync.service.spec.ts index 5f7357c64d..5b50340a9f 100644 --- a/server/src/services/sync.service.spec.ts +++ b/server/src/services/sync.service.spec.ts @@ -1,5 +1,4 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; import { SyncService } from 'src/services/sync.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; @@ -63,7 +62,7 @@ describe(SyncService.name, () => { it('should return a response requiring a full sync when there are too many changes', async () => { mocks.partner.getAll.mockResolvedValue([]); mocks.asset.getChangedDeltaSync.mockResolvedValue( - Array.from({ length: 10_000 }).fill(assetStub.image), + Array.from({ length: 10_000 }).fill(assetStub.image), ); await expect( sut.getDeltaSync(authStub.user1, { updatedAfter: new Date(), userIds: [authStub.user1.user.id] }), diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index c88348b39e..d6cbc17a29 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -4,7 +4,7 @@ import { DateTime } from 'luxon'; import { Writable } from 'node:stream'; import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; import { SessionSyncCheckpoints } from 'src/db'; -import { AssetResponseDto, hexOrBufferToBase64, mapAsset } from 'src/dtos/asset-response.dto'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetDeltaSyncDto, @@ -14,22 +14,24 @@ import { SyncAckSetDto, SyncStreamDto, } from 'src/dtos/sync.dto'; -import { DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { SyncAck } from 'src/types'; import { getMyPartnerIds } from 'src/utils/asset.util'; +import { hexOrBufferToBase64 } from 'src/utils/bytes'; import { setIsEqual } from 'src/utils/set'; import { fromAck, serialize } from 'src/utils/sync'; const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ - // SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, SyncRequestType.AssetExifsV1, SyncRequestType.PartnerAssetsV1, SyncRequestType.PartnerAssetExifsV1, + SyncRequestType.AlbumsV1, + SyncRequestType.AlbumUsersV1, ]; const throwSessionRequired = () => { @@ -205,6 +207,43 @@ export class SyncService extends BaseService { break; } + case SyncRequestType.AlbumsV1: { + const deletes = this.syncRepository.getAlbumDeletes( + auth.user.id, + checkpointMap[SyncEntityType.AlbumDeleteV1], + ); + for await (const { id, ...data } of deletes) { + response.write(serialize({ type: SyncEntityType.AlbumDeleteV1, updateId: id, data })); + } + + const upserts = this.syncRepository.getAlbumUpserts(auth.user.id, checkpointMap[SyncEntityType.AlbumV1]); + for await (const { updateId, ...data } of upserts) { + response.write(serialize({ type: SyncEntityType.AlbumV1, updateId, data })); + } + + break; + } + + case SyncRequestType.AlbumUsersV1: { + const deletes = this.syncRepository.getAlbumUserDeletes( + auth.user.id, + checkpointMap[SyncEntityType.AlbumUserDeleteV1], + ); + for await (const { id, ...data } of deletes) { + response.write(serialize({ type: SyncEntityType.AlbumUserDeleteV1, updateId: id, data })); + } + + const upserts = this.syncRepository.getAlbumUserUpserts( + auth.user.id, + checkpointMap[SyncEntityType.AlbumUserV1], + ); + for await (const { updateId, ...data } of upserts) { + response.write(serialize({ type: SyncEntityType.AlbumUserV1, updateId, data })); + } + + break; + } + default: { this.logger.warn(`Unsupported sync type: ${type}`); break; @@ -262,7 +301,10 @@ export class SyncService extends BaseService { needsFullSync: false, upserted: upserted // do not return archived assets for partner users - .filter((a) => a.ownerId === auth.user.id || (a.ownerId !== auth.user.id && !a.isArchived)) + .filter( + (a) => + a.ownerId === auth.user.id || (a.ownerId !== auth.user.id && a.visibility === AssetVisibility.TIMELINE), + ) .map((a) => mapAsset(a, { auth, diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 936acf27ad..176e6d6f04 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -6,6 +6,7 @@ import { CQMode, ImageFormat, LogLevel, + OAuthTokenEndpointAuthMethod, QueueName, ToneMapping, TranscodeHWAccel, @@ -119,6 +120,8 @@ const updatedConfig = Object.freeze({ scope: 'openid email profile', signingAlgorithm: 'RS256', profileSigningAlgorithm: 'none', + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.CLIENT_SECRET_POST, + timeout: 30_000, storageLabelClaim: 'preferred_username', storageQuotaClaim: 'immich_quota', }, diff --git a/server/src/services/system-metadata.service.ts b/server/src/services/system-metadata.service.ts index 93449c7a7b..750e6b1d0b 100644 --- a/server/src/services/system-metadata.service.ts +++ b/server/src/services/system-metadata.service.ts @@ -3,6 +3,7 @@ import { AdminOnboardingResponseDto, AdminOnboardingUpdateDto, ReverseGeocodingStateResponseDto, + VersionCheckStateResponseDto, } from 'src/dtos/system-metadata.dto'; import { SystemMetadataKey } from 'src/enum'; import { BaseService } from 'src/services/base.service'; @@ -24,4 +25,9 @@ export class SystemMetadataService extends BaseService { const value = await this.systemMetadataRepository.get(SystemMetadataKey.REVERSE_GEOCODING_STATE); return { lastUpdate: null, lastImportFileName: null, ...value }; } + + async getVersionCheckState(): Promise { + const value = await this.systemMetadataRepository.get(SystemMetadataKey.VERSION_CHECK_STATE); + return { checkedAt: null, releaseVersion: null, ...value }; + } } diff --git a/server/src/services/timeline.service.spec.ts b/server/src/services/timeline.service.spec.ts index c6a09d2fdf..1669b1eac7 100644 --- a/server/src/services/timeline.service.spec.ts +++ b/server/src/services/timeline.service.spec.ts @@ -1,9 +1,7 @@ import { BadRequestException } from '@nestjs/common'; -import { TimeBucketSize } from 'src/repositories/asset.repository'; +import { AssetVisibility } from 'src/enum'; import { TimelineService } from 'src/services/timeline.service'; -import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; -import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; describe(TimelineService.name, () => { @@ -18,13 +16,10 @@ describe(TimelineService.name, () => { it("should return buckets if userId and albumId aren't set", async () => { mocks.asset.getTimeBuckets.mockResolvedValue([{ timeBucket: 'bucket', count: 1 }]); - await expect( - sut.getTimeBuckets(authStub.admin, { - size: TimeBucketSize.DAY, - }), - ).resolves.toEqual(expect.arrayContaining([{ timeBucket: 'bucket', count: 1 }])); + await expect(sut.getTimeBuckets(authStub.admin, {})).resolves.toEqual( + expect.arrayContaining([{ timeBucket: 'bucket', count: 1 }]), + ); expect(mocks.asset.getTimeBuckets).toHaveBeenCalledWith({ - size: TimeBucketSize.DAY, userIds: [authStub.admin.user.id], }); }); @@ -33,133 +28,105 @@ describe(TimelineService.name, () => { describe('getTimeBucket', () => { it('should return the assets for a album time bucket if user has album.read', async () => { mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set(['album-id'])); - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); + const json = `[{ id: ['asset-id'] }]`; + mocks.asset.getTimeBucket.mockResolvedValue({ assets: json }); - await expect( - sut.getTimeBucket(authStub.admin, { size: TimeBucketSize.DAY, timeBucket: 'bucket', albumId: 'album-id' }), - ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); + await expect(sut.getTimeBucket(authStub.admin, { timeBucket: 'bucket', albumId: 'album-id' })).resolves.toEqual( + json, + ); expect(mocks.access.album.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['album-id'])); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, timeBucket: 'bucket', albumId: 'album-id', }); }); it('should return the assets for a archive time bucket if user has archive.read', async () => { - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); + const json = `[{ id: ['asset-id'] }]`; + mocks.asset.getTimeBucket.mockResolvedValue({ assets: json }); await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: true, + visibility: AssetVisibility.ARCHIVE, userId: authStub.admin.user.id, }), - ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); + ).resolves.toEqual(json); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith( 'bucket', expect.objectContaining({ - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: true, + visibility: AssetVisibility.ARCHIVE, userIds: [authStub.admin.user.id], }), ); }); it('should include partner shared assets', async () => { - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); + const json = `[{ id: ['asset-id'] }]`; + mocks.asset.getTimeBucket.mockResolvedValue({ assets: json }); mocks.partner.getAll.mockResolvedValue([]); await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: false, + visibility: AssetVisibility.TIMELINE, userId: authStub.admin.user.id, withPartners: true, }), - ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); + ).resolves.toEqual(json); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: false, + visibility: AssetVisibility.TIMELINE, withPartners: true, userIds: [authStub.admin.user.id], }); }); it('should check permissions to read tag', async () => { - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); + const json = `[{ id: ['asset-id'] }]`; + mocks.asset.getTimeBucket.mockResolvedValue({ assets: json }); mocks.access.tag.checkOwnerAccess.mockResolvedValue(new Set(['tag-123'])); await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', userId: authStub.admin.user.id, tagId: 'tag-123', }), - ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); + ).resolves.toEqual(json); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, tagId: 'tag-123', timeBucket: 'bucket', userIds: [authStub.admin.user.id], }); }); - it('should strip metadata if showExif is disabled', async () => { - mocks.access.album.checkSharedLinkAccess.mockResolvedValue(new Set(['album-id'])); - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); - - const auth = factory.auth({ sharedLink: { showExif: false } }); - - const buckets = await sut.getTimeBucket(auth, { - size: TimeBucketSize.DAY, - timeBucket: 'bucket', - isArchived: true, - albumId: 'album-id', - }); - - expect(buckets).toEqual([expect.objectContaining({ id: 'asset-id' })]); - expect(buckets[0]).not.toHaveProperty('exif'); - expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith('bucket', { - size: TimeBucketSize.DAY, - timeBucket: 'bucket', - isArchived: true, - albumId: 'album-id', - }); - }); - it('should return the assets for a library time bucket if user has library.read', async () => { - mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]); + const json = `[{ id: ['asset-id'] }]`; + mocks.asset.getTimeBucket.mockResolvedValue({ assets: json }); await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', userId: authStub.admin.user.id, }), - ).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })])); + ).resolves.toEqual(json); expect(mocks.asset.getTimeBucket).toHaveBeenCalledWith( 'bucket', expect.objectContaining({ - size: TimeBucketSize.DAY, timeBucket: 'bucket', userIds: [authStub.admin.user.id], }), ); }); - it('should throw an error if withParners is true and isArchived true or undefined', async () => { + it('should throw an error if withParners is true and visibility true or undefined', async () => { await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: true, + visibility: AssetVisibility.ARCHIVE, withPartners: true, userId: authStub.admin.user.id, }), @@ -167,9 +134,8 @@ describe(TimelineService.name, () => { await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', - isArchived: undefined, + visibility: undefined, withPartners: true, userId: authStub.admin.user.id, }), @@ -179,7 +145,6 @@ describe(TimelineService.name, () => { it('should throw an error if withParners is true and isFavorite is either true or false', async () => { await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', isFavorite: true, withPartners: true, @@ -189,7 +154,6 @@ describe(TimelineService.name, () => { await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', isFavorite: false, withPartners: true, @@ -201,7 +165,6 @@ describe(TimelineService.name, () => { it('should throw an error if withParners is true and isTrash is true', async () => { await expect( sut.getTimeBucket(authStub.admin, { - size: TimeBucketSize.DAY, timeBucket: 'bucket', isTrashed: true, withPartners: true, diff --git a/server/src/services/timeline.service.ts b/server/src/services/timeline.service.ts index 4c2332afaa..f3ebcc2cd7 100644 --- a/server/src/services/timeline.service.ts +++ b/server/src/services/timeline.service.ts @@ -1,30 +1,27 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; -import { Permission } from 'src/enum'; +import { TimeBucketAssetDto, TimeBucketDto, TimeBucketsResponseDto } from 'src/dtos/time-bucket.dto'; +import { AssetVisibility, Permission } from 'src/enum'; import { TimeBucketOptions } from 'src/repositories/asset.repository'; import { BaseService } from 'src/services/base.service'; import { getMyPartnerIds } from 'src/utils/asset.util'; @Injectable() export class TimelineService extends BaseService { - async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise { + async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise { await this.timeBucketChecks(auth, dto); const timeBucketOptions = await this.buildTimeBucketOptions(auth, dto); - return this.assetRepository.getTimeBuckets(timeBucketOptions); + return await this.assetRepository.getTimeBuckets(timeBucketOptions); } - async getTimeBucket( - auth: AuthDto, - dto: TimeBucketAssetDto, - ): Promise { + // pre-jsonified response + async getTimeBucket(auth: AuthDto, dto: TimeBucketAssetDto): Promise { await this.timeBucketChecks(auth, dto); - const timeBucketOptions = await this.buildTimeBucketOptions(auth, dto); - const assets = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions); - return !auth.sharedLink || auth.sharedLink?.showExif - ? assets.map((asset) => mapAsset(asset, { withStack: true, auth })) - : assets.map((asset) => mapAsset(asset, { stripMetadata: true, auth })); + const timeBucketOptions = await this.buildTimeBucketOptions(auth, { ...dto }); + + // TODO: use id cursor for pagination + const bucket = await this.assetRepository.getTimeBucket(dto.timeBucket, timeBucketOptions); + return bucket.assets; } private async buildTimeBucketOptions(auth: AuthDto, dto: TimeBucketDto): Promise { @@ -55,7 +52,7 @@ export class TimelineService extends BaseService { if (dto.userId) { await this.requireAccess({ auth, permission: Permission.TIMELINE_READ, ids: [dto.userId] }); - if (dto.isArchived !== false) { + if (dto.visibility === AssetVisibility.ARCHIVE) { await this.requireAccess({ auth, permission: Permission.ARCHIVE_READ, ids: [dto.userId] }); } } @@ -65,7 +62,7 @@ export class TimelineService extends BaseService { } if (dto.withPartners) { - const requestedArchived = dto.isArchived === true || dto.isArchived === undefined; + const requestedArchived = dto.visibility === AssetVisibility.ARCHIVE || dto.visibility === undefined; const requestedFavorite = dto.isFavorite === true || dto.isFavorite === false; const requestedTrash = dto.isTrashed === true; diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 0cba749d36..dcd415174d 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -1,5 +1,6 @@ import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common'; import { SALT_ROUNDS } from 'src/constants'; +import { AssetStatsDto, AssetStatsResponseDto, mapStats } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto'; import { @@ -18,7 +19,10 @@ import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/uti @Injectable() export class UserAdminService extends BaseService { async search(auth: AuthDto, dto: UserAdminSearchDto): Promise { - const users = await this.userRepository.getList({ withDeleted: dto.withDeleted }); + const users = await this.userRepository.getList({ + id: dto.id, + withDeleted: dto.withDeleted, + }); return users.map((user) => mapUserAdmin(user)); } @@ -70,6 +74,10 @@ export class UserAdminService extends BaseService { dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS); } + if (dto.pinCode) { + dto.pinCode = await this.cryptoRepository.hashBcrypt(dto.pinCode, SALT_ROUNDS); + } + if (dto.storageLabel === '') { dto.storageLabel = null; } @@ -105,22 +113,25 @@ export class UserAdminService extends BaseService { return mapUserAdmin(user); } + async getStatistics(auth: AuthDto, id: string, dto: AssetStatsDto): Promise { + const stats = await this.assetRepository.getStatistics(id, dto); + return mapStats(stats); + } + async getPreferences(auth: AuthDto, id: string): Promise { - const { email } = await this.findOrFail(id, { withDeleted: true }); + await this.findOrFail(id, { withDeleted: true }); const metadata = await this.userRepository.getMetadata(id); - const preferences = getPreferences(email, metadata); - return mapPreferences(preferences); + return mapPreferences(getPreferences(metadata)); } async updatePreferences(auth: AuthDto, id: string, dto: UserPreferencesUpdateDto) { - const { email } = await this.findOrFail(id, { withDeleted: false }); + await this.findOrFail(id, { withDeleted: false }); const metadata = await this.userRepository.getMetadata(id); - const preferences = getPreferences(email, metadata); - const newPreferences = mergePreferences(preferences, dto); + const newPreferences = mergePreferences(getPreferences(metadata), dto); await this.userRepository.upsertMetadata(id, { key: UserMetadataKey.PREFERENCES, - value: getPreferencesPartial({ email }, newPreferences), + value: getPreferencesPartial(newPreferences), }); return mapPreferences(newPreferences); diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index 327328eb1c..a0304d51ad 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -53,6 +53,7 @@ export class UserService extends BaseService { const update: Updateable = { email: dto.email, name: dto.name, + avatarColor: dto.avatarColor, }; if (dto.password) { @@ -68,18 +69,16 @@ export class UserService extends BaseService { async getMyPreferences(auth: AuthDto): Promise { const metadata = await this.userRepository.getMetadata(auth.user.id); - const preferences = getPreferences(auth.user.email, metadata); - return mapPreferences(preferences); + return mapPreferences(getPreferences(metadata)); } async updateMyPreferences(auth: AuthDto, dto: UserPreferencesUpdateDto) { const metadata = await this.userRepository.getMetadata(auth.user.id); - const current = getPreferences(auth.user.email, metadata); - const updated = mergePreferences(current, dto); + const updated = mergePreferences(getPreferences(metadata), dto); await this.userRepository.upsertMetadata(auth.user.id, { key: UserMetadataKey.PREFERENCES, - value: getPreferencesPartial(auth.user, updated), + value: getPreferencesPartial(updated), }); return mapPreferences(updated); diff --git a/server/src/services/view.service.ts b/server/src/services/view.service.ts index 5871b04b32..9d1ee3cf89 100644 --- a/server/src/services/view.service.ts +++ b/server/src/services/view.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AssetEntity } from 'src/entities/asset.entity'; import { BaseService } from 'src/services/base.service'; @Injectable() @@ -12,6 +11,6 @@ export class ViewService extends BaseService { async getAssetsByOriginalPath(auth: AuthDto, path: string): Promise { const assets = await this.viewRepository.getAssetsByOriginalPath(auth.user.id, path); - return assets.map((asset) => mapAsset(asset as unknown as AssetEntity, { auth })); + return assets.map((asset) => mapAsset(asset, { auth })); } } diff --git a/server/src/sql-tools/from-code/decorators/after-insert.decorator.ts b/server/src/sql-tools/from-code/decorators/after-insert.decorator.ts new file mode 100644 index 0000000000..103d59b4fc --- /dev/null +++ b/server/src/sql-tools/from-code/decorators/after-insert.decorator.ts @@ -0,0 +1,8 @@ +import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/from-code/decorators/trigger-function.decorator'; + +export const AfterInsertTrigger = (options: Omit) => + TriggerFunction({ + timing: 'after', + actions: ['insert'], + ...options, + }); diff --git a/server/src/sql-tools/from-code/decorators/column-index.decorator.ts b/server/src/sql-tools/from-code/decorators/column-index.decorator.ts deleted file mode 100644 index ab15292612..0000000000 --- a/server/src/sql-tools/from-code/decorators/column-index.decorator.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { register } from 'src/sql-tools/from-code/register'; -import { asOptions } from 'src/sql-tools/helpers'; - -export type ColumnIndexOptions = { - name?: string; - unique?: boolean; - expression?: string; - using?: string; - with?: string; - where?: string; - synchronize?: boolean; -}; -export const ColumnIndex = (options: string | ColumnIndexOptions = {}): PropertyDecorator => { - return (object: object, propertyName: string | symbol) => - void register({ type: 'columnIndex', item: { object, propertyName, options: asOptions(options) } }); -}; diff --git a/server/src/sql-tools/from-code/decorators/column.decorator.ts b/server/src/sql-tools/from-code/decorators/column.decorator.ts index 74a83cbcf3..7b00af80cc 100644 --- a/server/src/sql-tools/from-code/decorators/column.decorator.ts +++ b/server/src/sql-tools/from-code/decorators/column.decorator.ts @@ -15,13 +15,15 @@ export type ColumnBaseOptions = { synchronize?: boolean; storage?: ColumnStorage; identity?: boolean; + index?: boolean; + indexName?: string; + unique?: boolean; + uniqueConstraintName?: string; }; export type ColumnOptions = ColumnBaseOptions & { enum?: DatabaseEnum; array?: boolean; - unique?: boolean; - uniqueConstraintName?: string; }; export const Column = (options: string | ColumnOptions = {}): PropertyDecorator => { diff --git a/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts b/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts index 070aa5cb51..beb3aa6fd6 100644 --- a/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts +++ b/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts @@ -7,8 +7,6 @@ export type ForeignKeyColumnOptions = ColumnBaseOptions & { onUpdate?: Action; onDelete?: Action; constraintName?: string; - unique?: boolean; - uniqueConstraintName?: string; }; export const ForeignKeyColumn = (target: () => object, options: ForeignKeyColumnOptions): PropertyDecorator => { diff --git a/server/src/sql-tools/from-code/decorators/index.decorator.ts b/server/src/sql-tools/from-code/decorators/index.decorator.ts index cd76b5e36d..5d90c4f58d 100644 --- a/server/src/sql-tools/from-code/decorators/index.decorator.ts +++ b/server/src/sql-tools/from-code/decorators/index.decorator.ts @@ -1,8 +1,13 @@ -import { ColumnIndexOptions } from 'src/sql-tools/from-code/decorators/column-index.decorator'; import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; -export type IndexOptions = ColumnIndexOptions & { +export type IndexOptions = { + name?: string; + unique?: boolean; + expression?: string; + using?: string; + with?: string; + where?: string; columns?: string[]; synchronize?: boolean; }; diff --git a/server/src/sql-tools/from-code/index.ts b/server/src/sql-tools/from-code/index.ts index 3c74d2763c..95f1dbb22d 100644 --- a/server/src/sql-tools/from-code/index.ts +++ b/server/src/sql-tools/from-code/index.ts @@ -1,6 +1,5 @@ import 'reflect-metadata'; import { processCheckConstraints } from 'src/sql-tools/from-code/processors/check-constraint.processor'; -import { processColumnIndexes } from 'src/sql-tools/from-code/processors/column-index.processor'; import { processColumns } from 'src/sql-tools/from-code/processors/column.processor'; import { processConfigurationParameters } from 'src/sql-tools/from-code/processors/configuration-parameter.processor'; import { processDatabases } from 'src/sql-tools/from-code/processors/database.processor'; @@ -36,14 +35,21 @@ const processors: Processor[] = [ processUniqueConstraints, processCheckConstraints, processPrimaryKeyConstraints, - processIndexes, - processColumnIndexes, processForeignKeyConstraints, + processIndexes, processTriggers, ]; -export const schemaFromCode = () => { +export type SchemaFromCodeOptions = { + /** automatically create indexes on foreign key columns */ + createForeignKeyIndexes?: boolean; +}; +export const schemaFromCode = (options: SchemaFromCodeOptions = {}) => { if (!initialized) { + const globalOptions = { + createForeignKeyIndexes: options.createForeignKeyIndexes ?? true, + }; + const builder: SchemaBuilder = { name: 'postgres', schemaName: 'public', @@ -58,7 +64,7 @@ export const schemaFromCode = () => { const items = getRegisteredItems(); for (const processor of processors) { - processor(builder, items); + processor(builder, items, globalOptions); } schema = { ...builder, tables: builder.tables.map(({ metadata: _, ...table }) => table) }; diff --git a/server/src/sql-tools/from-code/processors/check-constraint.processor.ts b/server/src/sql-tools/from-code/processors/check-constraint.processor.ts index d61ee18277..feb21b9894 100644 --- a/server/src/sql-tools/from-code/processors/check-constraint.processor.ts +++ b/server/src/sql-tools/from-code/processors/check-constraint.processor.ts @@ -1,6 +1,6 @@ import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asCheckConstraintName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; import { DatabaseConstraintType } from 'src/sql-tools/types'; export const processCheckConstraints: Processor = (builder, items) => { @@ -24,3 +24,5 @@ export const processCheckConstraints: Processor = (builder, items) => { }); } }; + +const asCheckConstraintName = (table: string, expression: string) => asKey('CHK_', table, [expression]); diff --git a/server/src/sql-tools/from-code/processors/column-index.processor.ts b/server/src/sql-tools/from-code/processors/column-index.processor.ts deleted file mode 100644 index 0e40fa1ee3..0000000000 --- a/server/src/sql-tools/from-code/processors/column-index.processor.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; -import { onMissingTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asIndexName } from 'src/sql-tools/helpers'; - -export const processColumnIndexes: Processor = (builder, items) => { - for (const { - item: { object, propertyName, options }, - } of items.filter((item) => item.type === 'columnIndex')) { - const { table, column } = resolveColumn(builder, object, propertyName); - if (!table) { - onMissingTable(builder, '@ColumnIndex', object); - continue; - } - - if (!column) { - onMissingColumn(builder, `@ColumnIndex`, object, propertyName); - continue; - } - - table.indexes.push({ - name: options.name || asIndexName(table.name, [column.name], options.where), - tableName: table.name, - unique: options.unique ?? false, - expression: options.expression, - using: options.using, - where: options.where, - columnNames: [column.name], - synchronize: options.synchronize ?? true, - }); - } -}; diff --git a/server/src/sql-tools/from-code/processors/column.processor.ts b/server/src/sql-tools/from-code/processors/column.processor.ts index 37f3f5d082..e8c2544f87 100644 --- a/server/src/sql-tools/from-code/processors/column.processor.ts +++ b/server/src/sql-tools/from-code/processors/column.processor.ts @@ -1,8 +1,8 @@ import { ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor, SchemaBuilder } from 'src/sql-tools/from-code/processors/type'; -import { asMetadataKey, asUniqueConstraintName, fromColumnValue } from 'src/sql-tools/helpers'; -import { DatabaseColumn, DatabaseConstraintType } from 'src/sql-tools/types'; +import { asMetadataKey, fromColumnValue } from 'src/sql-tools/helpers'; +import { DatabaseColumn } from 'src/sql-tools/types'; export const processColumns: Processor = (builder, items) => { for (const { @@ -54,16 +54,6 @@ export const processColumns: Processor = (builder, items) => { writeMetadata(object, propertyName, { name: column.name, options }); table.columns.push(column); - - if (type === 'column' && !options.primary && options.unique) { - table.constraints.push({ - type: DatabaseConstraintType.UNIQUE, - name: options.uniqueConstraintName || asUniqueConstraintName(table.name, [column.name]), - tableName: table.name, - columnNames: [column.name], - synchronize: options.synchronize ?? true, - }); - } } }; diff --git a/server/src/sql-tools/from-code/processors/foreign-key-constriant.processor.ts b/server/src/sql-tools/from-code/processors/foreign-key-constriant.processor.ts index 784a8b8e99..612b74c30f 100644 --- a/server/src/sql-tools/from-code/processors/foreign-key-constriant.processor.ts +++ b/server/src/sql-tools/from-code/processors/foreign-key-constriant.processor.ts @@ -1,7 +1,7 @@ import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asForeignKeyConstraintName, asRelationKeyConstraintName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; import { DatabaseActionType, DatabaseConstraintType } from 'src/sql-tools/types'; export const processForeignKeyConstraints: Processor = (builder, items) => { @@ -46,7 +46,7 @@ export const processForeignKeyConstraints: Processor = (builder, items) => { synchronize: options.synchronize ?? true, }); - if (options.unique) { + if (options.unique || options.uniqueConstraintName) { table.constraints.push({ name: options.uniqueConstraintName || asRelationKeyConstraintName(table.name, columnNames), tableName: table.name, @@ -57,3 +57,6 @@ export const processForeignKeyConstraints: Processor = (builder, items) => { } } }; + +const asForeignKeyConstraintName = (table: string, columns: string[]) => asKey('FK_', table, columns); +const asRelationKeyConstraintName = (table: string, columns: string[]) => asKey('REL_', table, columns); diff --git a/server/src/sql-tools/from-code/processors/index.processor.ts b/server/src/sql-tools/from-code/processors/index.processor.ts index 3625bf9784..f4c9c7cec1 100644 --- a/server/src/sql-tools/from-code/processors/index.processor.ts +++ b/server/src/sql-tools/from-code/processors/index.processor.ts @@ -1,8 +1,9 @@ +import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asIndexName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; -export const processIndexes: Processor = (builder, items) => { +export const processIndexes: Processor = (builder, items, config) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'index')) { @@ -24,4 +25,66 @@ export const processIndexes: Processor = (builder, items) => { synchronize: options.synchronize ?? true, }); } + + // column indexes + for (const { + type, + item: { object, propertyName, options }, + } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { + const { table, column } = resolveColumn(builder, object, propertyName); + if (!table) { + onMissingTable(builder, '@Column', object); + continue; + } + + if (!column) { + // should be impossible since they are created in `column.processor.ts` + onMissingColumn(builder, '@Column', object, propertyName); + continue; + } + + if (options.index === false) { + continue; + } + + const isIndexRequested = + options.indexName || options.index || (type === 'foreignKeyColumn' && config.createForeignKeyIndexes); + if (!isIndexRequested) { + continue; + } + + const indexName = options.indexName || asIndexName(table.name, [column.name]); + + const isIndexPresent = table.indexes.some((index) => index.name === indexName); + if (isIndexPresent) { + continue; + } + + const isOnlyPrimaryColumn = options.primary && table.columns.filter(({ primary }) => primary === true).length === 1; + if (isOnlyPrimaryColumn) { + // will have an index created by the primary key constraint + continue; + } + + table.indexes.push({ + name: indexName, + tableName: table.name, + unique: false, + columnNames: [column.name], + synchronize: options.synchronize ?? true, + }); + } +}; + +const asIndexName = (table: string, columns?: string[], where?: string) => { + const items: string[] = []; + for (const columnName of columns ?? []) { + items.push(columnName); + } + + if (where) { + items.push(where); + } + + return asKey('IDX_', table, items); }; diff --git a/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts b/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts index f123f2e495..74aecc5ea0 100644 --- a/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts +++ b/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts @@ -1,5 +1,5 @@ import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asPrimaryKeyConstraintName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; import { DatabaseConstraintType } from 'src/sql-tools/types'; export const processPrimaryKeyConstraints: Processor = (builder) => { @@ -22,3 +22,5 @@ export const processPrimaryKeyConstraints: Processor = (builder) => { } } }; + +const asPrimaryKeyConstraintName = (table: string, columns: string[]) => asKey('PK_', table, columns); diff --git a/server/src/sql-tools/from-code/processors/table.processor.ts b/server/src/sql-tools/from-code/processors/table.processor.ts index eb4b414576..4ef4e82020 100644 --- a/server/src/sql-tools/from-code/processors/table.processor.ts +++ b/server/src/sql-tools/from-code/processors/table.processor.ts @@ -6,6 +6,13 @@ export const processTables: Processor = (builder, items) => { for (const { item: { options, object }, } of items.filter((item) => item.type === 'table')) { + const test = readMetadata(object); + if (test) { + throw new Error( + `Table ${test.name} has already been registered. Does ${object.name} have two @Table() decorators?`, + ); + } + const tableName = options.name || asSnakeCase(object.name); writeMetadata(object, { name: tableName, options }); diff --git a/server/src/sql-tools/from-code/processors/trigger.processor.ts b/server/src/sql-tools/from-code/processors/trigger.processor.ts index 2f4cc04326..4b875f353b 100644 --- a/server/src/sql-tools/from-code/processors/trigger.processor.ts +++ b/server/src/sql-tools/from-code/processors/trigger.processor.ts @@ -1,6 +1,7 @@ +import { TriggerOptions } from 'src/sql-tools/from-code/decorators/trigger.decorator'; import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asTriggerName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; export const processTriggers: Processor = (builder, items) => { for (const { @@ -26,3 +27,6 @@ export const processTriggers: Processor = (builder, items) => { }); } }; + +const asTriggerName = (table: string, trigger: TriggerOptions) => + asKey('TR_', table, [...trigger.actions, trigger.scope, trigger.timing, trigger.functionName]); diff --git a/server/src/sql-tools/from-code/processors/type.ts b/server/src/sql-tools/from-code/processors/type.ts index 5a69efbcf0..deb142d278 100644 --- a/server/src/sql-tools/from-code/processors/type.ts +++ b/server/src/sql-tools/from-code/processors/type.ts @@ -1,3 +1,4 @@ +import { SchemaFromCodeOptions } from 'src/sql-tools/from-code'; import { TableOptions } from 'src/sql-tools/from-code/decorators/table.decorator'; import { RegisterItem } from 'src/sql-tools/from-code/register-item'; import { DatabaseSchema, DatabaseTable } from 'src/sql-tools/types'; @@ -6,4 +7,4 @@ import { DatabaseSchema, DatabaseTable } from 'src/sql-tools/types'; export type TableWithMetadata = DatabaseTable & { metadata: { options: TableOptions; object: Function } }; export type SchemaBuilder = Omit & { tables: TableWithMetadata[] }; -export type Processor = (builder: SchemaBuilder, items: RegisterItem[]) => void; +export type Processor = (builder: SchemaBuilder, items: RegisterItem[], options: SchemaFromCodeOptions) => void; diff --git a/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts b/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts index 74c0504f7e..9014378085 100644 --- a/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts +++ b/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts @@ -1,6 +1,7 @@ +import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asUniqueConstraintName } from 'src/sql-tools/helpers'; +import { asKey } from 'src/sql-tools/helpers'; import { DatabaseConstraintType } from 'src/sql-tools/types'; export const processUniqueConstraints: Processor = (builder, items) => { @@ -24,4 +25,34 @@ export const processUniqueConstraints: Processor = (builder, items) => { synchronize: options.synchronize ?? true, }); } + + // column level constraints + for (const { + type, + item: { object, propertyName, options }, + } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { + const { table, column } = resolveColumn(builder, object, propertyName); + if (!table) { + onMissingTable(builder, '@Column', object); + continue; + } + + if (!column) { + // should be impossible since they are created in `column.processor.ts` + onMissingColumn(builder, '@Column', object, propertyName); + continue; + } + + if (type === 'column' && !options.primary && (options.unique || options.uniqueConstraintName)) { + table.constraints.push({ + type: DatabaseConstraintType.UNIQUE, + name: options.uniqueConstraintName || asUniqueConstraintName(table.name, [column.name]), + tableName: table.name, + columnNames: [column.name], + synchronize: options.synchronize ?? true, + }); + } + } }; + +const asUniqueConstraintName = (table: string, columns: string[]) => asKey('UQ_', table, columns); diff --git a/server/src/sql-tools/from-code/register-function.ts b/server/src/sql-tools/from-code/register-function.ts index 69e1a0f8f3..3e1e7054be 100644 --- a/server/src/sql-tools/from-code/register-function.ts +++ b/server/src/sql-tools/from-code/register-function.ts @@ -1,5 +1,4 @@ import { register } from 'src/sql-tools/from-code/register'; -import { asFunctionExpression } from 'src/sql-tools/helpers'; import { ColumnType, DatabaseFunction } from 'src/sql-tools/types'; export type FunctionOptions = { @@ -27,3 +26,33 @@ export const registerFunction = (options: FunctionOptions) => { return item; }; + +const asFunctionExpression = (options: FunctionOptions) => { + const name = options.name; + const sql: string[] = [ + `CREATE OR REPLACE FUNCTION ${name}(${(options.arguments || []).join(', ')})`, + `RETURNS ${options.returnType}`, + ]; + + const flags = [ + options.parallel ? `PARALLEL ${options.parallel.toUpperCase()}` : undefined, + options.strict ? 'STRICT' : undefined, + options.behavior ? options.behavior.toUpperCase() : undefined, + `LANGUAGE ${options.language ?? 'SQL'}`, + ].filter((x) => x !== undefined); + + if (flags.length > 0) { + sql.push(flags.join(' ')); + } + + if ('return' in options) { + sql.push(` RETURN ${options.return}`); + } + + if ('body' in options) { + const body = options.body; + sql.push(...(body.includes('\n') ? [`AS $$`, ' ' + body.trim(), `$$;`] : [`AS $$${body}$$;`])); + } + + return sql.join('\n ').trim(); +}; diff --git a/server/src/sql-tools/from-code/register-item.ts b/server/src/sql-tools/from-code/register-item.ts index 08200cbc4f..4889ae34b9 100644 --- a/server/src/sql-tools/from-code/register-item.ts +++ b/server/src/sql-tools/from-code/register-item.ts @@ -1,5 +1,4 @@ import { CheckOptions } from 'src/sql-tools/from-code/decorators/check.decorator'; -import { ColumnIndexOptions } from 'src/sql-tools/from-code/decorators/column-index.decorator'; import { ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; import { ConfigurationParameterOptions } from 'src/sql-tools/from-code/decorators/configuration-parameter.decorator'; import { DatabaseOptions } from 'src/sql-tools/from-code/decorators/database.decorator'; @@ -21,7 +20,6 @@ export type RegisterItem = | { type: 'uniqueConstraint'; item: ClassBased<{ options: UniqueOptions }> } | { type: 'checkConstraint'; item: ClassBased<{ options: CheckOptions }> } | { type: 'column'; item: PropertyBased<{ options: ColumnOptions }> } - | { type: 'columnIndex'; item: PropertyBased<{ options: ColumnIndexOptions }> } | { type: 'function'; item: DatabaseFunction } | { type: 'enum'; item: DatabaseEnum } | { type: 'trigger'; item: ClassBased<{ options: TriggerOptions }> } diff --git a/server/src/sql-tools/helpers.ts b/server/src/sql-tools/helpers.ts index 364b695194..2802407ea6 100644 --- a/server/src/sql-tools/helpers.ts +++ b/server/src/sql-tools/helpers.ts @@ -1,7 +1,5 @@ import { createHash } from 'node:crypto'; import { ColumnValue } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { TriggerOptions } from 'src/sql-tools/from-code/decorators/trigger.decorator'; -import { FunctionOptions } from 'src/sql-tools/from-code/register-function'; import { Comparer, DatabaseColumn, @@ -18,25 +16,6 @@ export const asSnakeCase = (name: string): string => name.replaceAll(/([a-z])([A // match TypeORM export const asKey = (prefix: string, tableName: string, values: string[]) => (prefix + sha1(`${tableName}_${values.toSorted().join('_')}`)).slice(0, 30); -export const asPrimaryKeyConstraintName = (table: string, columns: string[]) => asKey('PK_', table, columns); -export const asForeignKeyConstraintName = (table: string, columns: string[]) => asKey('FK_', table, columns); -export const asTriggerName = (table: string, trigger: TriggerOptions) => - asKey('TR_', table, [...trigger.actions, trigger.scope, trigger.timing, trigger.functionName]); -export const asRelationKeyConstraintName = (table: string, columns: string[]) => asKey('REL_', table, columns); -export const asUniqueConstraintName = (table: string, columns: string[]) => asKey('UQ_', table, columns); -export const asCheckConstraintName = (table: string, expression: string) => asKey('CHK_', table, [expression]); -export const asIndexName = (table: string, columns: string[] | undefined, where: string | undefined) => { - const items: string[] = []; - for (const columnName of columns ?? []) { - items.push(columnName); - } - - if (where) { - items.push(where); - } - - return asKey('IDX_', table, items); -}; export const asOptions = (options: string | T): T => { if (typeof options === 'string') { @@ -46,40 +25,6 @@ export const asOptions = (options: string | T): T = return options; }; -export const asFunctionExpression = (options: FunctionOptions) => { - const name = options.name; - const sql: string[] = [ - `CREATE OR REPLACE FUNCTION ${name}(${(options.arguments || []).join(', ')})`, - `RETURNS ${options.returnType}`, - ]; - - const flags = [ - options.parallel ? `PARALLEL ${options.parallel.toUpperCase()}` : undefined, - options.strict ? 'STRICT' : undefined, - options.behavior ? options.behavior.toUpperCase() : undefined, - `LANGUAGE ${options.language ?? 'SQL'}`, - ].filter((x) => x !== undefined); - - if (flags.length > 0) { - sql.push(flags.join(' ')); - } - - if ('return' in options) { - sql.push(` RETURN ${options.return}`); - } - - if ('body' in options) { - sql.push( - // - `AS $$`, - ' ' + options.body.trim(), - `$$;`, - ); - } - - return sql.join('\n ').trim(); -}; - export const sha1 = (value: string) => createHash('sha1').update(value).digest('hex'); export const hasMask = (input: number, mask: number) => (input & mask) === mask; diff --git a/server/src/sql-tools/public_api.ts b/server/src/sql-tools/public_api.ts index d916678d4a..c7a3023a4d 100644 --- a/server/src/sql-tools/public_api.ts +++ b/server/src/sql-tools/public_api.ts @@ -1,9 +1,9 @@ export { schemaDiff } from 'src/sql-tools/diff'; export { schemaFromCode } from 'src/sql-tools/from-code'; export * from 'src/sql-tools/from-code/decorators/after-delete.decorator'; +export * from 'src/sql-tools/from-code/decorators/after-insert.decorator'; export * from 'src/sql-tools/from-code/decorators/before-update.decorator'; export * from 'src/sql-tools/from-code/decorators/check.decorator'; -export * from 'src/sql-tools/from-code/decorators/column-index.decorator'; export * from 'src/sql-tools/from-code/decorators/column.decorator'; export * from 'src/sql-tools/from-code/decorators/configuration-parameter.decorator'; export * from 'src/sql-tools/from-code/decorators/create-date-column.decorator'; diff --git a/server/src/types.ts b/server/src/types.ts index 88ba644739..d166a94e8b 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,7 +1,8 @@ import { SystemConfig } from 'src/config'; +import { VECTOR_EXTENSIONS } from 'src/constants'; import { AssetType, - DatabaseExtension, + DatabaseSslMode, ExifOrientation, ImageFormat, JobName, @@ -11,7 +12,6 @@ import { SyncEntityType, SystemMetadataKey, TranscodeTarget, - UserAvatarColor, UserMetadataKey, VideoCodec, } from 'src/enum'; @@ -89,7 +89,7 @@ export interface VideoStreamInfo { export interface AudioStreamInfo { index: number; codecName?: string; - frameCount: number; + bitrate: number; } export interface VideoFormat { @@ -178,9 +178,10 @@ export interface IDelayedJob extends IBaseJob { delay?: number; } +export type JobSource = 'upload' | 'sidecar-write' | 'copy'; export interface IEntityJob extends IBaseJob { id: string; - source?: 'upload' | 'sidecar-write' | 'copy'; + source?: JobSource; notify?: boolean; } @@ -252,7 +253,7 @@ export interface INotifyAlbumInviteJob extends IEntityJob { } export interface INotifyAlbumUpdateJob extends IEntityJob, IDelayedJob { - recipientIds: string[]; + recipientId: string; } export interface JobCounts { @@ -298,6 +299,10 @@ export type JobItem = // Metadata Extraction | { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob } | { name: JobName.METADATA_EXTRACTION; data: IEntityJob } + + // Notifications + | { name: JobName.NOTIFICATIONS_CLEANUP; data?: IBaseJob } + // Sidecar Scanning | { name: JobName.QUEUE_SIDECAR; data: IBaseJob } | { name: JobName.SIDECAR_DISCOVERY; data: IEntityJob } @@ -356,13 +361,9 @@ export type JobItem = | { name: JobName.NOTIFY_SIGNUP; data: INotifySignupJob } // Version check - | { name: JobName.VERSION_CHECK; data: IBaseJob } + | { name: JobName.VERSION_CHECK; data: IBaseJob }; - // Memories - | { name: JobName.MEMORIES_CLEANUP; data?: IBaseJob } - | { name: JobName.MEMORIES_CREATE; data?: IBaseJob }; - -export type VectorExtension = DatabaseExtension.VECTOR | DatabaseExtension.VECTORS; +export type VectorExtension = (typeof VECTOR_EXTENSIONS)[number]; export type DatabaseConnectionURL = { connectionType: 'url'; @@ -376,11 +377,13 @@ export type DatabaseConnectionParts = { username: string; password: string; database: string; + ssl?: DatabaseSslMode; }; export type DatabaseConnectionParams = DatabaseConnectionURL | DatabaseConnectionParts; export interface ExtensionVersion { + name: VectorExtension; availableVersion: string | null; installedVersion: string | null; } @@ -486,9 +489,6 @@ export interface UserPreferences { enabled: boolean; sidebarWeb: boolean; }; - avatar: { - color: UserAvatarColor; - }; emailNotifications: { enabled: boolean; albumInvite: boolean; diff --git a/server/src/utils/access.ts b/server/src/utils/access.ts index 4e21a9226e..38697a654b 100644 --- a/server/src/utils/access.ts +++ b/server/src/utils/access.ts @@ -81,7 +81,7 @@ const checkSharedLinkAccess = async ( case Permission.ASSET_SHARE: { // TODO: fix this to not use sharedLink.userId for access control - return await access.asset.checkOwnerAccess(sharedLink.userId, ids); + return await access.asset.checkOwnerAccess(sharedLink.userId, ids, false); } case Permission.ALBUM_READ: { @@ -119,38 +119,38 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe } case Permission.ASSET_READ: { - const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids); + const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, auth.session?.hasElevatedPermission); const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner)); const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum)); return setUnion(isOwner, isAlbum, isPartner); } case Permission.ASSET_SHARE: { - const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids); + const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, false); const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner)); return setUnion(isOwner, isPartner); } case Permission.ASSET_VIEW: { - const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids); + const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, auth.session?.hasElevatedPermission); const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner)); const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum)); return setUnion(isOwner, isAlbum, isPartner); } case Permission.ASSET_DOWNLOAD: { - const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids); + const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids, auth.session?.hasElevatedPermission); const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner)); const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum)); return setUnion(isOwner, isAlbum, isPartner); } case Permission.ASSET_UPDATE: { - return await access.asset.checkOwnerAccess(auth.user.id, ids); + return await access.asset.checkOwnerAccess(auth.user.id, ids, auth.session?.hasElevatedPermission); } case Permission.ASSET_DELETE: { - return await access.asset.checkOwnerAccess(auth.user.id, ids); + return await access.asset.checkOwnerAccess(auth.user.id, ids, auth.session?.hasElevatedPermission); } case Permission.ALBUM_READ: { @@ -221,6 +221,12 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return access.person.checkFaceOwnerAccess(auth.user.id, ids); } + case Permission.NOTIFICATION_READ: + case Permission.NOTIFICATION_UPDATE: + case Permission.NOTIFICATION_DELETE: { + return access.notification.checkOwnerAccess(auth.user.id, ids); + } + case Permission.TAG_ASSET: case Permission.TAG_READ: case Permission.TAG_UPDATE: @@ -274,6 +280,13 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return await access.partner.checkUpdateAccess(auth.user.id, ids); } + case Permission.SESSION_READ: + case Permission.SESSION_UPDATE: + case Permission.SESSION_DELETE: + case Permission.SESSION_LOCK: { + return access.session.checkOwnerAccess(auth.user.id, ids); + } + case Permission.STACK_READ: { return access.stack.checkOwnerAccess(auth.user.id, ids); } diff --git a/server/src/utils/asset.util.ts b/server/src/utils/asset.util.ts index b44989eceb..2835269ff7 100644 --- a/server/src/utils/asset.util.ts +++ b/server/src/utils/asset.util.ts @@ -4,7 +4,7 @@ import { AssetFile } from 'src/database'; import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; import { UploadFieldName } from 'src/dtos/asset-media.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AssetFileType, AssetType, Permission } from 'src/enum'; +import { AssetFileType, AssetType, AssetVisibility, Permission } from 'src/enum'; import { AuthRequest } from 'src/middleware/auth.guard'; import { AccessRepository } from 'src/repositories/access.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -13,11 +13,8 @@ import { PartnerRepository } from 'src/repositories/partner.repository'; import { IBulkAsset, ImmichFile, UploadFile } from 'src/types'; import { checkAccess } from 'src/utils/access'; -export const getAssetFile = ( - files: T[], - type: AssetFileType | GeneratedImageType, -) => { - return (files || []).find((file) => file.type === type); +export const getAssetFile = (files: AssetFile[], type: AssetFileType | GeneratedImageType) => { + return files.find((file) => file.type === type); }; export const getAssetFiles = (files: AssetFile[]) => ({ @@ -153,8 +150,8 @@ export const onBeforeLink = async ( throw new BadRequestException('Live photo video does not belong to the user'); } - if (motionAsset?.isVisible) { - await assetRepository.update({ id: livePhotoVideoId, isVisible: false }); + if (motionAsset && motionAsset.visibility === AssetVisibility.TIMELINE) { + await assetRepository.update({ id: livePhotoVideoId, visibility: AssetVisibility.HIDDEN }); await eventRepository.emit('asset.hide', { assetId: motionAsset.id, userId }); } }; @@ -177,9 +174,9 @@ export const onBeforeUnlink = async ( export const onAfterUnlink = async ( { asset: assetRepository, event: eventRepository }: AssetHookRepositories, - { userId, livePhotoVideoId }: { userId: string; livePhotoVideoId: string }, + { userId, livePhotoVideoId, visibility }: { userId: string; livePhotoVideoId: string; visibility: AssetVisibility }, ) => { - await assetRepository.update({ id: livePhotoVideoId, isVisible: true }); + await assetRepository.update({ id: livePhotoVideoId, visibility }); await eventRepository.emit('asset.show', { assetId: livePhotoVideoId, userId }); }; diff --git a/server/src/utils/bytes.ts b/server/src/utils/bytes.ts index e837c81b9e..5e476f4dea 100644 --- a/server/src/utils/bytes.ts +++ b/server/src/utils/bytes.ts @@ -22,3 +22,12 @@ export function asHumanReadable(bytes: number, precision = 1): string { return `${remainder.toFixed(magnitude == 0 ? 0 : precision)} ${units[magnitude]}`; } + +// if an asset is jsonified in the DB before being returned, its buffer fields will be hex-encoded strings +export const hexOrBufferToBase64 = (encoded: string | Buffer) => { + if (typeof encoded === 'string') { + return Buffer.from(encoded.slice(2), 'hex').toString('base64'); + } + + return encoded.toString('base64'); +}; diff --git a/server/src/utils/database.spec.ts b/server/src/utils/database.spec.ts new file mode 100644 index 0000000000..4c6a82ad8f --- /dev/null +++ b/server/src/utils/database.spec.ts @@ -0,0 +1,83 @@ +import { asPostgresConnectionConfig } from 'src/utils/database'; + +describe('database utils', () => { + describe('asPostgresConnectionConfig', () => { + it('should handle sslmode=require', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=require', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=prefer', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=prefer', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=verify-ca', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-ca', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=verify-full', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=verify-full', + }), + ).toMatchObject({ ssl: {} }); + }); + + it('should handle sslmode=no-verify', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?sslmode=no-verify', + }), + ).toMatchObject({ ssl: { rejectUnauthorized: false } }); + }); + + it('should handle ssl=true', () => { + expect( + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?ssl=true', + }), + ).toMatchObject({ ssl: true }); + }); + + it('should reject invalid ssl', () => { + expect(() => + asPostgresConnectionConfig({ + connectionType: 'url', + url: 'postgres://postgres1:postgres2@database1:54320/immich?ssl=invalid', + }), + ).toThrowError('Invalid ssl option'); + }); + + it('should handle socket: URLs', () => { + expect( + asPostgresConnectionConfig({ connectionType: 'url', url: 'socket:/run/postgresql?db=database1' }), + ).toMatchObject({ host: '/run/postgresql', database: 'database1' }); + }); + + it('should handle sockets in postgres: URLs', () => { + expect( + asPostgresConnectionConfig({ connectionType: 'url', url: 'postgres:///database2?host=/path/to/socket' }), + ).toMatchObject({ + host: '/path/to/socket', + database: 'database2', + }); + }); + }); +}); diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index a9c7b09c5a..40bf7503db 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -1,36 +1,69 @@ import { + DeduplicateJoinsPlugin, Expression, ExpressionBuilder, ExpressionWrapper, + Kysely, KyselyConfig, Nullable, Selectable, + SelectQueryBuilder, Simplify, sql, } from 'kysely'; import { PostgresJSDialect } from 'kysely-postgres-js'; +import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; +import { parse } from 'pg-connection-string'; import postgres, { Notice } from 'postgres'; +import { columns, Exif, Person } from 'src/database'; +import { DB } from 'src/db'; +import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum'; +import { TimeBucketSize } from 'src/repositories/asset.repository'; +import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; +import { DatabaseConnectionParams, VectorExtension } from 'src/types'; type Ssl = 'require' | 'allow' | 'prefer' | 'verify-full' | boolean | object; -export type PostgresConnectionConfig = { - host?: string; - password?: string; - user?: string; - port?: number; - database?: string; - max?: number; - client_encoding?: string; - ssl?: Ssl; - application_name?: string; - fallback_application_name?: string; - options?: string; -}; - -export const isValidSsl = (ssl?: string | boolean | object): ssl is Ssl => +const isValidSsl = (ssl?: string | boolean | object): ssl is Ssl => typeof ssl !== 'string' || ssl === 'require' || ssl === 'allow' || ssl === 'prefer' || ssl === 'verify-full'; -export const getKyselyConfig = (options: PostgresConnectionConfig): KyselyConfig => { +export const asPostgresConnectionConfig = (params: DatabaseConnectionParams) => { + if (params.connectionType === 'parts') { + return { + host: params.host, + port: params.port, + username: params.username, + password: params.password, + database: params.database, + ssl: params.ssl === DatabaseSslMode.Disable ? false : params.ssl, + }; + } + + const { host, port, user, password, database, ...rest } = parse(params.url); + let ssl: Ssl | undefined; + if (rest.ssl) { + if (!isValidSsl(rest.ssl)) { + throw new Error(`Invalid ssl option: ${rest.ssl}`); + } + ssl = rest.ssl; + } + + return { + host: host ?? undefined, + port: port ? Number(port) : undefined, + username: user, + password, + database: database ?? undefined, + ssl, + }; +}; + +export const getKyselyConfig = ( + params: DatabaseConnectionParams, + options: Partial>> = {}, +): KyselyConfig => { + const config = asPostgresConnectionConfig(params); + return { dialect: new PostgresJSDialect({ postgres: postgres({ @@ -57,6 +90,12 @@ export const getKyselyConfig = (options: PostgresConnectionConfig): KyselyConfig connection: { TimeZone: 'UTC', }, + host: config.host, + port: config.port, + username: config.username, + password: config.password, + database: config.database, + ssl: config.ssl, ...options, }), }), @@ -112,3 +151,274 @@ export function toJson >; } + +export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; +// TODO come up with a better query that only selects the fields we need + +export function withDefaultVisibility(qb: SelectQueryBuilder) { + return qb.where((qb) => + qb.or([ + qb('assets.visibility', '=', AssetVisibility.TIMELINE), + qb('assets.visibility', '=', AssetVisibility.ARCHIVE), + ]), + ); +} + +export function withExif(qb: SelectQueryBuilder) { + return qb + .leftJoin('exif', 'assets.id', 'exif.assetId') + .select((eb) => eb.fn.toJson(eb.table('exif')).$castTo().as('exifInfo')); +} + +export function withExifInner(qb: SelectQueryBuilder) { + return qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .select((eb) => eb.fn.toJson(eb.table('exif')).$castTo().as('exifInfo')); +} + +export function withSmartSearch(qb: SelectQueryBuilder) { + return qb + .leftJoin('smart_search', 'assets.id', 'smart_search.assetId') + .select((eb) => toJson(eb, 'smart_search').as('smartSearch')); +} + +export function withFaces(eb: ExpressionBuilder, withDeletedFace?: boolean) { + return jsonArrayFrom( + eb + .selectFrom('asset_faces') + .selectAll('asset_faces') + .whereRef('asset_faces.assetId', '=', 'assets.id') + .$if(!withDeletedFace, (qb) => qb.where('asset_faces.deletedAt', 'is', null)), + ).as('faces'); +} + +export function withFiles(eb: ExpressionBuilder, type?: AssetFileType) { + return jsonArrayFrom( + eb + .selectFrom('asset_files') + .select(columns.assetFiles) + .whereRef('asset_files.assetId', '=', 'assets.id') + .$if(!!type, (qb) => qb.where('asset_files.type', '=', type!)), + ).as('files'); +} + +export function withFacesAndPeople(eb: ExpressionBuilder, withDeletedFace?: boolean) { + return jsonArrayFrom( + eb + .selectFrom('asset_faces') + .leftJoinLateral( + (eb) => + eb.selectFrom('person').selectAll('person').whereRef('asset_faces.personId', '=', 'person.id').as('person'), + (join) => join.onTrue(), + ) + .selectAll('asset_faces') + .select((eb) => eb.table('person').$castTo().as('person')) + .whereRef('asset_faces.assetId', '=', 'assets.id') + .$if(!withDeletedFace, (qb) => qb.where('asset_faces.deletedAt', 'is', null)), + ).as('faces'); +} + +export function hasPeople(qb: SelectQueryBuilder, personIds: string[]) { + return qb.innerJoin( + (eb) => + eb + .selectFrom('asset_faces') + .select('assetId') + .where('personId', '=', anyUuid(personIds!)) + .where('deletedAt', 'is', null) + .groupBy('assetId') + .having((eb) => eb.fn.count('personId').distinct(), '=', personIds.length) + .as('has_people'), + (join) => join.onRef('has_people.assetId', '=', 'assets.id'), + ); +} + +export function hasTags(qb: SelectQueryBuilder, tagIds: string[]) { + return qb.innerJoin( + (eb) => + eb + .selectFrom('tag_asset') + .select('assetsId') + .innerJoin('tags_closure', 'tag_asset.tagsId', 'tags_closure.id_descendant') + .where('tags_closure.id_ancestor', '=', anyUuid(tagIds)) + .groupBy('assetsId') + .having((eb) => eb.fn.count('tags_closure.id_ancestor').distinct(), '>=', tagIds.length) + .as('has_tags'), + (join) => join.onRef('has_tags.assetsId', '=', 'assets.id'), + ); +} + +export function withOwner(eb: ExpressionBuilder) { + return jsonObjectFrom(eb.selectFrom('users').select(columns.user).whereRef('users.id', '=', 'assets.ownerId')).as( + 'owner', + ); +} + +export function withLibrary(eb: ExpressionBuilder) { + return jsonObjectFrom( + eb.selectFrom('libraries').selectAll('libraries').whereRef('libraries.id', '=', 'assets.libraryId'), + ).as('library'); +} + +export function withTags(eb: ExpressionBuilder) { + return jsonArrayFrom( + eb + .selectFrom('tags') + .select(columns.tag) + .innerJoin('tag_asset', 'tags.id', 'tag_asset.tagsId') + .whereRef('assets.id', '=', 'tag_asset.assetsId'), + ).as('tags'); +} + +export function truncatedDate(size: TimeBucketSize) { + return sql`date_trunc(${sql.lit(size)}, "localDateTime" at time zone 'UTC') at time zone 'UTC'`; +} + +export function withTagId(qb: SelectQueryBuilder, tagId: string) { + return qb.where((eb) => + eb.exists( + eb + .selectFrom('tags_closure') + .innerJoin('tag_asset', 'tag_asset.tagsId', 'tags_closure.id_descendant') + .whereRef('tag_asset.assetsId', '=', 'assets.id') + .where('tags_closure.id_ancestor', '=', tagId), + ), + ); +} + +const joinDeduplicationPlugin = new DeduplicateJoinsPlugin(); +/** TODO: This should only be used for search-related queries, not as a general purpose query builder */ + +export function searchAssetBuilder(kysely: Kysely, options: AssetSearchBuilderOptions) { + options.withDeleted ||= !!(options.trashedAfter || options.trashedBefore || options.isOffline); + const visibility = options.visibility == null ? AssetVisibility.TIMELINE : options.visibility; + + return kysely + .withPlugin(joinDeduplicationPlugin) + .selectFrom('assets') + .selectAll('assets') + .where('assets.visibility', '=', visibility) + .$if(!!options.tagIds && options.tagIds.length > 0, (qb) => hasTags(qb, options.tagIds!)) + .$if(!!options.personIds && options.personIds.length > 0, (qb) => hasPeople(qb, options.personIds!)) + .$if(!!options.createdBefore, (qb) => qb.where('assets.createdAt', '<=', options.createdBefore!)) + .$if(!!options.createdAfter, (qb) => qb.where('assets.createdAt', '>=', options.createdAfter!)) + .$if(!!options.updatedBefore, (qb) => qb.where('assets.updatedAt', '<=', options.updatedBefore!)) + .$if(!!options.updatedAfter, (qb) => qb.where('assets.updatedAt', '>=', options.updatedAfter!)) + .$if(!!options.trashedBefore, (qb) => qb.where('assets.deletedAt', '<=', options.trashedBefore!)) + .$if(!!options.trashedAfter, (qb) => qb.where('assets.deletedAt', '>=', options.trashedAfter!)) + .$if(!!options.takenBefore, (qb) => qb.where('assets.fileCreatedAt', '<=', options.takenBefore!)) + .$if(!!options.takenAfter, (qb) => qb.where('assets.fileCreatedAt', '>=', options.takenAfter!)) + .$if(options.city !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.city', options.city === null ? 'is' : '=', options.city!), + ) + .$if(options.state !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.state', options.state === null ? 'is' : '=', options.state!), + ) + .$if(options.country !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.country', options.country === null ? 'is' : '=', options.country!), + ) + .$if(options.make !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.make', options.make === null ? 'is' : '=', options.make!), + ) + .$if(options.model !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.model', options.model === null ? 'is' : '=', options.model!), + ) + .$if(options.lensModel !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.lensModel', options.lensModel === null ? 'is' : '=', options.lensModel!), + ) + .$if(options.rating !== undefined, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where('exif.rating', options.rating === null ? 'is' : '=', options.rating!), + ) + .$if(!!options.checksum, (qb) => qb.where('assets.checksum', '=', options.checksum!)) + .$if(!!options.deviceAssetId, (qb) => qb.where('assets.deviceAssetId', '=', options.deviceAssetId!)) + .$if(!!options.deviceId, (qb) => qb.where('assets.deviceId', '=', options.deviceId!)) + .$if(!!options.id, (qb) => qb.where('assets.id', '=', asUuid(options.id!))) + .$if(!!options.libraryId, (qb) => qb.where('assets.libraryId', '=', asUuid(options.libraryId!))) + .$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!))) + .$if(!!options.encodedVideoPath, (qb) => qb.where('assets.encodedVideoPath', '=', options.encodedVideoPath!)) + .$if(!!options.originalPath, (qb) => + qb.where(sql`f_unaccent(assets."originalPath")`, 'ilike', sql`'%' || f_unaccent(${options.originalPath}) || '%'`), + ) + .$if(!!options.originalFileName, (qb) => + qb.where( + sql`f_unaccent(assets."originalFileName")`, + 'ilike', + sql`'%' || f_unaccent(${options.originalFileName}) || '%'`, + ), + ) + .$if(!!options.description, (qb) => + qb + .innerJoin('exif', 'assets.id', 'exif.assetId') + .where(sql`f_unaccent(exif.description)`, 'ilike', sql`'%' || f_unaccent(${options.description}) || '%'`), + ) + .$if(!!options.type, (qb) => qb.where('assets.type', '=', options.type!)) + .$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!)) + .$if(options.isOffline !== undefined, (qb) => qb.where('assets.isOffline', '=', options.isOffline!)) + .$if(options.isEncoded !== undefined, (qb) => + qb.where('assets.encodedVideoPath', options.isEncoded ? 'is not' : 'is', null), + ) + .$if(options.isMotion !== undefined, (qb) => + qb.where('assets.livePhotoVideoId', options.isMotion ? 'is not' : 'is', null), + ) + .$if(!!options.isNotInAlbum, (qb) => + qb.where((eb) => + eb.not(eb.exists((eb) => eb.selectFrom('albums_assets_assets').whereRef('assetsId', '=', 'assets.id'))), + ), + ) + .$if(!!options.withExif, withExifInner) + .$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople)) + .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)); +} + +export type ReindexVectorIndexOptions = { indexName: string; lists?: number }; + +type VectorIndexQueryOptions = { table: string; vectorExtension: VectorExtension } & ReindexVectorIndexOptions; + +export function vectorIndexQuery({ vectorExtension, table, indexName, lists }: VectorIndexQueryOptions): string { + switch (vectorExtension) { + case DatabaseExtension.VECTORCHORD: { + return ` + CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} USING vchordrq (embedding vector_cosine_ops) WITH (options = $$ + residual_quantization = false + [build.internal] + lists = [${lists ?? 1}] + spherical_centroids = true + build_threads = 4 + sampling_factor = 1024 + $$)`; + } + case DatabaseExtension.VECTORS: { + return ` + CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} + USING vectors (embedding vector_cos_ops) WITH (options = $$ + optimizing.optimizing_threads = 4 + [indexing.hnsw] + m = 16 + ef_construction = 300 + $$)`; + } + case DatabaseExtension.VECTOR: { + return ` + CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} + USING hnsw (embedding vector_cosine_ops) + WITH (ef_construction = 300, m = 16)`; + } + default: { + throw new Error(`Unsupported vector extension: '${vectorExtension}'`); + } + } +} diff --git a/server/src/utils/mime-types.ts b/server/src/utils/mime-types.ts index b1a9c77588..6aad418d9f 100644 --- a/server/src/utils/mime-types.ts +++ b/server/src/utils/mime-types.ts @@ -34,45 +34,40 @@ const raw: Record = { '.x3f': ['image/x3f', 'image/x-sigma-x3f'], }; +/** + * list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg + * @TODO share with the client + * @see {@link web/src/lib/utils/asset-utils.ts#L329} + **/ +const webSupportedImage = { + '.avif': ['image/avif'], + '.gif': ['image/gif'], + '.jpeg': ['image/jpeg'], + '.jpg': ['image/jpeg'], + '.png': ['image/png', 'image/apng'], + '.webp': ['image/webp'], +}; + const image: Record = { ...raw, - '.avif': ['image/avif'], + ...webSupportedImage, '.bmp': ['image/bmp'], - '.gif': ['image/gif'], '.heic': ['image/heic'], '.heif': ['image/heif'], '.hif': ['image/hif'], '.insp': ['image/jpeg'], '.jp2': ['image/jp2'], '.jpe': ['image/jpeg'], - '.jpeg': ['image/jpeg'], - '.jpg': ['image/jpeg'], '.jxl': ['image/jxl'], - '.png': ['image/png'], '.svg': ['image/svg'], '.tif': ['image/tiff'], '.tiff': ['image/tiff'], - '.webp': ['image/webp'], }; const extensionOverrides: Record = { 'image/jpeg': '.jpg', }; -/** - * list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg - * @TODO share with the client - * @see {@link web/src/lib/utils/asset-utils.ts#L329} - **/ -const webSupportedImageMimeTypes = new Set([ - 'image/apng', - 'image/avif', - 'image/gif', - 'image/jpeg', - 'image/png', - 'image/webp', -]); - const profileExtensions = new Set(['.avif', '.dng', '.heic', '.heif', '.jpeg', '.jpg', '.png', '.webp', '.svg']); const profile: Record = Object.fromEntries( Object.entries(image).filter(([key]) => profileExtensions.has(key)), @@ -123,7 +118,7 @@ export const mimeTypes = { isAsset: (filename: string) => isType(filename, image) || isType(filename, video), isImage: (filename: string) => isType(filename, image), - isWebSupportedImage: (filename: string) => webSupportedImageMimeTypes.has(lookup(filename)), + isWebSupportedImage: (filename: string) => isType(filename, webSupportedImage), isProfile: (filename: string) => isType(filename, profile), isSidecar: (filename: string) => isType(filename, sidecar), isVideo: (filename: string) => isType(filename, video), diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index ff1656da74..05811350e4 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -301,3 +301,7 @@ export const globToSqlPattern = (glob: string) => { const tokens = picomatch.parse(glob).tokens; return tokens.map((token) => convertTokenToSqlPattern(token)).join(''); }; + +export function clamp(value: number, min: number, max: number) { + return Math.max(min, Math.min(max, value)); +} diff --git a/server/src/utils/pagination.ts b/server/src/utils/pagination.ts index eb4106c86a..e440638a72 100644 --- a/server/src/utils/pagination.ts +++ b/server/src/utils/pagination.ts @@ -8,22 +8,6 @@ export interface PaginationResult { hasNextPage: boolean; } -export type Paginated = Promise>; - -/** @deprecated use `this.db. ... .stream()` instead */ -export async function* usePagination( - pageSize: number, - getNextPage: (pagination: PaginationOptions) => PaginationResult | Paginated, -) { - let hasNextPage = true; - - for (let skip = 0; hasNextPage; skip += pageSize) { - const result = await getNextPage({ take: pageSize, skip }); - hasNextPage = result.hasNextPage; - yield result.items; - } -} - export function paginationHelper(items: Entity[], take: number): PaginationResult { const hasNextPage = items.length > take; items.splice(take); diff --git a/server/src/utils/preferences.ts b/server/src/utils/preferences.ts index 584c5300cd..a013c0b74e 100644 --- a/server/src/utils/preferences.ts +++ b/server/src/utils/preferences.ts @@ -1,16 +1,11 @@ import _ from 'lodash'; import { UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; -import { UserAvatarColor, UserMetadataKey } from 'src/enum'; +import { UserMetadataKey } from 'src/enum'; import { DeepPartial, UserMetadataItem, UserPreferences } from 'src/types'; import { HumanReadableSize } from 'src/utils/bytes'; import { getKeysDeep } from 'src/utils/misc'; -const getDefaultPreferences = (user: { email: string }): UserPreferences => { - const values = Object.values(UserAvatarColor); - const randomIndex = Math.floor( - [...user.email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, - ); - +const getDefaultPreferences = (): UserPreferences => { return { folders: { enabled: false, @@ -34,9 +29,6 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => { enabled: false, sidebarWeb: false, }, - avatar: { - color: values[randomIndex], - }, emailNotifications: { enabled: true, albumInvite: true, @@ -53,8 +45,8 @@ const getDefaultPreferences = (user: { email: string }): UserPreferences => { }; }; -export const getPreferences = (email: string, metadata: UserMetadataItem[]): UserPreferences => { - const preferences = getDefaultPreferences({ email }); +export const getPreferences = (metadata: UserMetadataItem[]): UserPreferences => { + const preferences = getDefaultPreferences(); const item = metadata.find(({ key }) => key === UserMetadataKey.PREFERENCES); const partial = item?.value || {}; for (const property of getKeysDeep(partial)) { @@ -64,8 +56,8 @@ export const getPreferences = (email: string, metadata: UserMetadataItem[]): Use return preferences; }; -export const getPreferencesPartial = (user: { email: string }, newPreferences: UserPreferences) => { - const defaultPreferences = getDefaultPreferences(user); +export const getPreferencesPartial = (newPreferences: UserPreferences) => { + const defaultPreferences = getDefaultPreferences(); const partial: DeepPartial = {}; for (const property of getKeysDeep(defaultPreferences)) { const newValue = _.get(newPreferences, property); diff --git a/server/src/utils/response.ts b/server/src/utils/response.ts index 679d947afb..a50e86a4ff 100644 --- a/server/src/utils/response.ts +++ b/server/src/utils/response.ts @@ -15,6 +15,8 @@ export const respondWithCookie = (res: Response, body: T, { isSecure, values const cookieOptions: Record = { [ImmichCookie.AUTH_TYPE]: defaults, [ImmichCookie.ACCESS_TOKEN]: defaults, + [ImmichCookie.OAUTH_STATE]: defaults, + [ImmichCookie.OAUTH_CODE_VERIFIER]: defaults, // no httpOnly so that the client can know the auth state [ImmichCookie.IS_AUTHENTICATED]: { ...defaults, httpOnly: false }, [ImmichCookie.SHARED_LINK_TOKEN]: { ...defaults, maxAge: Duration.fromObject({ days: 1 }).toMillis() }, diff --git a/server/src/validation.ts b/server/src/validation.ts index 29e402826d..2d160f43ce 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -12,11 +12,13 @@ import { IsArray, IsBoolean, IsDate, + IsEnum, IsHexColor, IsNotEmpty, IsOptional, IsString, IsUUID, + Matches, Validate, ValidateBy, ValidateIf, @@ -29,6 +31,7 @@ import { import { CronJob } from 'cron'; import { DateTime } from 'luxon'; import sanitize from 'sanitize-filename'; +import { AssetVisibility } from 'src/enum'; import { isIP, isIPRange } from 'validator'; @Injectable() @@ -68,6 +71,22 @@ export class UUIDParamDto { id!: string; } +type PinCodeOptions = { optional?: boolean } & OptionalOptions; +export const PinCode = ({ optional, ...options }: PinCodeOptions = {}) => { + const decorators = [ + IsString(), + IsNotEmpty(), + Matches(/^\d{6}$/, { message: ({ property }) => `${property} must be a 6-digit numeric string` }), + ApiProperty({ example: '123456' }), + ]; + + if (optional) { + decorators.push(Optional(options)); + } + + return applyDecorators(...decorators); +}; + export interface OptionalOptions extends ValidationOptions { nullable?: boolean; /** convert empty strings to null */ @@ -146,6 +165,17 @@ export const ValidateDate = (options?: DateOptions) => { return applyDecorators(...decorators); }; +type AssetVisibilityOptions = { optional?: boolean }; +export const ValidateAssetVisibility = (options?: AssetVisibilityOptions) => { + const { optional } = { optional: false, ...options }; + const decorators = [IsEnum(AssetVisibility), ApiProperty({ enumName: 'AssetVisibility', enum: AssetVisibility })]; + + if (optional) { + decorators.push(Optional()); + } + return applyDecorators(...decorators); +}; + type BooleanOptions = { optional?: boolean }; export const ValidateBoolean = (options?: BooleanOptions) => { const { optional } = { optional: false, ...options }; diff --git a/server/src/workers/api.ts b/server/src/workers/api.ts index ddf6e50aa2..ce1520c475 100644 --- a/server/src/workers/api.ts +++ b/server/src/workers/api.ts @@ -1,6 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { json } from 'body-parser'; +import compression from 'compression'; import cookieParser from 'cookie-parser'; import { existsSync } from 'node:fs'; import sirv from 'sirv'; @@ -13,7 +14,6 @@ import { LoggingRepository } from 'src/repositories/logging.repository'; import { bootstrapTelemetry } from 'src/repositories/telemetry.repository'; import { ApiService } from 'src/services/api.service'; import { isStartUpError, useSwagger } from 'src/utils/misc'; - async function bootstrap() { process.title = 'immich-api'; @@ -60,6 +60,7 @@ async function bootstrap() { ); } app.use(app.get(ApiService).ssr(excludePaths)); + app.use(compression()); const server = await (host ? app.listen(port, host) : app.listen(port)); server.requestTimeout = 24 * 60 * 60 * 1000; diff --git a/server/test/fixtures/album.stub.ts b/server/test/fixtures/album.stub.ts index 5a1c141512..fd6a8678a0 100644 --- a/server/test/fixtures/album.stub.ts +++ b/server/test/fixtures/album.stub.ts @@ -1,11 +1,10 @@ -import { AlbumEntity } from 'src/entities/album.entity'; import { AlbumUserRole, AssetOrder } from 'src/enum'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { userStub } from 'test/fixtures/user.stub'; export const albumStub = { - empty: Object.freeze({ + empty: Object.freeze({ id: 'album-1', albumName: 'Empty album', description: '', @@ -21,8 +20,9 @@ export const albumStub = { albumUsers: [], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - sharedWithUser: Object.freeze({ + sharedWithUser: Object.freeze({ id: 'album-2', albumName: 'Empty album shared with user', description: '', @@ -43,8 +43,9 @@ export const albumStub = { ], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - sharedWithMultiple: Object.freeze({ + sharedWithMultiple: Object.freeze({ id: 'album-3', albumName: 'Empty album shared with users', description: '', @@ -69,8 +70,9 @@ export const albumStub = { ], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - sharedWithAdmin: Object.freeze({ + sharedWithAdmin: Object.freeze({ id: 'album-3', albumName: 'Empty album shared with admin', description: '', @@ -91,8 +93,9 @@ export const albumStub = { ], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - oneAsset: Object.freeze({ + oneAsset: Object.freeze({ id: 'album-4', albumName: 'Album with one asset', description: '', @@ -108,8 +111,9 @@ export const albumStub = { albumUsers: [], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - twoAssets: Object.freeze({ + twoAssets: Object.freeze({ id: 'album-4a', albumName: 'Album with two assets', description: '', @@ -125,8 +129,9 @@ export const albumStub = { albumUsers: [], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), - emptyWithValidThumbnail: Object.freeze({ + emptyWithValidThumbnail: Object.freeze({ id: 'album-5', albumName: 'Empty album with valid thumbnail', description: '', @@ -142,5 +147,6 @@ export const albumStub = { albumUsers: [], isActivityEnabled: true, order: AssetOrder.DESC, + updateId: '42', }), }; diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 16e4f20bb3..454be00844 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -1,6 +1,6 @@ -import { AssetFile, Exif } from 'src/database'; -import { AssetEntity } from 'src/entities/asset.entity'; -import { AssetFileType, AssetStatus, AssetType } from 'src/enum'; +import { AssetFace, AssetFile, Exif } from 'src/database'; +import { MapAsset } from 'src/dtos/asset-response.dto'; +import { AssetFileType, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; import { StorageAsset } from 'src/types'; import { authStub } from 'test/fixtures/auth.stub'; import { fileStub } from 'test/fixtures/file.stub'; @@ -26,13 +26,15 @@ const fullsizeFile: AssetFile = { const files: AssetFile[] = [fullsizeFile, previewFile, thumbnailFile]; -export const stackStub = (stackId: string, assets: AssetEntity[]) => { +export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif })[]) => { return { id: stackId, assets, ownerId: assets[0].ownerId, primaryAsset: assets[0], primaryAssetId: assets[0].id, + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), }; }; @@ -72,9 +74,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -85,9 +85,13 @@ export const assetStub = { isExternal: false, duplicateId: null, isOffline: false, + libraryId: null, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), - noWebpPath: Object.freeze({ + noWebpPath: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -106,9 +110,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -122,9 +124,13 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + libraryId: null, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), - noThumbhash: Object.freeze({ + noThumbhash: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -143,9 +149,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -156,6 +160,10 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + libraryId: null, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), primaryImage: Object.freeze({ @@ -177,9 +185,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -195,13 +201,15 @@ export const assetStub = { } as Exif, stackId: 'stack-1', stack: stackStub('stack-1', [ - { id: 'primary-asset-id' } as AssetEntity, - { id: 'stack-child-asset-1' } as AssetEntity, - { id: 'stack-child-asset-2' } as AssetEntity, + { id: 'primary-asset-id' } as MapAsset & { exifInfo: Exif }, + { id: 'stack-child-asset-1' } as MapAsset & { exifInfo: Exif }, + { id: 'stack-child-asset-2' } as MapAsset & { exifInfo: Exif }, ]), duplicateId: null, isOffline: false, + updateId: '42', libraryId: null, + visibility: AssetVisibility.TIMELINE, }), image: Object.freeze({ @@ -223,12 +231,13 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2025-01-01T01:02:03.456Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.jpg', faces: [], @@ -241,10 +250,15 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, - libraryId: null, + stack: null, + orientation: '', + projectionType: null, + height: 3840, + width: 2160, + visibility: AssetVisibility.TIMELINE, }), - trashed: Object.freeze({ + trashed: Object.freeze({ id: 'asset-id', deviceAssetId: 'device-asset-id', fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), @@ -263,9 +277,7 @@ export const assetStub = { deletedAt: new Date('2023-02-24T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: false, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -281,9 +293,13 @@ export const assetStub = { duplicateId: null, isOffline: false, status: AssetStatus.TRASHED, + libraryId: null, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), - trashedOffline: Object.freeze({ + trashedOffline: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -303,10 +319,8 @@ export const assetStub = { deletedAt: new Date('2023-02-24T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: false, - isArchived: false, duration: null, libraryId: 'library-id', - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -321,8 +335,11 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: true, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), - archived: Object.freeze({ + archived: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -341,9 +358,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: true, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -359,9 +374,13 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + libraryId: null, + stackId: null, + updateId: '42', + visibility: AssetVisibility.TIMELINE, }), - external: Object.freeze({ + external: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -380,10 +399,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: true, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, libraryId: 'library-id', @@ -397,9 +414,13 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + updateId: '42', + stackId: null, + stack: null, + visibility: AssetVisibility.TIMELINE, }), - image1: Object.freeze({ + image1: Object.freeze({ id: 'asset-id-1', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -419,9 +440,7 @@ export const assetStub = { deletedAt: null, localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, isExternal: false, @@ -434,9 +453,14 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + updateId: '42', + stackId: null, + libraryId: null, + stack: null, + visibility: AssetVisibility.TIMELINE, }), - imageFrom2015: Object.freeze({ + imageFrom2015: Object.freeze({ id: 'asset-id-1', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -455,10 +479,8 @@ export const assetStub = { updatedAt: new Date('2015-02-23T05:06:29.716Z'), localDateTime: new Date('2015-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -471,6 +493,7 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + visibility: AssetVisibility.TIMELINE, }), video: Object.freeze({ @@ -493,10 +516,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -510,7 +531,10 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + updateId: '42', libraryId: null, + stackId: null, + visibility: AssetVisibility.TIMELINE, }), livePhotoMotionAsset: Object.freeze({ @@ -519,7 +543,6 @@ export const assetStub = { originalPath: fileStub.livePhotoMotion.originalPath, ownerId: authStub.user1.user.id, type: AssetType.VIDEO, - isVisible: false, fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), exifInfo: { @@ -527,7 +550,8 @@ export const assetStub = { timeZone: `America/New_York`, }, libraryId: null, - } as AssetEntity & { libraryId: string | null; files: AssetFile[]; exifInfo: Exif }), + visibility: AssetVisibility.HIDDEN, + } as MapAsset & { faces: AssetFace[]; files: AssetFile[]; exifInfo: Exif }), livePhotoStillAsset: Object.freeze({ id: 'live-photo-still-asset', @@ -536,7 +560,6 @@ export const assetStub = { ownerId: authStub.user1.user.id, type: AssetType.IMAGE, livePhotoVideoId: 'live-photo-motion-asset', - isVisible: true, fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), exifInfo: { @@ -544,7 +567,9 @@ export const assetStub = { timeZone: `America/New_York`, }, files, - } as AssetEntity & { libraryId: string | null }), + faces: [] as AssetFace[], + visibility: AssetVisibility.TIMELINE, + } as MapAsset & { faces: AssetFace[] }), livePhotoWithOriginalFileName: Object.freeze({ id: 'live-photo-still-asset', @@ -554,7 +579,6 @@ export const assetStub = { ownerId: authStub.user1.user.id, type: AssetType.IMAGE, livePhotoVideoId: 'live-photo-motion-asset', - isVisible: true, fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), exifInfo: { @@ -562,7 +586,9 @@ export const assetStub = { timeZone: `America/New_York`, }, libraryId: null, - } as AssetEntity & { libraryId: string | null }), + faces: [] as AssetFace[], + visibility: AssetVisibility.TIMELINE, + } as MapAsset & { faces: AssetFace[] }), withLocation: Object.freeze({ id: 'asset-with-favorite-id', @@ -584,12 +610,13 @@ export const assetStub = { updatedAt: new Date('2023-02-22T05:06:29.716Z'), localDateTime: new Date('2020-12-31T23:59:00.000Z'), isFavorite: false, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.ext', faces: [], @@ -604,7 +631,8 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, - libraryId: null, + tags: [], + visibility: AssetVisibility.TIMELINE, }), sidecar: Object.freeze({ @@ -626,10 +654,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -639,10 +665,13 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + updateId: 'foo', libraryId: null, + stackId: null, + visibility: AssetVisibility.TIMELINE, }), - sidecarWithoutExt: Object.freeze({ + sidecarWithoutExt: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -661,10 +690,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -674,9 +701,10 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + visibility: AssetVisibility.TIMELINE, }), - hasEncodedVideo: Object.freeze({ + hasEncodedVideo: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, originalFileName: 'asset-id.ext', @@ -696,10 +724,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: false, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, sharedLinks: [], @@ -711,9 +737,14 @@ export const assetStub = { deletedAt: null, duplicateId: null, isOffline: false, + updateId: '42', + libraryId: null, + stackId: null, + stack: null, + visibility: AssetVisibility.TIMELINE, }), - hasFileExtension: Object.freeze({ + hasFileExtension: Object.freeze({ id: 'asset-id', status: AssetStatus.ACTIVE, deviceAssetId: 'device-asset-id', @@ -732,10 +763,8 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, isExternal: true, duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, libraryId: 'library-id', @@ -749,6 +778,7 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + visibility: AssetVisibility.TIMELINE, }), imageDng: Object.freeze({ @@ -770,9 +800,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -788,6 +816,10 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + updateId: '42', + libraryId: null, + stackId: null, + visibility: AssetVisibility.TIMELINE, }), imageHif: Object.freeze({ @@ -809,9 +841,7 @@ export const assetStub = { updatedAt: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'), isFavorite: true, - isArchived: false, duration: null, - isVisible: true, isExternal: false, livePhotoVideo: null, livePhotoVideoId: null, @@ -827,5 +857,9 @@ export const assetStub = { } as Exif, duplicateId: null, isOffline: false, + updateId: '42', + libraryId: null, + stackId: null, + visibility: AssetVisibility.TIMELINE, }), }; diff --git a/server/test/fixtures/auth.stub.ts b/server/test/fixtures/auth.stub.ts index dfa21fc707..3e5825c0cc 100644 --- a/server/test/fixtures/auth.stub.ts +++ b/server/test/fixtures/auth.stub.ts @@ -1,6 +1,5 @@ -import { Session } from 'src/database'; +import { AuthSession } from 'src/database'; import { AuthDto } from 'src/dtos/auth.dto'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; const authUser = { admin: { @@ -27,7 +26,7 @@ export const authStub = { user: authUser.user1, session: { id: 'token-id', - } as Session, + } as AuthSession, }), user2: Object.freeze({ user: { @@ -40,16 +39,18 @@ export const authStub = { }, session: { id: 'token-id', - } as Session, + } as AuthSession, }), - adminSharedLink: Object.freeze({ + adminSharedLink: Object.freeze({ user: authUser.admin, sharedLink: { id: '123', showExif: true, allowDownload: true, allowUpload: true, - key: Buffer.from('shared-link-key'), - } as SharedLinkEntity, + expiresAt: null, + password: null, + userId: '42', + }, }), }; diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index e1579435f5..efbf21d317 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -21,7 +21,7 @@ const probeStubDefaultVideoStream: VideoStreamInfo[] = [ }, ]; -const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 1, codecName: 'mp3', frameCount: 100 }]; +const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 3, codecName: 'mp3', bitrate: 100 }]; const probeStubDefault: VideoInfo = { format: probeStubDefaultFormat, @@ -40,23 +40,42 @@ export const probeStub = { height: 1080, width: 400, codecName: 'hevc', - frameCount: 100, + frameCount: 1, rotation: 0, isHDR: false, - bitrate: 0, + bitrate: 100, pixelFormat: 'yuv420p', }, { index: 1, height: 1080, width: 400, - codecName: 'h7000', - frameCount: 99, + codecName: 'hevc', + frameCount: 2, rotation: 0, isHDR: false, - bitrate: 0, + bitrate: 101, pixelFormat: 'yuv420p', }, + { + index: 2, + height: 1080, + width: 400, + codecName: 'h7000', + frameCount: 3, + rotation: 0, + isHDR: false, + bitrate: 99, + pixelFormat: 'yuv420p', + }, + ], + }), + multipleAudioStreams: Object.freeze({ + ...probeStubDefault, + audioStreams: [ + { index: 0, codecName: 'mp3', bitrate: 100 }, + { index: 1, codecName: 'mp3', bitrate: 101 }, + { index: 2, codecName: 'mp3', bitrate: 102 }, ], }), noHeight: Object.freeze({ @@ -200,13 +219,13 @@ export const probeStub = { }), audioStreamAac: Object.freeze({ ...probeStubDefault, - audioStreams: [{ index: 1, codecName: 'aac', frameCount: 100 }], + audioStreams: [{ index: 1, codecName: 'aac', bitrate: 100 }], }), audioStreamUnknown: Object.freeze({ ...probeStubDefault, audioStreams: [ - { index: 0, codecName: 'aac', frameCount: 100 }, - { index: 1, codecName: 'unknown', frameCount: 200 }, + { index: 0, codecName: 'aac', bitrate: 100 }, + { index: 1, codecName: 'unknown', bitrate: 200 }, ], }), matroskaContainer: Object.freeze({ diff --git a/server/test/fixtures/person.stub.ts b/server/test/fixtures/person.stub.ts index 8457f9ddcd..86f3bcde21 100644 --- a/server/test/fixtures/person.stub.ts +++ b/server/test/fixtures/person.stub.ts @@ -178,8 +178,7 @@ export const personThumbnailStub = { oldWidth: 2160, type: AssetType.IMAGE, originalPath: '/original/path.jpg', - exifImageHeight: 3840, - exifImageWidth: 2160, + exifOrientation: '1', previewPath: previewFile.path, }), newThumbnailMiddle: Object.freeze({ @@ -192,8 +191,7 @@ export const personThumbnailStub = { oldWidth: 400, type: AssetType.IMAGE, originalPath: '/original/path.jpg', - exifImageHeight: 1000, - exifImageWidth: 1000, + exifOrientation: '1', previewPath: previewFile.path, }), newThumbnailEnd: Object.freeze({ @@ -206,8 +204,59 @@ export const personThumbnailStub = { oldWidth: 500, type: AssetType.IMAGE, originalPath: '/original/path.jpg', - exifImageHeight: 1000, - exifImageWidth: 1000, + exifOrientation: '1', + previewPath: previewFile.path, + }), + rawEmbeddedThumbnail: Object.freeze({ + ownerId: userStub.admin.id, + x1: 100, + y1: 100, + x2: 200, + y2: 200, + oldHeight: 500, + oldWidth: 400, + type: AssetType.IMAGE, + originalPath: '/original/path.dng', + exifOrientation: '1', + previewPath: previewFile.path, + }), + negativeCoordinate: Object.freeze({ + ownerId: userStub.admin.id, + x1: -176, + y1: -230, + x2: 193, + y2: 251, + oldHeight: 1440, + oldWidth: 2162, + type: AssetType.IMAGE, + originalPath: '/original/path.jpg', + exifOrientation: '1', + previewPath: previewFile.path, + }), + overflowingCoordinate: Object.freeze({ + ownerId: userStub.admin.id, + x1: 2097, + y1: 0, + x2: 2171, + y2: 152, + oldHeight: 1440, + oldWidth: 2162, + type: AssetType.IMAGE, + originalPath: '/original/path.jpg', + exifOrientation: '1', + previewPath: previewFile.path, + }), + videoThumbnail: Object.freeze({ + ownerId: userStub.admin.id, + x1: 100, + y1: 100, + x2: 200, + y2: 200, + oldHeight: 500, + oldWidth: 400, + type: AssetType.VIDEO, + originalPath: '/original/path.mp4', + exifOrientation: '1', previewPath: previewFile.path, }), }; diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 4eba0de845..f3096280d9 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -1,11 +1,10 @@ import { UserAdmin } from 'src/database'; import { AlbumResponseDto } from 'src/dtos/album.dto'; -import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AssetResponseDto, MapAsset } from 'src/dtos/asset-response.dto'; import { ExifResponseDto } from 'src/dtos/exif.dto'; import { SharedLinkResponseDto } from 'src/dtos/shared-link.dto'; import { mapUser } from 'src/dtos/user.dto'; -import { SharedLinkEntity } from 'src/entities/shared-link.entity'; -import { AssetOrder, AssetStatus, AssetType, SharedLinkType } from 'src/enum'; +import { AssetOrder, AssetStatus, AssetType, AssetVisibility, SharedLinkType } from 'src/enum'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { userStub } from 'test/fixtures/user.stub'; @@ -71,6 +70,7 @@ const assetResponse: AssetResponseDto = { isTrashed: false, libraryId: 'library-id', hasMetadata: true, + visibility: AssetVisibility.TIMELINE, }; const assetResponseWithoutMetadata = { @@ -113,12 +113,12 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, - album: undefined, + albumId: null, + album: null, description: null, assets: [assetStub.image], password: 'password', - albumId: null, - } as SharedLinkEntity), + }), valid: Object.freeze({ id: '123', userId: authStub.admin.user.id, @@ -130,12 +130,12 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, - album: undefined, albumId: null, description: null, password: null, - assets: [], - } as SharedLinkEntity), + assets: [] as MapAsset[], + album: null, + }), expired: Object.freeze({ id: '123', userId: authStub.admin.user.id, @@ -150,9 +150,10 @@ export const sharedLinkStub = { description: null, password: null, albumId: null, - assets: [], - } as SharedLinkEntity), - readonlyNoExif: Object.freeze({ + assets: [] as MapAsset[], + album: null, + }), + readonlyNoExif: Object.freeze({ id: '123', userId: authStub.admin.user.id, key: sharedLinkBytes, @@ -168,6 +169,7 @@ export const sharedLinkStub = { albumId: 'album-123', album: { id: 'album-123', + updateId: '42', ownerId: authStub.admin.user.id, owner: userStub.admin, albumName: 'Test Album', @@ -205,7 +207,6 @@ export const sharedLinkStub = { thumbhash: null, encodedVideoPath: '', duration: null, - isVisible: true, livePhotoVideo: null, livePhotoVideoId: null, originalFileName: 'asset_1.jpeg', @@ -239,17 +240,23 @@ export const sharedLinkStub = { colorspace: 'sRGB', autoStackId: null, rating: 3, + updatedAt: today, + updateId: '42', }, sharedLinks: [], faces: [], sidecarPath: null, deletedAt: null, duplicateId: null, + updateId: '42', + libraryId: null, + stackId: null, + visibility: AssetVisibility.TIMELINE, }, ], }, }), - passwordRequired: Object.freeze({ + passwordRequired: Object.freeze({ id: '123', userId: authStub.admin.user.id, key: sharedLinkBytes, @@ -263,6 +270,7 @@ export const sharedLinkStub = { password: 'password', assets: [], albumId: null, + album: null, }), }; diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index f0043d174a..0db58e2eed 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -1,5 +1,5 @@ import { UserAdmin } from 'src/database'; -import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum'; +import { UserStatus } from 'src/enum'; import { authStub } from 'test/fixtures/auth.stub'; export const userStub = { @@ -12,6 +12,7 @@ export const userStub = { storageLabel: 'admin', oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, @@ -28,16 +29,12 @@ export const userStub = { storageLabel: null, oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, updatedAt: new Date('2021-01-01'), - metadata: [ - { - key: UserMetadataKey.PREFERENCES, - value: { avatar: { color: UserAvatarColor.PRIMARY } }, - }, - ], + metadata: [], quotaSizeInBytes: null, quotaUsageInBytes: 0, }, @@ -50,6 +47,7 @@ export const userStub = { storageLabel: null, oauthId: '', shouldChangePassword: false, + avatarColor: null, profileImagePath: '', createdAt: new Date('2021-01-01'), deletedAt: null, diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d3ab876e07..cab74f70fb 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -4,18 +4,22 @@ import { DateTime } from 'luxon'; import { createHash, randomBytes } from 'node:crypto'; import { Writable } from 'node:stream'; import { AssetFace } from 'src/database'; -import { AssetJobStatus, Assets, DB, FaceSearch, Person, Sessions } from 'src/db'; -import { AssetType, SourceType } from 'src/enum'; +import { Albums, AssetJobStatus, Assets, DB, FaceSearch, Person, Sessions } from 'src/db'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { AssetType, AssetVisibility, SourceType, SyncRequestType } from 'src/enum'; import { ActivityRepository } from 'src/repositories/activity.repository'; +import { AlbumUserRepository } from 'src/repositories/album-user.repository'; import { AlbumRepository } from 'src/repositories/album.repository'; import { AssetJobRepository } from 'src/repositories/asset-job.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; +import { NotificationRepository } from 'src/repositories/notification.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { SearchRepository } from 'src/repositories/search.repository'; @@ -26,8 +30,9 @@ import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { UserTable } from 'src/schema/tables/user.table'; import { BaseService } from 'src/services/base.service'; +import { SyncService } from 'src/services/sync.service'; import { RepositoryInterface } from 'src/types'; -import { newDate, newEmbedding, newUuid } from 'test/small.factory'; +import { factory, newDate, newEmbedding, newUuid } from 'test/small.factory'; import { automock, ServiceOverrides } from 'test/utils'; import { Mocked } from 'vitest'; @@ -37,15 +42,18 @@ const sha256 = (value: string) => createHash('sha256').update(value).digest('bas type RepositoriesTypes = { activity: ActivityRepository; album: AlbumRepository; + albumUser: AlbumUserRepository; asset: AssetRepository; assetJob: AssetJobRepository; config: ConfigRepository; crypto: CryptoRepository; database: DatabaseRepository; + email: EmailRepository; job: JobRepository; user: UserRepository; logger: LoggingRepository; memory: MemoryRepository; + notification: NotificationRepository; partner: PartnerRepository; person: PersonRepository; search: SearchRepository; @@ -72,6 +80,61 @@ export type Context = { getRepository(key: T): RepositoriesTypes[T]; }; +export type SyncTestOptions = { + db: Kysely; +}; + +export const newSyncAuthUser = () => { + const user = mediumFactory.userInsert(); + const session = mediumFactory.sessionInsert({ userId: user.id }); + + const auth = factory.auth({ + session, + user: { + id: user.id, + name: user.name, + email: user.email, + }, + }); + + return { + auth, + session, + user, + create: async (db: Kysely) => { + await new UserRepository(db).create(user); + await new SessionRepository(db).create(session); + }, + }; +}; + +export const newSyncTest = (options: SyncTestOptions) => { + const { sut, mocks, repos, getRepository } = newMediumService(SyncService, { + database: options.db, + repos: { + sync: 'real', + session: 'real', + }, + }); + + const testSync = async (auth: AuthDto, types: SyncRequestType[]) => { + const stream = mediumFactory.syncStream(); + // Wait for 2ms to ensure all updates are available and account for setTimeout inaccuracy + await new Promise((resolve) => setTimeout(resolve, 2)); + await sut.stream(auth, stream, { types }); + + return stream.getResponse(); + }; + + return { + sut, + mocks, + repos, + getRepository, + testSync, + }; +}; + export const newMediumService = ( Service: ClassConstructor, options: { @@ -121,6 +184,14 @@ export const getRepository = (key: K, db: Kys return new ActivityRepository(db); } + case 'album': { + return new AlbumRepository(db); + } + + case 'albumUser': { + return new AlbumUserRepository(db); + } + case 'asset': { return new AssetRepository(db); } @@ -138,19 +209,25 @@ export const getRepository = (key: K, db: Kys } case 'database': { - const configRepo = new ConfigRepository(); - return new DatabaseRepository(db, new LoggingRepository(undefined, configRepo), configRepo); + return new DatabaseRepository(db, LoggingRepository.create(), new ConfigRepository()); + } + + case 'email': { + return new EmailRepository(LoggingRepository.create()); } case 'logger': { - const configMock = { getEnv: () => ({ noColor: false }) }; - return new LoggingRepository(undefined, configMock as ConfigRepository); + return LoggingRepository.create(); } case 'memory': { return new MemoryRepository(db); } + case 'notification': { + return new NotificationRepository(db); + } + case 'partner': { return new PartnerRepository(db); } @@ -217,12 +294,37 @@ const getRepositoryMock = (key: K) => { case 'database': { return automock(DatabaseRepository, { - args: [undefined, { setContext: () => {} }, { getEnv: () => ({ database: { vectorExtension: '' } }) }], + args: [ + undefined, + { + setContext: () => {}, + }, + { getEnv: () => ({ database: { vectorExtension: '' } }) }, + ], + }); + } + + case 'email': { + return automock(EmailRepository, { + args: [ + { + setContext: () => {}, + }, + ], }); } case 'job': { - return automock(JobRepository, { args: [undefined, undefined, undefined, { setContext: () => {} }] }); + return automock(JobRepository, { + args: [ + undefined, + undefined, + undefined, + { + setContext: () => {}, + }, + ], + }); } case 'logger': { @@ -234,6 +336,10 @@ const getRepositoryMock = (key: K) => { return automock(MemoryRepository); } + case 'notification': { + return automock(NotificationRepository); + } + case 'partner': { return automock(PartnerRepository); } @@ -284,6 +390,7 @@ export const asDeps = (repositories: ServiceOverrides) => { repositories.crypto || getRepositoryMock('crypto'), repositories.database || getRepositoryMock('database'), repositories.downloadRepository, + repositories.email || getRepositoryMock('email'), repositories.event, repositories.job || getRepositoryMock('job'), repositories.library, @@ -293,7 +400,7 @@ export const asDeps = (repositories: ServiceOverrides) => { repositories.memory || getRepositoryMock('memory'), repositories.metadata, repositories.move, - repositories.notification, + repositories.notification || getRepositoryMock('notification'), repositories.oauth, repositories.partner || getRepositoryMock('partner'), repositories.person || getRepositoryMock('person'), @@ -326,11 +433,11 @@ const assetInsert = (asset: Partial> = {}) => { type: AssetType.IMAGE, originalPath: '/path/to/something.jpg', ownerId: '@immich.cloud', - isVisible: true, isFavorite: false, fileCreatedAt: now, fileModifiedAt: now, localDateTime: now, + visibility: AssetVisibility.TIMELINE, }; return { @@ -340,6 +447,19 @@ const assetInsert = (asset: Partial> = {}) => { }; }; +const albumInsert = (album: Partial> & { ownerId: string }) => { + const id = album.id || newUuid(); + const defaults: Omit, 'ownerId'> = { + albumName: 'Album', + }; + + return { + ...defaults, + ...album, + id, + }; +}; + const faceInsert = (face: Partial> & { faceId: string }) => { const defaults = { faceId: face.faceId, @@ -462,6 +582,7 @@ export const mediumFactory = { assetInsert, assetFaceInsert, assetJobStatusInsert, + albumInsert, faceInsert, personInsert, sessionInsert, diff --git a/server/test/medium/globalSetup.ts b/server/test/medium/globalSetup.ts index 89b25da766..c1bc755a0e 100644 --- a/server/test/medium/globalSetup.ts +++ b/server/test/medium/globalSetup.ts @@ -1,13 +1,13 @@ -import { FileMigrationProvider, Kysely, Migrator } from 'kysely'; -import { mkdir, readdir } from 'node:fs/promises'; -import { join } from 'node:path'; -import { parse } from 'pg-connection-string'; +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; import { getKyselyConfig } from 'src/utils/database'; import { GenericContainer, Wait } from 'testcontainers'; -import { DataSource } from 'typeorm'; const globalSetup = async () => { - const postgresContainer = await new GenericContainer('tensorchord/pgvecto-rs:pg14-v0.2.0') + const postgresContainer = await new GenericContainer('ghcr.io/immich-app/postgres:14-vectorchord0.4.1') .withExposedPorts(5432) .withEnvironment({ POSTGRES_PASSWORD: 'postgres', @@ -17,9 +17,7 @@ const globalSetup = async () => { .withCommand([ 'postgres', '-c', - 'shared_preload_libraries=vectors.so', - '-c', - 'search_path="$$user", public, vectors', + 'shared_preload_libraries=vchord.so', '-c', 'max_wal_size=2GB', '-c', @@ -30,72 +28,22 @@ const globalSetup = async () => { 'full_page_writes=off', '-c', 'synchronous_commit=off', + '-c', + 'config_file=/var/lib/postgresql/data/postgresql.conf', ]) .withWaitStrategy(Wait.forAll([Wait.forLogMessage('database system is ready to accept connections', 2)])) .start(); const postgresPort = postgresContainer.getMappedPort(5432); const postgresUrl = `postgres://postgres:postgres@localhost:${postgresPort}/immich`; + process.env.IMMICH_TEST_POSTGRES_URL = postgresUrl; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const modules = import.meta.glob('/src/migrations/*.ts', { eager: true }); + const db = new Kysely(getKyselyConfig({ connectionType: 'url', url: postgresUrl })); - const config = { - type: 'postgres' as const, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - migrations: Object.values(modules).map((module) => Object.values(module)[0]), - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - url: postgresUrl, - }; - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const dataSource = new DataSource(config); - await dataSource.initialize(); - await dataSource.runMigrations(); - await dataSource.destroy(); - - // for whatever reason, importing from test/utils causes vitest to crash - // eslint-disable-next-line unicorn/prefer-module - const migrationFolder = join(__dirname, '..', 'schema/migrations'); - // TODO remove after we have at least one kysely migration - await mkdir(migrationFolder, { recursive: true }); - - const parsed = parse(process.env.IMMICH_TEST_POSTGRES_URL!); - - const parsedOptions = { - ...parsed, - ssl: false, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }; - - const db = new Kysely(getKyselyConfig(parsedOptions)); - - // TODO just call `databaseRepository.migrate()` (probably have to wait until TypeOrm is gone) - const migrator = new Migrator({ - db, - migrationLockTableName: 'kysely_migrations_lock', - migrationTableName: 'kysely_migrations', - provider: new FileMigrationProvider({ - fs: { readdir }, - path: { join }, - migrationFolder, - }), - }); - - const { error } = await migrator.migrateToLatest(); - if (error) { - console.error('Unable to run kysely migrations', error); - throw error; - } + const configRepository = new ConfigRepository(); + const logger = LoggingRepository.create(); + await new DatabaseRepository(db, logger, configRepository).runMigrations(); await db.destroy(); }; diff --git a/server/test/medium/responses.ts b/server/test/medium/responses.ts index 0148f2e1e9..e9f9daaf76 100644 --- a/server/test/medium/responses.ts +++ b/server/test/medium/responses.ts @@ -47,7 +47,6 @@ export const errorDto = { error: 'Bad Request', statusCode: 400, message: message ?? expect.anything(), - correlationId: expect.any(String), }), noPermission: { error: 'Bad Request', @@ -67,12 +66,6 @@ export const errorDto = { message: 'The server already has an admin', correlationId: expect.any(String), }, - invalidEmail: { - error: 'Bad Request', - statusCode: 400, - message: ['email must be an email'], - correlationId: expect.any(String), - }, }; export const signupResponseDto = { diff --git a/server/test/medium/specs/controllers/auth.controller.spec.ts b/server/test/medium/specs/controllers/auth.controller.spec.ts deleted file mode 100644 index ef2b904f48..0000000000 --- a/server/test/medium/specs/controllers/auth.controller.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { AuthController } from 'src/controllers/auth.controller'; -import { AuthService } from 'src/services/auth.service'; -import request from 'supertest'; -import { errorDto } from 'test/medium/responses'; -import { createControllerTestApp, TestControllerApp } from 'test/medium/utils'; - -describe(AuthController.name, () => { - let app: TestControllerApp; - - beforeAll(async () => { - app = await createControllerTestApp(); - }); - - describe('POST /auth/admin-sign-up', () => { - const name = 'admin'; - const email = 'admin@immich.cloud'; - const password = 'password'; - - const invalid = [ - { - should: 'require an email address', - data: { name, password }, - }, - { - should: 'require a password', - data: { name, email }, - }, - { - should: 'require a name', - data: { email, password }, - }, - { - should: 'require a valid email', - data: { name, email: 'immich', password }, - }, - ]; - - for (const { should, data } of invalid) { - it(`should ${should}`, async () => { - const { status, body } = await request(app.getHttpServer()).post('/auth/admin-sign-up').send(data); - expect(status).toEqual(400); - expect(body).toEqual(errorDto.badRequest()); - }); - } - - it('should transform email to lower case', async () => { - const { status } = await request(app.getHttpServer()) - .post('/auth/admin-sign-up') - .send({ name: 'admin', password: 'password', email: 'aDmIn@IMMICH.cloud' }); - expect(status).toEqual(201); - expect(app.getMockedService(AuthService).adminSignUp).toHaveBeenCalledWith( - expect.objectContaining({ email: 'admin@immich.cloud' }), - ); - }); - }); - - afterAll(async () => { - await app.close(); - }); -}); diff --git a/server/test/medium/specs/controllers/user.controller.spec.ts b/server/test/medium/specs/controllers/user.controller.spec.ts deleted file mode 100644 index f4d90d5469..0000000000 --- a/server/test/medium/specs/controllers/user.controller.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { UserController } from 'src/controllers/user.controller'; -import { AuthService } from 'src/services/auth.service'; -import { UserService } from 'src/services/user.service'; -import request from 'supertest'; -import { errorDto } from 'test/medium/responses'; -import { createControllerTestApp, TestControllerApp } from 'test/medium/utils'; -import { factory } from 'test/small.factory'; - -describe(UserController.name, () => { - let realApp: TestControllerApp; - let mockApp: TestControllerApp; - - beforeAll(async () => { - realApp = await createControllerTestApp({ authType: 'real' }); - mockApp = await createControllerTestApp({ authType: 'mock' }); - }); - - describe('GET /users', () => { - it('should require authentication', async () => { - const { status, body } = await request(realApp.getHttpServer()).get('/users'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should call the service with an auth dto', async () => { - const user = factory.user(); - const authService = mockApp.getMockedService(AuthService); - const auth = factory.auth({ user }); - authService.authenticate.mockResolvedValue(auth); - - const userService = mockApp.getMockedService(UserService); - const { status } = await request(mockApp.getHttpServer()).get('/users').set('Authorization', `Bearer token`); - - expect(status).toBe(200); - expect(userService.search).toHaveBeenCalledWith(auth); - }); - }); - - describe('GET /users/me', () => { - it('should require authentication', async () => { - const { status, body } = await request(realApp.getHttpServer()).get(`/users/me`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - }); - - describe('PUT /users/me', () => { - it('should require authentication', async () => { - const { status, body } = await request(realApp.getHttpServer()).put(`/users/me`); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - for (const key of ['email', 'name']) { - it(`should not allow null ${key}`, async () => { - const dto = { [key]: null }; - const { status, body } = await request(mockApp.getHttpServer()) - .put(`/users/me`) - .set('Authorization', `Bearer token`) - .send(dto); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest()); - }); - } - }); - - describe('GET /users/:id', () => { - it('should require authentication', async () => { - const { status } = await request(realApp.getHttpServer()).get(`/users/${factory.uuid()}`); - expect(status).toEqual(401); - }); - }); - - describe('GET /server/license', () => { - it('should require authentication', async () => { - const { status, body } = await request(realApp.getHttpServer()).get('/users/me/license'); - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - }); - - describe('PUT /users/me/license', () => { - it('should require authentication', async () => { - const { status } = await request(realApp.getHttpServer()).put(`/users/me/license`); - expect(status).toEqual(401); - }); - }); - - describe('DELETE /users/me/license', () => { - it('should require authentication', async () => { - const { status } = await request(realApp.getHttpServer()).put(`/users/me/license`); - expect(status).toEqual(401); - }); - }); - - afterAll(async () => { - await realApp.close(); - await mockApp.close(); - }); -}); diff --git a/server/test/medium/specs/services/memory.service.spec.ts b/server/test/medium/specs/services/memory.service.spec.ts index 172c48ca5b..8489e6bcc9 100644 --- a/server/test/medium/specs/services/memory.service.spec.ts +++ b/server/test/medium/specs/services/memory.service.spec.ts @@ -15,6 +15,7 @@ describe(MemoryService.name, () => { database: db || defaultDatabase, repos: { asset: 'real', + database: 'real', memory: 'real', user: 'real', systemMetadata: 'real', @@ -39,9 +40,12 @@ describe(MemoryService.name, () => { it('should create a memory from an asset', async () => { const { sut, repos, getRepository } = createSut(); - const now = DateTime.fromObject({ year: 2025, month: 2, day: 25 }, { zone: 'utc' }); + const now = DateTime.fromObject({ year: 2025, month: 2, day: 25 }, { zone: 'utc' }) as DateTime; const user = mediumFactory.userInsert(); - const asset = mediumFactory.assetInsert({ ownerId: user.id, localDateTime: now.minus({ years: 1 }).toISO() }); + const asset = mediumFactory.assetInsert({ + ownerId: user.id, + localDateTime: now.minus({ years: 1 }).toISO(), + }); const jobStatus = mediumFactory.assetJobStatusInsert({ assetId: asset.id }); const userRepo = getRepository('user'); @@ -86,7 +90,7 @@ describe(MemoryService.name, () => { it('should not generate a memory twice for the same day', async () => { const { sut, repos, getRepository } = createSut(); - const now = DateTime.fromObject({ year: 2025, month: 2, day: 20 }, { zone: 'utc' }); + const now = DateTime.fromObject({ year: 2025, month: 2, day: 20 }, { zone: 'utc' }) as DateTime; const assetRepo = getRepository('asset'); const memoryRepo = getRepository('memory'); diff --git a/server/test/medium/specs/services/metadata.service.spec.ts b/server/test/medium/specs/services/metadata.service.spec.ts index b25cce2724..13b9867373 100644 --- a/server/test/medium/specs/services/metadata.service.spec.ts +++ b/server/test/medium/specs/services/metadata.service.spec.ts @@ -118,7 +118,7 @@ describe(MetadataService.name, () => { process.env.TZ = serverTimeZone ?? undefined; const { filePath } = await createTestFile(exifData); - mocks.assetJob.getForMetadataExtraction.mockResolvedValue({ id: 'asset-1', originalPath: filePath } as never); + mocks.assetJob.getForMetadataExtraction.mockResolvedValue({ id: 'asset-1', originalPath: filePath } as any); await sut.handleMetadataExtraction({ id: 'asset-1' }); diff --git a/server/test/medium/specs/services/sync.service.spec.ts b/server/test/medium/specs/services/sync.service.spec.ts deleted file mode 100644 index 98df296cbf..0000000000 --- a/server/test/medium/specs/services/sync.service.spec.ts +++ /dev/null @@ -1,910 +0,0 @@ -import { AuthDto } from 'src/dtos/auth.dto'; -import { SyncEntityType, SyncRequestType } from 'src/enum'; -import { SYNC_TYPES_ORDER, SyncService } from 'src/services/sync.service'; -import { mediumFactory, newMediumService } from 'test/medium.factory'; -import { factory } from 'test/small.factory'; -import { getKyselyDB } from 'test/utils'; - -const setup = async () => { - const db = await getKyselyDB(); - - const { sut, mocks, repos, getRepository } = newMediumService(SyncService, { - database: db, - repos: { - sync: 'real', - session: 'real', - }, - }); - - const user = mediumFactory.userInsert(); - const session = mediumFactory.sessionInsert({ userId: user.id }); - const auth = factory.auth({ - session, - user: { - id: user.id, - name: user.name, - email: user.email, - }, - }); - - await getRepository('user').create(user); - await getRepository('session').create(session); - - const testSync = async (auth: AuthDto, types: SyncRequestType[]) => { - const stream = mediumFactory.syncStream(); - // Wait for 1ms to ensure all updates are available - await new Promise((resolve) => setTimeout(resolve, 1)); - await sut.stream(auth, stream, { types }); - - return stream.getResponse(); - }; - - return { - sut, - auth, - mocks, - repos, - getRepository, - testSync, - }; -}; - -describe(SyncService.name, () => { - it('should have all the types in the ordering variable', () => { - for (const key in SyncRequestType) { - expect(SYNC_TYPES_ORDER).includes(key); - } - - expect(SYNC_TYPES_ORDER.length).toBe(Object.keys(SyncRequestType).length); - }); - - describe.concurrent(SyncEntityType.UserV1, () => { - it('should detect and sync the first user', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user = await userRepo.get(auth.user.id, { withDeleted: false }); - if (!user) { - expect.fail('First user should exist'); - } - - const initialSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual([ - { - ack: expect.any(String), - data: { - deletedAt: user.deletedAt, - email: user.email, - id: user.id, - name: user.name, - }, - type: 'UserV1', - }, - ]); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a soft deleted user', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const deletedAt = new Date().toISOString(); - const deletedUser = mediumFactory.userInsert({ deletedAt }); - const deleted = await getRepository('user').create(deletedUser); - - const response = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(response).toHaveLength(2); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, - type: 'UserV1', - }, - { - ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, - type: 'UserV1', - }, - ]), - ); - - const acks = [response[1].ack]; - await sut.setAcks(auth, { acks }); - const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a deleted user', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user = mediumFactory.userInsert(); - await userRepo.create(user); - await userRepo.delete({ id: user.id }, true); - - const response = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(response).toHaveLength(2); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - userId: user.id, - }, - type: 'UserDeleteV1', - }, - { - ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, - type: 'UserV1', - }, - ]), - ); - - const acks = response.map(({ ack }) => ack); - await sut.setAcks(auth, { acks }); - const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should sync a user and then an update to that same user', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, - type: 'UserV1', - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const userRepo = getRepository('user'); - const updated = await userRepo.update(auth.user.id, { name: 'new name' }); - const updatedSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); - - expect(updatedSyncResponse).toHaveLength(1); - expect(updatedSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, - type: 'UserV1', - }, - ]), - ); - }); - }); - - describe.concurrent(SyncEntityType.PartnerV1, () => { - it('should detect and sync the first partner', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const user1 = auth.user; - const userRepo = getRepository('user'); - const partnerRepo = getRepository('partner'); - - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - inTimeline: partner.inTimeline, - sharedById: partner.sharedById, - sharedWithId: partner.sharedWithId, - }, - type: 'PartnerV1', - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a deleted partner', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user1 = auth.user; - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partnerRepo = getRepository('partner'); - const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); - await partnerRepo.remove(partner); - - const response = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(response).toHaveLength(1); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - sharedById: partner.sharedById, - sharedWithId: partner.sharedWithId, - }, - type: 'PartnerDeleteV1', - }, - ]), - ); - - const acks = response.map(({ ack }) => ack); - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a partner share both to and from another user', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user1 = auth.user; - const user2 = await userRepo.create(mediumFactory.userInsert()); - - const partnerRepo = getRepository('partner'); - const partner1 = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); - const partner2 = await partnerRepo.create({ sharedById: user1.id, sharedWithId: user2.id }); - - const response = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(response).toHaveLength(2); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - inTimeline: partner1.inTimeline, - sharedById: partner1.sharedById, - sharedWithId: partner1.sharedWithId, - }, - type: 'PartnerV1', - }, - { - ack: expect.any(String), - data: { - inTimeline: partner2.inTimeline, - sharedById: partner2.sharedById, - sharedWithId: partner2.sharedWithId, - }, - type: 'PartnerV1', - }, - ]), - ); - - await sut.setAcks(auth, { acks: [response[1].ack] }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should sync a partner and then an update to that same partner', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user1 = auth.user; - const user2 = await userRepo.create(mediumFactory.userInsert()); - - const partnerRepo = getRepository('partner'); - const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - inTimeline: partner.inTimeline, - sharedById: partner.sharedById, - sharedWithId: partner.sharedWithId, - }, - type: 'PartnerV1', - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const updated = await partnerRepo.update( - { sharedById: partner.sharedById, sharedWithId: partner.sharedWithId }, - { inTimeline: true }, - ); - - const updatedSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); - - expect(updatedSyncResponse).toHaveLength(1); - expect(updatedSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - inTimeline: updated.inTimeline, - sharedById: updated.sharedById, - sharedWithId: updated.sharedWithId, - }, - type: 'PartnerV1', - }, - ]), - ); - }); - - it('should not sync a partner or partner delete for an unrelated user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = await userRepo.create(mediumFactory.userInsert()); - const user3 = await userRepo.create(mediumFactory.userInsert()); - - const partnerRepo = getRepository('partner'); - const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user3.id }); - - expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); - - await partnerRepo.remove(partner); - - expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); - }); - - it('should not sync a partner delete after a user is deleted', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = await userRepo.create(mediumFactory.userInsert()); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - await userRepo.delete({ id: user2.id }, true); - - expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); - }); - }); - - describe.concurrent(SyncEntityType.AssetV1, () => { - it('should detect and sync the first asset', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const checksum = '1115vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; - const thumbhash = '2225vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; - const date = new Date().toISOString(); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ - ownerId: auth.user.id, - checksum: Buffer.from(checksum, 'base64'), - thumbhash: Buffer.from(thumbhash, 'base64'), - fileCreatedAt: date, - fileModifiedAt: date, - localDateTime: date, - deletedAt: null, - }); - await assetRepo.create(asset); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - id: asset.id, - ownerId: asset.ownerId, - thumbhash, - checksum, - deletedAt: asset.deletedAt, - fileCreatedAt: asset.fileCreatedAt, - fileModifiedAt: asset.fileModifiedAt, - isFavorite: asset.isFavorite, - isVisible: asset.isVisible, - localDateTime: asset.localDateTime, - type: asset.type, - }, - type: 'AssetV1', - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a deleted asset', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); - await assetRepo.create(asset); - await assetRepo.remove(asset); - - const response = await testSync(auth, [SyncRequestType.AssetsV1]); - - expect(response).toHaveLength(1); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - assetId: asset.id, - }, - type: 'AssetDeleteV1', - }, - ]), - ); - - const acks = response.map(({ ack }) => ack); - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should not sync an asset or asset delete for an unrelated user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const sessionRepo = getRepository('session'); - const session = mediumFactory.sessionInsert({ userId: user2.id }); - await sessionRepo.create(session); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: user2.id }); - await assetRepo.create(asset); - - const auth2 = factory.auth({ session, user: user2 }); - - expect(await testSync(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); - expect(await testSync(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); - - await assetRepo.remove(asset); - expect(await testSync(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); - expect(await testSync(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); - }); - }); - - describe.concurrent(SyncRequestType.PartnerAssetsV1, () => { - it('should detect and sync the first partner asset', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const checksum = '1115vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; - const thumbhash = '2225vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; - const date = new Date().toISOString(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ - ownerId: user2.id, - checksum: Buffer.from(checksum, 'base64'), - thumbhash: Buffer.from(thumbhash, 'base64'), - fileCreatedAt: date, - fileModifiedAt: date, - localDateTime: date, - deletedAt: null, - }); - await assetRepo.create(asset); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - id: asset.id, - ownerId: asset.ownerId, - thumbhash, - checksum, - deletedAt: null, - fileCreatedAt: date, - fileModifiedAt: date, - isFavorite: false, - isVisible: true, - localDateTime: date, - type: asset.type, - }, - type: SyncEntityType.PartnerAssetV1, - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should detect and sync a deleted partner asset', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - const asset = mediumFactory.assetInsert({ ownerId: user2.id }); - - const assetRepo = getRepository('asset'); - await assetRepo.create(asset); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - await assetRepo.remove(asset); - - const response = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); - - expect(response).toHaveLength(1); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - assetId: asset.id, - }, - type: SyncEntityType.PartnerAssetDeleteV1, - }, - ]), - ); - - const acks = response.map(({ ack }) => ack); - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should not sync a deleted partner asset due to a user delete', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const assetRepo = getRepository('asset'); - await assetRepo.create(mediumFactory.assetInsert({ ownerId: user2.id })); - - await userRepo.delete({ id: user2.id }, true); - - const response = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); - expect(response).toHaveLength(0); - }); - - it('should not sync a deleted partner asset due to a partner delete (unshare)', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const assetRepo = getRepository('asset'); - await assetRepo.create(mediumFactory.assetInsert({ ownerId: user2.id })); - - const partnerRepo = getRepository('partner'); - const partner = { sharedById: user2.id, sharedWithId: auth.user.id }; - await partnerRepo.create(partner); - - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(1); - - await partnerRepo.remove(partner); - - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); - }); - - it('should not sync an asset or asset delete for own user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); - await assetRepo.create(asset); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - await expect(testSync(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); - - await assetRepo.remove(asset); - - await expect(testSync(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); - }); - - it('should not sync an asset or asset delete for unrelated user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const sessionRepo = getRepository('session'); - const session = mediumFactory.sessionInsert({ userId: user2.id }); - await sessionRepo.create(session); - - const auth2 = factory.auth({ session, user: user2 }); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: user2.id }); - await assetRepo.create(asset); - - await expect(testSync(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); - - await assetRepo.remove(asset); - - await expect(testSync(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); - }); - }); - - describe.concurrent(SyncRequestType.AssetExifsV1, () => { - it('should detect and sync the first asset exif', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); - await assetRepo.create(asset); - await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.AssetExifsV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - assetId: asset.id, - city: null, - country: null, - dateTimeOriginal: null, - description: '', - exifImageHeight: null, - exifImageWidth: null, - exposureTime: null, - fNumber: null, - fileSizeInByte: null, - focalLength: null, - fps: null, - iso: null, - latitude: null, - lensModel: null, - longitude: null, - make: 'Canon', - model: null, - modifyDate: null, - orientation: null, - profileDescription: null, - projectionType: null, - rating: null, - state: null, - timeZone: null, - }, - type: SyncEntityType.AssetExifV1, - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetExifsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should only sync asset exif for own user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: user2.id }); - await assetRepo.create(asset); - await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); - - const sessionRepo = getRepository('session'); - const session = mediumFactory.sessionInsert({ userId: user2.id }); - await sessionRepo.create(session); - - const auth2 = factory.auth({ session, user: user2 }); - await expect(testSync(auth2, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(0); - }); - }); - - describe.concurrent(SyncRequestType.PartnerAssetExifsV1, () => { - it('should detect and sync the first partner asset exif', async () => { - const { auth, sut, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: user2.id }); - await assetRepo.create(asset); - await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); - - const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetExifsV1]); - - expect(initialSyncResponse).toHaveLength(1); - expect(initialSyncResponse).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - assetId: asset.id, - city: null, - country: null, - dateTimeOriginal: null, - description: '', - exifImageHeight: null, - exifImageWidth: null, - exposureTime: null, - fNumber: null, - fileSizeInByte: null, - focalLength: null, - fps: null, - iso: null, - latitude: null, - lensModel: null, - longitude: null, - make: 'Canon', - model: null, - modifyDate: null, - orientation: null, - profileDescription: null, - projectionType: null, - rating: null, - state: null, - timeZone: null, - }, - type: SyncEntityType.PartnerAssetExifV1, - }, - ]), - ); - - const acks = [initialSyncResponse[0].ack]; - await sut.setAcks(auth, { acks }); - - const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetExifsV1]); - - expect(ackSyncResponse).toHaveLength(0); - }); - - it('should not sync partner asset exif for own user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - const user2 = mediumFactory.userInsert(); - await userRepo.create(user2); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); - await assetRepo.create(asset); - await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); - - await expect(testSync(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); - }); - - it('should not sync partner asset exif for unrelated user', async () => { - const { auth, getRepository, testSync } = await setup(); - - const userRepo = getRepository('user'); - - const user2 = mediumFactory.userInsert(); - const user3 = mediumFactory.userInsert(); - await Promise.all([userRepo.create(user2), userRepo.create(user3)]); - - const partnerRepo = getRepository('partner'); - await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); - - const assetRepo = getRepository('asset'); - const asset = mediumFactory.assetInsert({ ownerId: user3.id }); - await assetRepo.create(asset); - await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); - - const sessionRepo = getRepository('session'); - const session = mediumFactory.sessionInsert({ userId: user3.id }); - await sessionRepo.create(session); - - const authUser3 = factory.auth({ session, user: user3 }); - await expect(testSync(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(testSync(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); - }); - }); -}); diff --git a/server/test/medium/specs/sync/sync-album-user.spec.ts b/server/test/medium/specs/sync/sync-album-user.spec.ts new file mode 100644 index 0000000000..4967df5264 --- /dev/null +++ b/server/test/medium/specs/sync/sync-album-user.spec.ts @@ -0,0 +1,269 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncRequestType.AlbumUsersV1, () => { + it('should sync an album user with the correct properties', async () => { + const { auth, getRepository, testSync } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + + const user = mediumFactory.userInsert(); + await userRepo.create(user); + + const albumUser = { albumsId: album.id, usersId: user.id, role: AlbumUserRole.EDITOR }; + await albumUserRepo.create(albumUser); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: albumUser.albumsId, + role: albumUser.role, + userId: albumUser.usersId, + }), + type: SyncEntityType.AlbumUserV1, + }, + ]); + }); + describe('owner', () => { + it('should detect and sync a new shared user', async () => { + const { auth, testSync, getRepository } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user1 = mediumFactory.userInsert(); + await userRepo.create(user1); + + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + + const albumUser = { albumsId: album.id, usersId: user1.id, role: AlbumUserRole.EDITOR }; + await albumUserRepo.create(albumUser); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: albumUser.albumsId, + role: albumUser.role, + userId: albumUser.usersId, + }), + type: SyncEntityType.AlbumUserV1, + }, + ]); + }); + + it('should detect and sync an updated shared user', async () => { + const { auth, testSync, getRepository, sut } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user1 = mediumFactory.userInsert(); + await userRepo.create(user1); + + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + + const albumUser = { albumsId: album.id, usersId: user1.id, role: AlbumUserRole.EDITOR }; + await albumUserRepo.create(albumUser); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumUsersV1]); + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + + await albumUserRepo.update({ albumsId: album.id, usersId: user1.id }, { role: AlbumUserRole.VIEWER }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: albumUser.albumsId, + role: AlbumUserRole.VIEWER, + userId: albumUser.usersId, + }), + type: SyncEntityType.AlbumUserV1, + }, + ]); + }); + + it('should detect and sync a deleted shared user', async () => { + const { auth, testSync, getRepository, sut } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user1 = mediumFactory.userInsert(); + await userRepo.create(user1); + + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + + const albumUser = { albumsId: album.id, usersId: user1.id, role: AlbumUserRole.EDITOR }; + await albumUserRepo.create(albumUser); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumUsersV1]); + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + + await albumUserRepo.delete({ albumsId: album.id, usersId: user1.id }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: albumUser.albumsId, + userId: albumUser.usersId, + }), + type: SyncEntityType.AlbumUserDeleteV1, + }, + ]); + }); + }); + + describe('shared user', () => { + it('should detect and sync a new shared user', async () => { + const { auth, testSync, getRepository } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user1 = mediumFactory.userInsert(); + await userRepo.create(user1); + + const album = mediumFactory.albumInsert({ ownerId: user1.id }); + await albumRepo.create(album, [], []); + + const albumUser = { albumsId: album.id, usersId: auth.user.id, role: AlbumUserRole.EDITOR }; + await albumUserRepo.create(albumUser); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: albumUser.albumsId, + role: albumUser.role, + userId: albumUser.usersId, + }), + type: SyncEntityType.AlbumUserV1, + }, + ]); + }); + + it('should detect and sync an updated shared user', async () => { + const { auth, testSync, getRepository, sut } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const owner = mediumFactory.userInsert(); + const user = mediumFactory.userInsert(); + await Promise.all([userRepo.create(owner), userRepo.create(user)]); + + const album = mediumFactory.albumInsert({ ownerId: owner.id }); + await albumRepo.create( + album, + [], + [ + { userId: auth.user.id, role: AlbumUserRole.EDITOR }, + { userId: user.id, role: AlbumUserRole.EDITOR }, + ], + ); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumUsersV1]); + expect(initialSyncResponse).toHaveLength(2); + const acks = [initialSyncResponse[1].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + + await albumUserRepo.update({ albumsId: album.id, usersId: user.id }, { role: AlbumUserRole.VIEWER }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: album.id, + role: AlbumUserRole.VIEWER, + userId: user.id, + }), + type: SyncEntityType.AlbumUserV1, + }, + ]); + }); + + it('should detect and sync a deleted shared user', async () => { + const { auth, testSync, getRepository, sut } = await setup(); + + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const owner = mediumFactory.userInsert(); + const user = mediumFactory.userInsert(); + await Promise.all([userRepo.create(owner), userRepo.create(user)]); + + const album = mediumFactory.albumInsert({ ownerId: owner.id }); + await albumRepo.create( + album, + [], + [ + { userId: auth.user.id, role: AlbumUserRole.EDITOR }, + { userId: user.id, role: AlbumUserRole.EDITOR }, + ], + ); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumUsersV1]); + expect(initialSyncResponse).toHaveLength(2); + const acks = [initialSyncResponse[1].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + + await albumUserRepo.delete({ albumsId: album.id, usersId: user.id }); + + await expect(testSync(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + albumId: album.id, + userId: user.id, + }), + type: SyncEntityType.AlbumUserDeleteV1, + }, + ]); + }); + }); +}); diff --git a/server/test/medium/specs/sync/sync-album.spec.ts b/server/test/medium/specs/sync/sync-album.spec.ts new file mode 100644 index 0000000000..7ee7bf624f --- /dev/null +++ b/server/test/medium/specs/sync/sync-album.spec.ts @@ -0,0 +1,220 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncRequestType.AlbumsV1, () => { + it('should sync an album with the correct properties', async () => { + const { auth, getRepository, testSync } = await setup(); + const albumRepo = getRepository('album'); + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: album.id, + name: album.albumName, + ownerId: album.ownerId, + }), + type: SyncEntityType.AlbumV1, + }, + ]); + }); + + it('should detect and sync a new album', async () => { + const { auth, getRepository, testSync } = await setup(); + const albumRepo = getRepository('album'); + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: album.id, + }), + type: SyncEntityType.AlbumV1, + }, + ]); + }); + + it('should detect and sync an album delete', async () => { + const { auth, getRepository, testSync } = await setup(); + const albumRepo = getRepository('album'); + const album = mediumFactory.albumInsert({ ownerId: auth.user.id }); + await albumRepo.create(album, [], []); + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: album.id, + }), + type: SyncEntityType.AlbumV1, + }, + ]); + + await albumRepo.delete(album.id); + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { + albumId: album.id, + }, + type: SyncEntityType.AlbumDeleteV1, + }, + ]); + }); + + describe('shared albums', () => { + it('should detect and sync an album create', async () => { + const { auth, getRepository, testSync } = await setup(); + const albumRepo = getRepository('album'); + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const album = mediumFactory.albumInsert({ ownerId: user2.id }); + await albumRepo.create(album, [], [{ userId: auth.user.id, role: AlbumUserRole.EDITOR }]); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ id: album.id }), + type: SyncEntityType.AlbumV1, + }, + ]); + }); + + it('should detect and sync an album share (share before sync)', async () => { + const { auth, getRepository, testSync } = await setup(); + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const album = mediumFactory.albumInsert({ ownerId: user2.id }); + await albumRepo.create(album, [], []); + await albumUserRepo.create({ usersId: auth.user.id, albumsId: album.id, role: AlbumUserRole.EDITOR }); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ id: album.id }), + type: SyncEntityType.AlbumV1, + }, + ]); + }); + + it('should detect and sync an album share (share after sync)', async () => { + const { auth, getRepository, sut, testSync } = await setup(); + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const userAlbum = mediumFactory.albumInsert({ ownerId: auth.user.id }); + const user2Album = mediumFactory.albumInsert({ ownerId: user2.id }); + await Promise.all([albumRepo.create(user2Album, [], []), albumRepo.create(userAlbum, [], [])]); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumsV1]); + + expect(initialSyncResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ id: userAlbum.id }), + type: SyncEntityType.AlbumV1, + }, + ]); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + await albumUserRepo.create({ usersId: auth.user.id, albumsId: user2Album.id, role: AlbumUserRole.EDITOR }); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ id: user2Album.id }), + type: SyncEntityType.AlbumV1, + }, + ]); + }); + + it('should detect and sync an album delete`', async () => { + const { auth, getRepository, testSync, sut } = await setup(); + const albumRepo = getRepository('album'); + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const album = mediumFactory.albumInsert({ ownerId: user2.id }); + await albumRepo.create(album, [], [{ userId: auth.user.id, role: AlbumUserRole.EDITOR }]); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumsV1]); + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + + await albumRepo.delete(album.id); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { albumId: album.id }, + type: SyncEntityType.AlbumDeleteV1, + }, + ]); + }); + + it('should detect and sync an album unshare as an album delete', async () => { + const { auth, getRepository, testSync, sut } = await setup(); + const albumRepo = getRepository('album'); + const albumUserRepo = getRepository('albumUser'); + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const album = mediumFactory.albumInsert({ ownerId: user2.id }); + await albumRepo.create(album, [], [{ userId: auth.user.id, role: AlbumUserRole.EDITOR }]); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AlbumsV1]); + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + + await albumUserRepo.delete({ albumsId: album.id, usersId: auth.user.id }); + + await expect(testSync(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { albumId: album.id }, + type: SyncEntityType.AlbumDeleteV1, + }, + ]); + }); + }); +}); diff --git a/server/test/medium/specs/sync/sync-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-asset-exif.spec.ts new file mode 100644 index 0000000000..9a3bcb4314 --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-exif.spec.ts @@ -0,0 +1,100 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncRequestType.AssetExifsV1, () => { + it('should detect and sync the first asset exif', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); + await assetRepo.create(asset); + await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AssetExifsV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + city: null, + country: null, + dateTimeOriginal: null, + description: '', + exifImageHeight: null, + exifImageWidth: null, + exposureTime: null, + fNumber: null, + fileSizeInByte: null, + focalLength: null, + fps: null, + iso: null, + latitude: null, + lensModel: null, + longitude: null, + make: 'Canon', + model: null, + modifyDate: null, + orientation: null, + profileDescription: null, + projectionType: null, + rating: null, + state: null, + timeZone: null, + }, + type: SyncEntityType.AssetExifV1, + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetExifsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should only sync asset exif for own user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: user2.id }); + await assetRepo.create(asset); + await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); + + const sessionRepo = getRepository('session'); + const session = mediumFactory.sessionInsert({ userId: user2.id }); + await sessionRepo.create(session); + + const auth2 = factory.auth({ session, user: user2 }); + await expect(testSync(auth2, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts new file mode 100644 index 0000000000..3cf6d7d30d --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -0,0 +1,130 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncEntityType.AssetV1, () => { + it('should detect and sync the first asset', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const checksum = '1115vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; + const thumbhash = '2225vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; + const date = new Date().toISOString(); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ + ownerId: auth.user.id, + checksum: Buffer.from(checksum, 'base64'), + thumbhash: Buffer.from(thumbhash, 'base64'), + fileCreatedAt: date, + fileModifiedAt: date, + localDateTime: date, + deletedAt: null, + }); + await assetRepo.create(asset); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + id: asset.id, + ownerId: asset.ownerId, + thumbhash, + checksum, + deletedAt: asset.deletedAt, + fileCreatedAt: asset.fileCreatedAt, + fileModifiedAt: asset.fileModifiedAt, + isFavorite: asset.isFavorite, + localDateTime: asset.localDateTime, + type: asset.type, + visibility: asset.visibility, + }, + type: 'AssetV1', + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a deleted asset', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); + await assetRepo.create(asset); + await assetRepo.remove(asset); + + const response = await testSync(auth, [SyncRequestType.AssetsV1]); + + expect(response).toHaveLength(1); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + }, + type: 'AssetDeleteV1', + }, + ]), + ); + + const acks = response.map(({ ack }) => ack); + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.AssetsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should not sync an asset or asset delete for an unrelated user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const sessionRepo = getRepository('session'); + const session = mediumFactory.sessionInsert({ userId: user2.id }); + await sessionRepo.create(session); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: user2.id }); + await assetRepo.create(asset); + + const auth2 = factory.auth({ session, user: user2 }); + + expect(await testSync(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); + expect(await testSync(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); + + await assetRepo.remove(asset); + expect(await testSync(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); + expect(await testSync(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts new file mode 100644 index 0000000000..8d9e6d6ac5 --- /dev/null +++ b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts @@ -0,0 +1,129 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncRequestType.PartnerAssetExifsV1, () => { + it('should detect and sync the first partner asset exif', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: user2.id }); + await assetRepo.create(asset); + await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetExifsV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + city: null, + country: null, + dateTimeOriginal: null, + description: '', + exifImageHeight: null, + exifImageWidth: null, + exposureTime: null, + fNumber: null, + fileSizeInByte: null, + focalLength: null, + fps: null, + iso: null, + latitude: null, + lensModel: null, + longitude: null, + make: 'Canon', + model: null, + modifyDate: null, + orientation: null, + profileDescription: null, + projectionType: null, + rating: null, + state: null, + timeZone: null, + }, + type: SyncEntityType.PartnerAssetExifV1, + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetExifsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should not sync partner asset exif for own user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); + await assetRepo.create(asset); + await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); + + await expect(testSync(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); + }); + + it('should not sync partner asset exif for unrelated user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + + const user2 = mediumFactory.userInsert(); + const user3 = mediumFactory.userInsert(); + await Promise.all([userRepo.create(user2), userRepo.create(user3)]); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: user3.id }); + await assetRepo.create(asset); + await assetRepo.upsertExif({ assetId: asset.id, make: 'Canon' }); + + const sessionRepo = getRepository('session'); + const session = mediumFactory.sessionInsert({ userId: user3.id }); + await sessionRepo.create(session); + + const authUser3 = factory.auth({ session, user: user3 }); + await expect(testSync(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts new file mode 100644 index 0000000000..70e31eca4c --- /dev/null +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -0,0 +1,208 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncRequestType.PartnerAssetsV1, () => { + it('should detect and sync the first partner asset', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const checksum = '1115vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; + const thumbhash = '2225vHcVkZzNp3Q9G+FEA0nu6zUbGb4Tj4UOXkN0wRA='; + const date = new Date().toISOString(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ + ownerId: user2.id, + checksum: Buffer.from(checksum, 'base64'), + thumbhash: Buffer.from(thumbhash, 'base64'), + fileCreatedAt: date, + fileModifiedAt: date, + localDateTime: date, + deletedAt: null, + }); + await assetRepo.create(asset); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + id: asset.id, + ownerId: asset.ownerId, + thumbhash, + checksum, + deletedAt: null, + fileCreatedAt: date, + fileModifiedAt: date, + isFavorite: false, + localDateTime: date, + type: asset.type, + visibility: asset.visibility, + }, + type: SyncEntityType.PartnerAssetV1, + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a deleted partner asset', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + const asset = mediumFactory.assetInsert({ ownerId: user2.id }); + + const assetRepo = getRepository('asset'); + await assetRepo.create(asset); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + await assetRepo.remove(asset); + + const response = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); + + expect(response).toHaveLength(1); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + }, + type: SyncEntityType.PartnerAssetDeleteV1, + }, + ]), + ); + + const acks = response.map(({ ack }) => ack); + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should not sync a deleted partner asset due to a user delete', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + const assetRepo = getRepository('asset'); + await assetRepo.create(mediumFactory.assetInsert({ ownerId: user2.id })); + + await userRepo.delete({ id: user2.id }, true); + + const response = await testSync(auth, [SyncRequestType.PartnerAssetsV1]); + expect(response).toHaveLength(0); + }); + + it('should not sync a deleted partner asset due to a partner delete (unshare)', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const assetRepo = getRepository('asset'); + await assetRepo.create(mediumFactory.assetInsert({ ownerId: user2.id })); + + const partnerRepo = getRepository('partner'); + const partner = { sharedById: user2.id, sharedWithId: auth.user.id }; + await partnerRepo.create(partner); + + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(1); + + await partnerRepo.remove(partner); + + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + }); + + it('should not sync an asset or asset delete for own user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: auth.user.id }); + await assetRepo.create(asset); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + + await expect(testSync(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + + await assetRepo.remove(asset); + + await expect(testSync(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + }); + + it('should not sync an asset or asset delete for unrelated user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const sessionRepo = getRepository('session'); + const session = mediumFactory.sessionInsert({ userId: user2.id }); + await sessionRepo.create(session); + + const auth2 = factory.auth({ session, user: user2 }); + + const assetRepo = getRepository('asset'); + const asset = mediumFactory.assetInsert({ ownerId: user2.id }); + await assetRepo.create(asset); + + await expect(testSync(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + + await assetRepo.remove(asset); + + await expect(testSync(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); + await expect(testSync(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-partner.spec.ts b/server/test/medium/specs/sync/sync-partner.spec.ts new file mode 100644 index 0000000000..f262eec853 --- /dev/null +++ b/server/test/medium/specs/sync/sync-partner.spec.ts @@ -0,0 +1,221 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncEntityType.PartnerV1, () => { + it('should detect and sync the first partner', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const user1 = auth.user; + const userRepo = getRepository('user'); + const partnerRepo = getRepository('partner'); + + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + inTimeline: partner.inTimeline, + sharedById: partner.sharedById, + sharedWithId: partner.sharedWithId, + }, + type: 'PartnerV1', + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a deleted partner', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user1 = auth.user; + const user2 = mediumFactory.userInsert(); + await userRepo.create(user2); + + const partnerRepo = getRepository('partner'); + const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); + await partnerRepo.remove(partner); + + const response = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(response).toHaveLength(1); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + sharedById: partner.sharedById, + sharedWithId: partner.sharedWithId, + }, + type: 'PartnerDeleteV1', + }, + ]), + ); + + const acks = response.map(({ ack }) => ack); + await sut.setAcks(auth, { acks }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a partner share both to and from another user', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user1 = auth.user; + const user2 = await userRepo.create(mediumFactory.userInsert()); + + const partnerRepo = getRepository('partner'); + const partner1 = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); + const partner2 = await partnerRepo.create({ sharedById: user1.id, sharedWithId: user2.id }); + + const response = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(response).toHaveLength(2); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + inTimeline: partner1.inTimeline, + sharedById: partner1.sharedById, + sharedWithId: partner1.sharedWithId, + }, + type: 'PartnerV1', + }, + { + ack: expect.any(String), + data: { + inTimeline: partner2.inTimeline, + sharedById: partner2.sharedById, + sharedWithId: partner2.sharedWithId, + }, + type: 'PartnerV1', + }, + ]), + ); + + await sut.setAcks(auth, { acks: [response[1].ack] }); + + const ackSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should sync a partner and then an update to that same partner', async () => { + const { auth, sut, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user1 = auth.user; + const user2 = await userRepo.create(mediumFactory.userInsert()); + + const partnerRepo = getRepository('partner'); + const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user1.id }); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + inTimeline: partner.inTimeline, + sharedById: partner.sharedById, + sharedWithId: partner.sharedWithId, + }, + type: 'PartnerV1', + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const updated = await partnerRepo.update( + { sharedById: partner.sharedById, sharedWithId: partner.sharedWithId }, + { inTimeline: true }, + ); + + const updatedSyncResponse = await testSync(auth, [SyncRequestType.PartnersV1]); + + expect(updatedSyncResponse).toHaveLength(1); + expect(updatedSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + inTimeline: updated.inTimeline, + sharedById: updated.sharedById, + sharedWithId: updated.sharedWithId, + }, + type: 'PartnerV1', + }, + ]), + ); + }); + + it('should not sync a partner or partner delete for an unrelated user', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = await userRepo.create(mediumFactory.userInsert()); + const user3 = await userRepo.create(mediumFactory.userInsert()); + + const partnerRepo = getRepository('partner'); + const partner = await partnerRepo.create({ sharedById: user2.id, sharedWithId: user3.id }); + + expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); + + await partnerRepo.remove(partner); + + expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); + }); + + it('should not sync a partner delete after a user is deleted', async () => { + const { auth, getRepository, testSync } = await setup(); + + const userRepo = getRepository('user'); + const user2 = await userRepo.create(mediumFactory.userInsert()); + + const partnerRepo = getRepository('partner'); + await partnerRepo.create({ sharedById: user2.id, sharedWithId: auth.user.id }); + await userRepo.delete({ id: user2.id }, true); + + expect(await testSync(auth, [SyncRequestType.PartnersV1])).toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-types.spec.ts b/server/test/medium/specs/sync/sync-types.spec.ts new file mode 100644 index 0000000000..1af5a68fd6 --- /dev/null +++ b/server/test/medium/specs/sync/sync-types.spec.ts @@ -0,0 +1,12 @@ +import { SyncRequestType } from 'src/enum'; +import { SYNC_TYPES_ORDER } from 'src/services/sync.service'; + +describe('types', () => { + it('should have all the types in the ordering variable', () => { + for (const key in SyncRequestType) { + expect(SYNC_TYPES_ORDER).includes(key); + } + + expect(SYNC_TYPES_ORDER.length).toBe(Object.keys(SyncRequestType).length); + }); +}); diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts new file mode 100644 index 0000000000..2cea38267c --- /dev/null +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -0,0 +1,179 @@ +import { Kysely } from 'kysely'; +import { DB } from 'src/db'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { mediumFactory, newSyncAuthUser, newSyncTest } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const database = db || defaultDatabase; + const result = newSyncTest({ db: database }); + const { auth, create } = newSyncAuthUser(); + await create(database); + return { ...result, auth }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe.concurrent(SyncEntityType.UserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, sut, getRepository, testSync } = await setup(await getKyselyDB()); + + const userRepo = getRepository('user'); + const user = await userRepo.get(auth.user.id, { withDeleted: false }); + if (!user) { + expect.fail('First user should exist'); + } + + const initialSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual([ + { + ack: expect.any(String), + data: { + deletedAt: user.deletedAt, + email: user.email, + id: user.id, + name: user.name, + }, + type: 'UserV1', + }, + ]); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a soft deleted user', async () => { + const { auth, sut, getRepository, testSync } = await setup(await getKyselyDB()); + + const deletedAt = new Date().toISOString(); + const deletedUser = mediumFactory.userInsert({ deletedAt }); + const deleted = await getRepository('user').create(deletedUser); + + const response = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(response).toHaveLength(2); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + deletedAt: null, + email: auth.user.email, + id: auth.user.id, + name: auth.user.name, + }, + type: 'UserV1', + }, + { + ack: expect.any(String), + data: { + deletedAt, + email: deleted.email, + id: deleted.id, + name: deleted.name, + }, + type: 'UserV1', + }, + ]), + ); + + const acks = [response[1].ack]; + await sut.setAcks(auth, { acks }); + const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should detect and sync a deleted user', async () => { + const { auth, sut, getRepository, testSync } = await setup(await getKyselyDB()); + + const userRepo = getRepository('user'); + const user = mediumFactory.userInsert(); + await userRepo.create(user); + await userRepo.delete({ id: user.id }, true); + + const response = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(response).toHaveLength(2); + expect(response).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + userId: user.id, + }, + type: 'UserDeleteV1', + }, + { + ack: expect.any(String), + data: { + deletedAt: null, + email: auth.user.email, + id: auth.user.id, + name: auth.user.name, + }, + type: 'UserV1', + }, + ]), + ); + + const acks = response.map(({ ack }) => ack); + await sut.setAcks(auth, { acks }); + const ackSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(ackSyncResponse).toHaveLength(0); + }); + + it('should sync a user and then an update to that same user', async () => { + const { auth, sut, getRepository, testSync } = await setup(await getKyselyDB()); + + const initialSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(initialSyncResponse).toHaveLength(1); + expect(initialSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + deletedAt: null, + email: auth.user.email, + id: auth.user.id, + name: auth.user.name, + }, + type: 'UserV1', + }, + ]), + ); + + const acks = [initialSyncResponse[0].ack]; + await sut.setAcks(auth, { acks }); + + const userRepo = getRepository('user'); + const updated = await userRepo.update(auth.user.id, { name: 'new name' }); + const updatedSyncResponse = await testSync(auth, [SyncRequestType.UsersV1]); + + expect(updatedSyncResponse).toHaveLength(1); + expect(updatedSyncResponse).toEqual( + expect.arrayContaining([ + { + ack: expect.any(String), + data: { + deletedAt: null, + email: auth.user.email, + id: auth.user.id, + name: updated.name, + }, + type: 'UserV1', + }, + ]), + ); + }); +}); diff --git a/server/test/medium/utils.ts b/server/test/medium/utils.ts deleted file mode 100644 index 030780b35b..0000000000 --- a/server/test/medium/utils.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Provider } from '@nestjs/common'; -import { SchedulerRegistry } from '@nestjs/schedule'; -import { Test } from '@nestjs/testing'; -import { ClassConstructor } from 'class-transformer'; -import { ClsService } from 'nestjs-cls'; -import { middleware } from 'src/app.module'; -import { controllers } from 'src/controllers'; -import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; -import { LoggingRepository } from 'src/repositories/logging.repository'; -import { services } from 'src/services'; -import { ApiService } from 'src/services/api.service'; -import { AuthService } from 'src/services/auth.service'; -import { BaseService } from 'src/services/base.service'; -import { automock } from 'test/utils'; -import { Mocked } from 'vitest'; - -export const createControllerTestApp = async (options?: { authType?: 'mock' | 'real' }) => { - const { authType = 'mock' } = options || {}; - - const configMock = { getEnv: () => ({ noColor: true }) }; - const clsMock = { getId: vitest.fn().mockReturnValue('cls-id') }; - const loggerMock = automock(LoggingRepository, { args: [clsMock, configMock], strict: false }); - loggerMock.setContext.mockReturnValue(void 0); - loggerMock.error.mockImplementation((...args: any[]) => { - console.log('Logger.error was called with', ...args); - }); - - const mockBaseService = (service: ClassConstructor) => { - return automock(service, { args: [loggerMock], strict: false }); - }; - - const clsServiceMock = clsMock; - - const FAKE_MOCK = vitest.fn(); - - const providers: Provider[] = [ - ...middleware, - ...services.map((Service) => { - if ((authType === 'real' && Service === AuthService) || Service === ApiService) { - return Service; - } - return { provide: Service, useValue: mockBaseService(Service as ClassConstructor) }; - }), - GlobalExceptionFilter, - { provide: LoggingRepository, useValue: loggerMock }, - { provide: ClsService, useValue: clsServiceMock }, - ]; - - const moduleRef = await Test.createTestingModule({ - imports: [], - controllers: [...controllers], - providers, - }) - .useMocker((token) => { - if (token === LoggingRepository) { - return; - } - - if (token === SchedulerRegistry) { - return FAKE_MOCK; - } - - if (typeof token === 'function' && token.name.endsWith('Repository')) { - return FAKE_MOCK; - } - - if (typeof token === 'string' && token === 'KyselyModuleConnectionToken') { - return FAKE_MOCK; - } - }) - - .compile(); - - const app = moduleRef.createNestApplication(); - - await app.init(); - - const getMockedRepository = (token: ClassConstructor) => { - return app.get(token) as Mocked; - }; - - return { - getHttpServer: () => app.getHttpServer(), - getMockedService: (token: ClassConstructor) => { - if (authType === 'real' && token === AuthService) { - throw new Error('Auth type is real, cannot get mocked service'); - } - return app.get(token) as Mocked; - }, - getMockedRepository, - close: () => app.close(), - }; -}; - -export type TestControllerApp = { - getHttpServer: () => any; - getMockedService: (token: ClassConstructor) => Mocked; - getMockedRepository: (token: ClassConstructor) => Mocked; - close: () => Promise; -}; diff --git a/server/test/repositories/access.repository.mock.ts b/server/test/repositories/access.repository.mock.ts index ec5115b839..50db983cba 100644 --- a/server/test/repositories/access.repository.mock.ts +++ b/server/test/repositories/access.repository.mock.ts @@ -37,6 +37,10 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => { checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, + notification: { + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + }, + person: { checkFaceOwnerAccess: vitest.fn().mockResolvedValue(new Set()), checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), @@ -46,6 +50,10 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => { checkUpdateAccess: vitest.fn().mockResolvedValue(new Set()), }, + session: { + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + }, + stack: { checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 2418b6aa64..d8230a23f3 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -11,16 +11,13 @@ export const newAssetRepositoryMock = (): Mocked true), hashSha1: vitest.fn().mockImplementation((input) => Buffer.from(`${input.toString()} (hashed)`)), hashFile: vitest.fn().mockImplementation((input) => `${input} (file-hashed)`), - newPassword: vitest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')), + randomBytesAsText: vitest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')), }; }; diff --git a/server/test/repositories/database.repository.mock.ts b/server/test/repositories/database.repository.mock.ts index eeedf682de..abdde53e9d 100644 --- a/server/test/repositories/database.repository.mock.ts +++ b/server/test/repositories/database.repository.mock.ts @@ -5,14 +5,19 @@ import { Mocked, vitest } from 'vitest'; export const newDatabaseRepositoryMock = (): Mocked> => { return { shutdown: vitest.fn(), - getExtensionVersion: vitest.fn(), + getExtensionVersions: vitest.fn(), + getVectorExtension: vitest.fn(), getExtensionVersionRange: vitest.fn(), getPostgresVersion: vitest.fn().mockResolvedValue('14.10 (Debian 14.10-1.pgdg120+1)'), getPostgresVersionRange: vitest.fn().mockReturnValue('>=14.0.0'), createExtension: vitest.fn().mockResolvedValue(void 0), + dropExtension: vitest.fn(), updateVectorExtension: vitest.fn(), - reindex: vitest.fn(), - shouldReindex: vitest.fn(), + reindexVectorsIfNeeded: vitest.fn(), + getDimensionSize: vitest.fn(), + setDimensionSize: vitest.fn(), + deleteAllSearchEmbeddings: vitest.fn(), + prewarm: vitest.fn(), runMigrations: vitest.fn(), withLock: vitest.fn().mockImplementation((_, function_: () => Promise) => function_()), tryLock: vitest.fn(), diff --git a/server/test/repositories/media.repository.mock.ts b/server/test/repositories/media.repository.mock.ts index e9f624d6bf..c6ab11aaa1 100644 --- a/server/test/repositories/media.repository.mock.ts +++ b/server/test/repositories/media.repository.mock.ts @@ -8,7 +8,7 @@ export const newMediaRepositoryMock = (): Mocked Promise.resolve()), generateThumbhash: vitest.fn().mockResolvedValue(Buffer.from('')), decodeImage: vitest.fn().mockResolvedValue({ data: Buffer.from(''), info: {} }), - extract: vitest.fn().mockResolvedValue(false), + extract: vitest.fn().mockResolvedValue(null), probe: vitest.fn(), transcode: vitest.fn(), getImageDimensions: vitest.fn(), diff --git a/server/test/repositories/person.repository.mock.ts b/server/test/repositories/person.repository.mock.ts index 59377576b1..2875c9ada5 100644 --- a/server/test/repositories/person.repository.mock.ts +++ b/server/test/repositories/person.repository.mock.ts @@ -33,5 +33,6 @@ export const newPersonRepositoryMock = (): Mocked randomUUID() as string; @@ -58,7 +58,7 @@ const authFactory = ({ } if (session) { - auth.session = { id: session.id }; + auth.session = { id: session.id, hasElevatedPermission: false }; } if (sharedLink) { @@ -126,7 +126,10 @@ const sessionFactory = (session: Partial = {}) => ({ deviceOS: 'android', deviceType: 'mobile', token: 'abc123', + parentId: null, + expiresAt: null, userId: newUuid(), + pinExpiresAt: newDate(), ...session, }); @@ -140,6 +143,7 @@ const userFactory = (user: Partial = {}) => ({ id: newUuid(), name: 'Test User', email: 'test@immich.cloud', + avatarColor: null, profileImagePath: '', profileChangedAt: newDate(), ...user, @@ -155,6 +159,7 @@ const userAdminFactory = (user: Partial = {}) => { storageLabel = null, shouldChangePassword = false, isAdmin = false, + avatarColor = null, createdAt = newDate(), updatedAt = newDate(), deletedAt = null, @@ -173,6 +178,7 @@ const userAdminFactory = (user: Partial = {}) => { storageLabel, shouldChangePassword, isAdmin, + avatarColor, createdAt, updatedAt, deletedAt, @@ -184,7 +190,7 @@ const userAdminFactory = (user: Partial = {}) => { }; }; -const assetFactory = (asset: Partial = {}) => ({ +const assetFactory = (asset: Partial = {}) => ({ id: newUuid(), createdAt: newDate(), updatedAt: newDate(), @@ -199,11 +205,9 @@ const assetFactory = (asset: Partial = {}) => ({ encodedVideoPath: null, fileCreatedAt: newDate(), fileModifiedAt: newDate(), - isArchived: false, isExternal: false, isFavorite: false, isOffline: false, - isVisible: true, libraryId: null, livePhotoVideoId: null, localDateTime: newDate(), @@ -214,6 +218,7 @@ const assetFactory = (asset: Partial = {}) => ({ stackId: null, thumbhash: null, type: AssetType.IMAGE, + visibility: AssetVisibility.TIMELINE, ...asset, }); @@ -311,4 +316,12 @@ export const factory = { sidecarWrite: assetSidecarWriteFactory, }, uuid: newUuid, + date: newDate, + responses: { + badRequest: (message: any = null) => ({ + error: 'Bad Request', + statusCode: 400, + message: message ?? expect.anything(), + }), + }, }; diff --git a/server/test/sql-tools/column-index-name-default.ts b/server/test/sql-tools/column-index-name-default.ts index e8b36ec119..cedae006be 100644 --- a/server/test/sql-tools/column-index-name-default.ts +++ b/server/test/sql-tools/column-index-name-default.ts @@ -1,9 +1,8 @@ -import { Column, ColumnIndex, DatabaseSchema, Table } from 'src/sql-tools'; +import { Column, DatabaseSchema, Table } from 'src/sql-tools'; @Table() export class Table1 { - @ColumnIndex() - @Column() + @Column({ index: true }) column1!: string; } diff --git a/server/test/sql-tools/column-index-name.ts b/server/test/sql-tools/column-index-name.ts new file mode 100644 index 0000000000..8ba18a8851 --- /dev/null +++ b/server/test/sql-tools/column-index-name.ts @@ -0,0 +1,46 @@ +import { Column, DatabaseSchema, Table } from 'src/sql-tools'; + +@Table() +export class Table1 { + @Column({ indexName: 'IDX_test' }) + column1!: string; +} + +export const description = 'should create a column with an index if a name is provided'; +export const schema: DatabaseSchema = { + name: 'postgres', + schemaName: 'public', + functions: [], + enums: [], + extensions: [], + parameters: [], + tables: [ + { + name: 'table1', + columns: [ + { + name: 'column1', + tableName: 'table1', + type: 'character varying', + nullable: false, + isArray: false, + primary: false, + synchronize: true, + }, + ], + indexes: [ + { + name: 'IDX_test', + columnNames: ['column1'], + tableName: 'table1', + unique: false, + synchronize: true, + }, + ], + triggers: [], + constraints: [], + synchronize: true, + }, + ], + warnings: [], +}; diff --git a/server/test/sql-tools/foreign-key-inferred-type.stub.ts b/server/test/sql-tools/foreign-key-inferred-type.stub.ts index 2ecaafdcad..0b66a1acd4 100644 --- a/server/test/sql-tools/foreign-key-inferred-type.stub.ts +++ b/server/test/sql-tools/foreign-key-inferred-type.stub.ts @@ -60,7 +60,15 @@ export const schema: DatabaseSchema = { synchronize: true, }, ], - indexes: [], + indexes: [ + { + name: 'IDX_3fcca5cc563abf256fc346e3ff', + tableName: 'table2', + columnNames: ['parentId'], + unique: false, + synchronize: true, + }, + ], triggers: [], constraints: [ { diff --git a/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts b/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts index 0601a02d42..109a3dfc85 100644 --- a/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts +++ b/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts @@ -60,7 +60,15 @@ export const schema: DatabaseSchema = { synchronize: true, }, ], - indexes: [], + indexes: [ + { + name: 'IDX_3fcca5cc563abf256fc346e3ff', + tableName: 'table2', + columnNames: ['parentId'], + unique: false, + synchronize: true, + }, + ], triggers: [], constraints: [ { diff --git a/server/test/sql-tools/index-with-where.stub copy.ts b/server/test/sql-tools/index-with-expression.ts similarity index 100% rename from server/test/sql-tools/index-with-where.stub copy.ts rename to server/test/sql-tools/index-with-expression.ts diff --git a/server/test/utils.ts b/server/test/utils.ts index 52984d97a2..c1459bac84 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -1,10 +1,16 @@ +import { CallHandler, Provider, ValidationPipe } from '@nestjs/common'; +import { APP_GUARD, APP_PIPE } from '@nestjs/core'; +import { Test } from '@nestjs/testing'; import { ClassConstructor } from 'class-transformer'; -import { Kysely, sql } from 'kysely'; +import { Kysely } from 'kysely'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; import { Writable } from 'node:stream'; -import { parse } from 'pg-connection-string'; import { PNG } from 'pngjs'; +import postgres from 'postgres'; import { DB } from 'src/db'; +import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; +import { AuthGuard } from 'src/middleware/auth.guard'; +import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; @@ -18,6 +24,7 @@ import { CronRepository } from 'src/repositories/cron.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; import { DownloadRepository } from 'src/repositories/download.repository'; +import { EmailRepository } from 'src/repositories/email.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -47,9 +54,10 @@ import { TrashRepository } from 'src/repositories/trash.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; +import { AuthService } from 'src/services/auth.service'; import { BaseService } from 'src/services/base.service'; import { RepositoryInterface } from 'src/types'; -import { getKyselyConfig } from 'src/utils/database'; +import { asPostgresConnectionConfig, getKyselyConfig } from 'src/utils/database'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; @@ -63,30 +71,82 @@ import { newStorageRepositoryMock } from 'test/repositories/storage.repository.m import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { ITelemetryRepositoryMock, newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { Readable } from 'typeorm/platform/PlatformTools'; -import { assert, Mocked, vitest } from 'vitest'; +import { assert, Mock, Mocked, vitest } from 'vitest'; + +export type ControllerContext = { + authenticate: Mock; + getHttpServer: () => any; + reset: () => void; + close: () => Promise; +}; + +export const controllerSetup = async (controller: ClassConstructor, providers: Provider[]) => { + const noopInterceptor = { intercept: (ctx: never, next: CallHandler) => next.handle() }; + const moduleRef = await Test.createTestingModule({ + controllers: [controller], + providers: [ + { provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) }, + { provide: APP_GUARD, useClass: AuthGuard }, + { provide: LoggingRepository, useValue: LoggingRepository.create() }, + { provide: AuthService, useValue: { authenticate: vi.fn() } }, + ...providers, + ], + }) + .overrideInterceptor(FileUploadInterceptor) + .useValue(noopInterceptor) + .overrideInterceptor(AssetUploadInterceptor) + .useValue(noopInterceptor) + .compile(); + const app = moduleRef.createNestApplication(); + await app.init(); + + // allow the AuthController to override the AuthService itself + const authenticate = app.get>(AuthService).authenticate as Mock; + + return { + authenticate, + getHttpServer: () => app.getHttpServer(), + reset: () => { + authenticate.mockReset(); + }, + close: async () => { + await app.close(); + }, + }; +}; + +export type AutoMocked = Mocked & { resetAllMocks: () => void }; const mockFn = (label: string, { strict }: { strict: boolean }) => { const message = `Called a mock function without a mock implementation (${label})`; - return vitest.fn().mockImplementation(() => { - if (strict) { - assert.fail(message); - } else { - // console.warn(message); + return vitest.fn(() => { + { + if (strict) { + assert.fail(message); + } else { + // console.warn(message); + } } }); }; +export const mockBaseService = (service: ClassConstructor) => { + return automock(service, { args: [{ setContext: () => {} }], strict: false }); +}; + export const automock = ( Dependency: ClassConstructor, options?: { args?: ConstructorParameters>; strict?: boolean; }, -): Mocked => { +): AutoMocked => { const mock: Record = {}; const strict = options?.strict ?? true; const args = options?.args ?? []; + const mocks: Mock[] = []; + const instance = new Dependency(...args); for (const property of Object.getOwnPropertyNames(Dependency.prototype)) { if (property === 'constructor') { @@ -99,7 +159,9 @@ export const automock = ( const target = instance[property as keyof T]; if (typeof target === 'function') { - mock[property] = mockFn(label, { strict }); + const mockImplementation = mockFn(label, { strict }); + mock[property] = mockImplementation; + mocks.push(mockImplementation); continue; } } catch { @@ -107,7 +169,14 @@ export const automock = ( } } - return mock as Mocked; + const result = mock as AutoMocked; + result.resetAllMocks = () => { + for (const mock of mocks) { + mock.mockReset(); + } + }; + + return result; }; export type ServiceOverrides = { @@ -124,6 +193,7 @@ export type ServiceOverrides = { crypto: CryptoRepository; database: DatabaseRepository; downloadRepository: DownloadRepository; + email: EmailRepository; event: EventRepository; job: JobRepository; library: LibraryRepository; @@ -190,6 +260,7 @@ export const newTestService = ( config: newConfigRepositoryMock(), database: newDatabaseRepositoryMock(), downloadRepository: automock(DownloadRepository, { strict: false }), + email: automock(EmailRepository, { args: [loggerMock] }), // eslint-disable-next-line no-sparse-arrays event: automock(EventRepository, { args: [, , loggerMock], strict: false }), job: newJobRepositoryMock(), @@ -201,7 +272,7 @@ export const newTestService = ( memory: automock(MemoryRepository), metadata: newMetadataRepositoryMock(), move: automock(MoveRepository, { strict: false }), - notification: automock(NotificationRepository, { args: [loggerMock] }), + notification: automock(NotificationRepository), oauth: automock(OAuthRepository, { args: [loggerMock] }), partner: automock(PartnerRepository, { strict: false }), person: newPersonRepositoryMock(), @@ -240,6 +311,7 @@ export const newTestService = ( overrides.crypto || (mocks.crypto as As), overrides.database || (mocks.database as As), overrides.downloadRepository || (mocks.downloadRepository as As), + overrides.email || (mocks.email as As), overrides.event || (mocks.event as As), overrides.job || (mocks.job as As), overrides.library || (mocks.library as As), @@ -297,24 +369,20 @@ function* newPngFactory() { const pngFactory = newPngFactory(); +const withDatabase = (url: string, name: string) => url.replace('/immich', `/${name}`); + export const getKyselyDB = async (suffix?: string): Promise> => { - const parsed = parse(process.env.IMMICH_TEST_POSTGRES_URL!); + const testUrl = process.env.IMMICH_TEST_POSTGRES_URL!; + const sql = postgres({ + ...asPostgresConnectionConfig({ connectionType: 'url', url: withDatabase(testUrl, 'postgres') }), + max: 1, + }); - const parsedOptions = { - ...parsed, - ssl: false, - host: parsed.host ?? undefined, - port: parsed.port ? Number(parsed.port) : undefined, - database: parsed.database ?? undefined, - }; - - const kysely = new Kysely(getKyselyConfig({ ...parsedOptions, max: 1, database: 'postgres' })); const randomSuffix = Math.random().toString(36).slice(2, 7); const dbName = `immich_${suffix ?? randomSuffix}`; + await sql.unsafe(`CREATE DATABASE ${dbName} WITH TEMPLATE immich OWNER postgres;`); - await sql.raw(`CREATE DATABASE ${dbName} WITH TEMPLATE immich OWNER postgres;`).execute(kysely); - - return new Kysely(getKyselyConfig({ ...parsedOptions, database: dbName })); + return new Kysely(getKyselyConfig({ connectionType: 'url', url: withDatabase(testUrl, dbName) })); }; export const newRandomImage = () => { diff --git a/server/test/vitest.config.mjs b/server/test/vitest.config.mjs index a6929bf806..a22a6751c3 100644 --- a/server/test/vitest.config.mjs +++ b/server/test/vitest.config.mjs @@ -20,12 +20,6 @@ export default defineConfig({ 'src/services/index.ts', 'src/sql-tools/from-database/index.ts', ], - thresholds: { - lines: 85, - statements: 85, - branches: 90, - functions: 85, - }, }, server: { deps: { diff --git a/typescript-open-api/typescript-sdk/package-lock.json b/typescript-open-api/typescript-sdk/package-lock.json new file mode 100644 index 0000000000..ca6fc5e1de --- /dev/null +++ b/typescript-open-api/typescript-sdk/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "typescript-sdk", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/web/.nvmrc b/web/.nvmrc index 7d41c735d7..5b540673a8 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.14.0 +22.16.0 diff --git a/web/Dockerfile b/web/Dockerfile index 8c2e67e62e..1c6c4b46bf 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 +FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e RUN apk add --no-cache tini USER node diff --git a/web/bin/immich-web b/web/bin/immich-web index 6b2880d6d2..ea748863db 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -5,10 +5,17 @@ TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk npm --prefix "$TYPESCRIPT_SDK" install npm --prefix "$TYPESCRIPT_SDK" run build + +COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" -until wget --spider --quiet "${UPSTREAM}/api/server/config"; do - echo 'waiting for api server...' +until wget --spider --quiet "${UPSTREAM}/api/server/config" > /dev/null 2>&1; do + if [ $((COUNT % 10)) -eq 0 ]; then + echo "Waiting for $UPSTREAM to start..." + fi + COUNT=$((COUNT + 1)) sleep 1 done +echo "Connected to $UPSTREAM" + node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000 diff --git a/web/eslint.config.js b/web/eslint.config.js index 5c24cd1aeb..9ced619504 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -58,6 +58,8 @@ export default typescriptEslint.config( }, }, + ignores: ['**/service-worker/**'], + rules: { '@typescript-eslint/no-unused-vars': [ 'warn', diff --git a/web/package-lock.json b/web/package-lock.json index 57af7fa56d..a631c514ff 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,17 +1,17 @@ { "name": "immich-web", - "version": "1.131.3", + "version": "1.134.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.131.3", + "version": "1.134.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.17.3", + "@immich/ui": "^0.22.4", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -35,6 +35,7 @@ "svelte-i18n": "^4.0.1", "svelte-maplibre": "^1.0.0", "svelte-persisted-store": "^0.12.0", + "tabbable": "^6.2.0", "thumbhash": "^0.1.1" }, "devDependencies": { @@ -43,12 +44,15 @@ "@faker-js/faker": "^9.3.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.4.4", + "@sveltejs/enhanced-img": "^0.6.0", "@sveltejs/kit": "^2.15.2", "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@tailwindcss/postcss": "^4.1.7", + "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/svelte": "^5.2.6", + "@testing-library/svelte": "^5.2.8", "@testing-library/user-event": "^14.5.2", + "@types/chromecast-caf-sender": "^1.0.11", "@types/dom-to-image": "^2.6.7", "@types/justified-layout": "^4.1.4", "@types/lodash-es": "^4.17.12", @@ -59,7 +63,8 @@ "dotenv": "^16.4.7", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", - "eslint-plugin-svelte": "^3.0.0", + "eslint-p": "^0.23.0", + "eslint-plugin-svelte": "^3.9.0", "eslint-plugin-unicorn": "^57.0.0", "factory.ts": "^1.4.1", "globals": "^16.0.0", @@ -71,7 +76,7 @@ "rollup-plugin-visualizer": "^5.14.0", "svelte": "^5.25.3", "svelte-check": "^4.1.5", - "tailwindcss": "^3.4.17", + "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", @@ -81,13 +86,13 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.131.3", + "version": "1.134.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.14.0", + "@types/node": "^22.15.21", "typescript": "^5.3.3" } }, @@ -102,6 +107,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -212,9 +218,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.2.tgz", - "integrity": "sha512-+b+3BJl18a0LKeHvy5eLOwPkiaz10C2MUUYKQ25itZS50TlP5FuDh2Q5EiFlB++vAuCS6HnrihqVlbdcRYyp9w==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", "dev": true, "license": "MIT", "optional": true, @@ -495,9 +501,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", "cpu": [ "arm64" ], @@ -528,9 +534,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -625,9 +631,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -692,9 +698,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -742,13 +748,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -762,36 +771,23 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@faker-js/faker": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.7.0.tgz", - "integrity": "sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.8.0.tgz", + "integrity": "sha512-U9wpuSrJC93jZBxx/Qq2wPjCuYISBueyVUGK7qqdmj7r/nxaxwW8AQDCLeRO7wZnjj94sh3p246cAYjUKuqgfg==", "dev": true, "funding": [ { @@ -806,21 +802,21 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, @@ -948,9 +944,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", + "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", "cpu": [ "arm64" ], @@ -967,13 +963,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "@img/sharp-libvips-darwin-arm64": "1.1.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", + "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", "cpu": [ "x64" ], @@ -990,13 +986,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "@img/sharp-libvips-darwin-x64": "1.1.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", "cpu": [ "arm64" ], @@ -1011,9 +1007,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", "cpu": [ "x64" ], @@ -1028,9 +1024,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", "cpu": [ "arm" ], @@ -1045,9 +1041,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", "cpu": [ "arm64" ], @@ -1061,10 +1057,27 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", "cpu": [ "s390x" ], @@ -1079,9 +1092,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", "cpu": [ "x64" ], @@ -1096,9 +1109,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", "cpu": [ "arm64" ], @@ -1113,9 +1126,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", "cpu": [ "x64" ], @@ -1130,9 +1143,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", + "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", "cpu": [ "arm" ], @@ -1149,13 +1162,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "@img/sharp-libvips-linux-arm": "1.1.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", + "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", "cpu": [ "arm64" ], @@ -1172,13 +1185,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "@img/sharp-libvips-linux-arm64": "1.1.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", + "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", "cpu": [ "s390x" ], @@ -1195,13 +1208,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "@img/sharp-libvips-linux-s390x": "1.1.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", + "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", "cpu": [ "x64" ], @@ -1218,13 +1231,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "@img/sharp-libvips-linux-x64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", + "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", "cpu": [ "arm64" ], @@ -1241,13 +1254,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", + "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", "cpu": [ "x64" ], @@ -1264,13 +1277,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", + "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", "cpu": [ "wasm32" ], @@ -1278,7 +1291,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@emnapi/runtime": "^1.4.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1288,9 +1301,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", + "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", "cpu": [ "ia32" ], @@ -1308,9 +1321,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", + "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", "cpu": [ "x64" ], @@ -1332,24 +1345,24 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.17.4.tgz", - "integrity": "sha512-a6M7Fxno5fwY5A0kxdluS8r+A4L6xZhSTKMW8c8hoFhQHvbBTHAsGFKQF3GOEQLOlUuvsS2Lt7dMevBlAPgo/A==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.22.4.tgz", + "integrity": "sha512-l0H8G8XZ3YaP/pA8NsLhGsNZpTAwcOyEFmF88D5HZkK3nFTZOQFxvzcMfyOeMS6Nevv0CHdvJp3ns0zajfvNzw==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", - "bits-ui": "^1.0.0-next.46", + "bits-ui": "^1.5.3", "tailwind-merge": "^2.5.4", - "tailwind-variants": "^0.3.0" + "tailwind-variants": "^1.0.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "node_modules/@internationalized/date": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz", - "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.1.tgz", + "integrity": "sha512-PgVE6B6eIZtzf9Gu5HvJxRK3ufUFz9DhspELuhW/N0GuMGMTLvPQNRkHP2hTuP9lblOk+f+1xi96sPiPXANXAA==", "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" @@ -1359,6 +1372,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -1376,6 +1390,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1388,6 +1403,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1400,12 +1416,14 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -1423,6 +1441,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -1438,6 +1457,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -1451,6 +1471,29 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1616,9 +1659,9 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "23.1.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.1.0.tgz", - "integrity": "sha512-R6/ihEuC5KRexmKIYkWqUv84Gm+/QwsOUgHyt1yy2XqCdGdLvlBWVWIIeTZWN4NGdwmY6xDzdSGU2R9oBLNg2w==", + "version": "23.2.2", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.2.2.tgz", + "integrity": "sha512-kLcVlItPCULc20SM6pSVA7u8nST9xmQA8d7utc9j3KB0Tf/xhM4GgCn/QsZcmlbN/wW0ujyomDrvZ3/LbwvAmw==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", @@ -1673,6 +1716,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -1686,6 +1730,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -1695,6 +1740,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -1705,56 +1751,57 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.1.tgz", - "integrity": "sha512-f5fkoGPCBUt5BD9S9U37h+UDmo2slMZThesoH82iyjKR6uRyYnJvJXwopwo+iMfc6x1ZAWmustBBuES4qKzx+g==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.2.tgz", + "integrity": "sha512-rL4Ey39Prx4Iyxt1f2tAqlXvqu4/ovXfUvIpLt540OpZJiFjWccs6qLywof9vuhBJ7PXHudHWCjRPce0W8kx8w==", "license": "MIT", "dependencies": { "three": "^0.175.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.1.tgz", - "integrity": "sha512-5LCMMc1bnKMFvR//TKguSwyBEBF+fTYLOGnnCzS7HHHNr8jc+bmaxBsPhOENZf8VcoupXYxo4KRNfYwIB0nTEA==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.2.tgz", + "integrity": "sha512-Ln9VyZSGAEjqtJ5dYluiSYkUF87FsOwzZvQoEgAt4odQR/q7ktSaVDdRfuuTMcbBKq6kTdsavNzdg+g877WyhA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1", - "@photo-sphere-viewer/video-plugin": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2", + "@photo-sphere-viewer/video-plugin": "5.13.2" } }, "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.1.tgz", - "integrity": "sha512-XVxR5rAtGYbcy0PQfgGgMAuRAg5gb/tRjgMiB9zzQ6sESLviWCqvk247z4Q6J4TxNYeGSQzKbyous1eS+nUqTg==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.2.tgz", + "integrity": "sha512-T2bUvtKqhPk7FVqRJfynWhnglMpar5FNxCgf3EsnFjV9g+Xnc0LmOLlCeNmsCWXv0lRmNbohDMRN1WpY1O3ojA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1", - "@photo-sphere-viewer/settings-plugin": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2", + "@photo-sphere-viewer/settings-plugin": "5.13.2" } }, "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.1.tgz", - "integrity": "sha512-W2naZCP9huhN6cmFcGfgJEvxqrBB481/an8o/qice5iIH9xw50qXiDq6czLCtUo8GD4P9ULtsoWo9DUT2EsWzw==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.2.tgz", + "integrity": "sha512-z1539qy4XC9UextvgxFBBZqiNKQ1DzaI4EZRbrRbfG6LnSsjKwGgX8gIZ8ZpBHoZdU+b2d8PRPmNKiSjhYOvGA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.1.tgz", - "integrity": "sha512-GmjI4weoKRCOACNEIloL3XSAbYVyrO6s8esJfmpjdGKdDr3kQjKK+oiplRSD28ALIhSxyqemNOGodfgzWH7xLA==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.2.tgz", + "integrity": "sha512-6/tajOJaPUDP7mwtdQZul+KNfjL2sUPUt7EcAHZ9KcSq1WcwqZfaUYSCdKaW2uxdpn4BLESSD1h0mJdWNw7vJA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.1" + "@photo-sphere-viewer/core": "5.13.2" } }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -2097,35 +2144,37 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.4.4.tgz", - "integrity": "sha512-BlBTGfbLUgHa+zSVrsGLOd+noCKWfipoOjoxE26bAAX97v7zh5eiCAp1KEdpkluL05Tl3+nR14gQdPsATyZqoA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.6.0.tgz", + "integrity": "sha512-B9rHh6zHnFex6fWxD8rkmUsMvkAG+cZiv+5/NfXyLcDvDFqUQbcADACeioVFuUxNXDXAe3y+Ui3JVmekk8R/zg==", "dev": true, "license": "MIT", "dependencies": { "magic-string": "^0.30.5", - "sharp": "^0.33.5", + "sharp": "^0.34.1", "svelte-parse-markup": "^0.1.5", - "vite-imagetools": "^7.0.1", + "vite-imagetools": "^7.1.0", "zimmerframe": "^1.1.2" }, "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": ">= 5.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.20.7", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.7.tgz", - "integrity": "sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.21.1.tgz", + "integrity": "sha512-vLbtVwtDcK8LhJKnFkFYwM0uCdFmzioQnif0bjEYH1I24Arz22JPr/hLUiXGVYAwhu8INKx5qrdvr4tHgPwX6w==", "dev": true, "license": "MIT", "dependencies": { + "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", - "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", @@ -2194,6 +2243,434 @@ "tslib": "^2.8.0" } }, + "node_modules/@tailwindcss/node": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", + "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", + "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-x64": "4.1.7", + "@tailwindcss/oxide-freebsd-x64": "4.1.7", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-x64-musl": "4.1.7", + "@tailwindcss/oxide-wasm32-wasi": "4.1.7", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", + "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", + "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", + "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", + "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", + "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", + "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", + "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", + "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", + "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", + "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.9", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", + "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", + "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.7.tgz", + "integrity": "sha512-88g3qmNZn7jDgrrcp3ZXEQfp9CVox7xjP1HN2TFKI03CltPVd/c61ydn5qJJL8FYunn0OqBaW5HNUga0kmPVvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "postcss": "^8.4.41", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", + "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "tailwindcss": "4.1.7" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -2257,13 +2734,13 @@ "license": "MIT" }, "node_modules/@testing-library/svelte": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.2.7.tgz", - "integrity": "sha512-aGhUaFmEXEVost4QOsbHUUbHLwi7ZZRRxAHFDO2Cmr0BZD3/3+XvaYEPq70Rdw0NRNjdqZHdARBEcrCOkPuAqw==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.2.8.tgz", + "integrity": "sha512-ucQOtGsJhtawOEtUmbR4rRh53e6RbM1KUluJIXRmh6D4UzxR847iIqqjRtg9mHNFmGQ8Vkam9yVcR5d1mhIHKA==", "dev": true, "license": "MIT", "dependencies": { - "@testing-library/dom": "^10.0.0" + "@testing-library/dom": "9.x.x || 10.x.x" }, "engines": { "node": ">= 10" @@ -2313,6 +2790,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chrome": { + "version": "0.0.322", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.322.tgz", + "integrity": "sha512-glbRm82TzLLJfi3ttlnn7HR9KIX5OYeTo9Xug0Hna03JvaqNipZT+P/q/O5kxOvUQqKUqmn8NAOrcRSG6BOQAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/chromecast-caf-sender": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/chromecast-caf-sender/-/chromecast-caf-sender-1.0.11.tgz", + "integrity": "sha512-Pv3xvNYtxD/cTM/tKfuZRlLasvpxAm+CFni0GJd6Cp8XgiZS9g9tMZkR1uymsi5fIFv057SZKKAWVFFgy7fJtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chrome": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -2333,6 +2831,23 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", @@ -2348,6 +2863,13 @@ "@types/geojson": "*" } }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2455,21 +2977,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2484,17 +3006,27 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -2510,14 +3042,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2528,16 +3060,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2552,9 +3084,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", - "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "license": "MIT", "engines": { @@ -2566,20 +3098,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2619,16 +3151,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2643,13 +3175,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2661,9 +3193,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", - "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz", + "integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==", "dev": true, "license": "MIT", "dependencies": { @@ -2676,7 +3208,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -2684,8 +3216,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.1", - "vitest": "3.1.1" + "@vitest/browser": "3.1.4", + "vitest": "3.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2694,14 +3226,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", + "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -2710,13 +3242,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", + "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.1", + "@vitest/spy": "3.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -2747,9 +3279,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", + "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", "dev": true, "license": "MIT", "dependencies": { @@ -2760,13 +3292,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", + "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.1", + "@vitest/utils": "3.1.4", "pathe": "^2.0.3" }, "funding": { @@ -2774,13 +3306,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", + "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -2789,9 +3321,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", + "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -2802,13 +3334,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.1", + "@vitest/pretty-format": "3.1.4", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -2960,37 +3492,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -3013,12 +3514,6 @@ "node": ">=10" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3104,24 +3599,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true, "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bits-ui": { - "version": "1.3.19", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.3.19.tgz", - "integrity": "sha512-2blb6dkgedHUsDXqCjvmtUi4Advgd9MhaJDT8r7bEWDzHI8HGsOoYsLeh8CxpEWWEYPrlGN+7k+kpxRhIDdFrQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.5.3.tgz", + "integrity": "sha512-BTZ9/GU11DaEGyQp+AY+sXCMLZO0gbDC5J8l7+Ngj4Vf6hNOwrpMmoh5iuKktA6cphXYolVkUDgBWmkh415I+w==", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.4", @@ -3158,6 +3642,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3262,15 +3747,6 @@ "node": ">=6" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001713", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", @@ -3534,15 +4010,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3585,6 +4052,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3613,6 +4081,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -3809,9 +4278,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "devOptional": true, "license": "Apache-2.0", "engines": { @@ -3825,24 +4294,12 @@ "dev": true, "license": "MIT" }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -3909,6 +4366,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, "license": "MIT" }, "node_modules/electron-to-chromium": { @@ -3984,6 +4442,20 @@ "node": ">=10.0.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -4018,9 +4490,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -4189,20 +4661,20 @@ } }, "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4250,34 +4722,54 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, + "node_modules/eslint-p": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/eslint-p/-/eslint-p-0.23.1.tgz", + "integrity": "sha512-g1ZzYhoSsSmOEO9W2Elcnza0cjDdcU0wUJtpBHkg4WT3QRt0WO3QIBoBFcjMvWUfzajZCbyizg+LeWR7y2pUSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "eslint": "9.27.0" + }, + "bin": { + "eslint-p": "lib/eslint-p.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/eslint-plugin-svelte": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.5.1.tgz", - "integrity": "sha512-Qn1slddZHfqYiDO6IN8/iN3YL+VuHlgYjm30FT+hh0Jf/TX0jeZMTJXQMajFm5f6f6hURi+XO8P+NPYD+T4jkg==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.9.0.tgz", + "integrity": "sha512-nvIUNyyPGbr5922Kd1p/jXe+FfNdVPXsxLyrrXpwfSbZZEFdAYva9O/gm2lObC/wXkQo/AUmQkAihfmNJYeCjA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.1", + "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", - "known-css-properties": "^0.35.0", + "globals": "^16.0.0", + "known-css-properties": "^0.36.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", - "svelte-eslint-parser": "^1.1.1" + "svelte-eslint-parser": "^1.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4516,9 +5008,9 @@ } }, "node_modules/fabric": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.6.4.tgz", - "integrity": "sha512-GJ+9CsTo4oDGO6eEsSYaxgaZnndsiVr/pl8itfLkaBuxH4ek9+hxKfpjzrIaiSGzoZ8jVxUP8pFJaCronLxukA==", + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.6.5.tgz", + "integrity": "sha512-BFxyLDeLMMgtteqQwKAyRM+oSkf82lDFzsiC7AMob7k7ag7naFuHOtWtcll4v+M9Cpn5aqRBfz1shnsO0vZhbg==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -4553,6 +5045,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -4569,6 +5062,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -4595,15 +5089,16 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4638,6 +5133,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -4701,6 +5197,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -4717,6 +5214,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -4792,6 +5290,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4807,6 +5306,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", + "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4932,6 +5432,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -4979,9 +5480,9 @@ } }, "node_modules/globals": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", - "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "dev": true, "license": "MIT", "engines": { @@ -5016,6 +5517,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -5102,6 +5610,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "optional": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -5215,9 +5724,9 @@ } }, "node_modules/imagetools-core": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-7.0.2.tgz", - "integrity": "sha512-nrLdKLJHHXd8MitwlXK6/h1TSwGaH3X1DZ3z6yMv/tX7dJ12ecLxZ6P5jgKetfIFh8IJwH9fCWMoTA8ixg0VVA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-7.1.0.tgz", + "integrity": "sha512-8Aa4NecBBGmTkaAUjcuRYgTPKHCsBEWYmCnvKCL6/bxedehtVVFyZPdXe8DD0Nevd6UWBq85ifUaJ8498lgqNQ==", "dev": true, "license": "MIT", "engines": { @@ -5241,17 +5750,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5350,18 +5848,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-builtin-module": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz", @@ -5378,21 +5864,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -5413,6 +5884,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5431,6 +5903,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5443,6 +5916,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5500,6 +5974,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -5586,6 +6061,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -5598,12 +6074,13 @@ } }, "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, "license": "MIT", "bin": { - "jiti": "bin/jiti.js" + "jiti": "lib/jiti-cli.mjs" } }, "node_modules/js-tokens": { @@ -5760,9 +6237,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", - "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.36.0.tgz", + "integrity": "sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==", "dev": true, "license": "MIT" }, @@ -5780,6 +6257,245 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -5790,12 +6506,6 @@ "node": ">=10" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", @@ -5849,6 +6559,7 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/lru-queue": { @@ -5961,9 +6672,9 @@ } }, "node_modules/maplibre-gl": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.3.1.tgz", - "integrity": "sha512-Ihx+oUUSsZkjMou1Cw5J6silE+5OtFFQSPslWF9+7v4yFC/XDHrpsORYO9lWE4KZI0djCEUpZQJpkpnMArAbeA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.5.0.tgz", + "integrity": "sha512-p8AOPuzzqn1ZA9gcXxKw0IED715we/2Owa/YUr6PANmgMvNMe/JG+V/C1hRra43Wm62Biz+Aa8AgbOLJimA8tA==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", @@ -5973,7 +6684,7 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^23.1.0", + "@maplibre/maplibre-gl-style-spec": "^23.2.2", "@types/geojson": "^7946.0.16", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", @@ -6085,6 +6796,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -6094,6 +6806,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -6107,6 +6820,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -6187,6 +6901,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, "license": "ISC", "engines": { "node": ">=8" @@ -6263,17 +6978,6 @@ "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", "license": "MIT" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nan": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", @@ -6285,6 +6989,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -6402,15 +7107,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -6447,19 +7143,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", + "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6551,6 +7239,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { @@ -6620,21 +7309,17 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -6681,6 +7366,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -6696,24 +7382,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -6746,6 +7414,7 @@ "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6770,42 +7439,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, "node_modules/postcss-load-config": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", @@ -6846,44 +7479,6 @@ "node": ">= 6" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -6956,6 +7551,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, "license": "MIT" }, "node_modules/potpack": { @@ -7022,9 +7618,9 @@ } }, "node_modules/prettier-plugin-svelte": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz", - "integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz", + "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7117,6 +7713,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -7147,15 +7744,6 @@ "dev": true, "license": "MIT" }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, "node_modules/read-package-up": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", @@ -7325,26 +7913,6 @@ "license": "MIT", "optional": true }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -7368,6 +7936,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -7548,6 +8117,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -7681,16 +8251,16 @@ } }, "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", + "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", - "semver": "^7.6.3" + "semver": "^7.7.1" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -7699,31 +8269,33 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" + "@img/sharp-darwin-arm64": "0.34.1", + "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.1", + "@img/sharp-linux-arm64": "0.34.1", + "@img/sharp-linux-s390x": "0.34.1", + "@img/sharp-linux-x64": "0.34.1", + "@img/sharp-linuxmusl-arm64": "0.34.1", + "@img/sharp-linuxmusl-x64": "0.34.1", + "@img/sharp-wasm32": "0.34.1", + "@img/sharp-win32-ia32": "0.34.1", + "@img/sharp-win32-x64": "0.34.1" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -7736,6 +8308,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7888,6 +8461,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7983,6 +8557,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -8010,6 +8585,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -8056,81 +8632,6 @@ "inline-style-parser": "0.2.4" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/supercluster": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", @@ -8161,22 +8662,10 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/svelte": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.27.0.tgz", - "integrity": "sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==", + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.33.1.tgz", + "integrity": "sha512-7znzaaQALL62NBzkdKV04tmYIVla8qjrW+k6GdgFZcKcj8XOb8iEjmfRPo40iaWZlKv3+uiuc0h4iaGgwoORtA==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -8199,9 +8688,9 @@ } }, "node_modules/svelte-check": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.6.tgz", - "integrity": "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.2.1.tgz", + "integrity": "sha512-e49SU1RStvQhoipkQ/aonDhHnG3qxHSBtNfBRb9pxVXoa+N7qybAo32KgA9wEb2PCYFNaDg7bZCdhLD1vHpdYA==", "dev": true, "license": "MIT", "dependencies": { @@ -8223,9 +8712,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.2.tgz", - "integrity": "sha512-vqFBRamDKo1l70KMfxxXj1/0Cco5TfMDnqaAjgz6D8PyoMhfMcDOLRkAwPg8WkMyZjMtQL3wW66TZ0x59iqO2w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", + "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", "dev": true, "license": "MIT", "dependencies": { @@ -8252,9 +8741,9 @@ } }, "node_modules/svelte-gestures": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/svelte-gestures/-/svelte-gestures-5.1.3.tgz", - "integrity": "sha512-ELOlzuH9E4+S1biCCTfusRlvzFpnqRPlljEqayoBTu5STH42u0kTT45D1m3Py3E9UmIyZTgrSLw6Fus/fh75Dw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte-gestures/-/svelte-gestures-5.1.4.tgz", + "integrity": "sha512-gfSO/GqWLu9nRMCz12jqdyA0+NTsojYcIBcRqZjwWrpQbqMXr0zWPFpZBtzfYbRHtuFxZImMZp9MrVaFCYbhDg==", "license": "MIT" }, "node_modules/svelte-i18n": { @@ -8399,12 +8888,12 @@ } }, "node_modules/tailwind-variants": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.1.tgz", - "integrity": "sha512-krn67M3FpPwElg4FsZrOQd0U26o7UDH/QOkK8RNaiCCrr052f6YJPBUfNKnPo/s/xRzNPtv1Mldlxsg8Tb46BQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", + "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", "license": "MIT", "dependencies": { - "tailwind-merge": "2.5.4" + "tailwind-merge": "3.0.2" }, "engines": { "node": ">=16.x", @@ -8415,9 +8904,9 @@ } }, "node_modules/tailwind-variants/node_modules/tailwind-merge": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", - "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", + "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", "license": "MIT", "funding": { "type": "github", @@ -8425,160 +8914,19 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", + "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==", + "license": "MIT" }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/tailwindcss/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" + "node": ">=6" } }, "node_modules/tar": { @@ -8671,27 +9019,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/three": { "version": "0.175.0", "resolved": "https://registry.npmjs.org/three/-/three-0.175.0.tgz", @@ -8741,6 +9068,23 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -8782,6 +9126,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8842,12 +9187,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -8901,15 +9240,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9022,6 +9361,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "devOptional": true, "license": "MIT" }, "node_modules/validate-npm-package-license": { @@ -9036,15 +9376,18 @@ } }, "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -9108,30 +9451,30 @@ } }, "node_modules/vite-imagetools": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-7.0.5.tgz", - "integrity": "sha512-OOvVnaBTqJJ2J7X1cM1qpH4pj9jsfTxia1VSuWeyXtf+OnP8d0YI1LHpv8y2NT47wg+n7XiTgh3BvcSffuBWrw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-7.1.0.tgz", + "integrity": "sha512-Mqh1uUY2DEMuBOogFz5Rd7cAs70VP6wsdQh2IShrJ+qGk5f7yQa4pN8w0YMLlGIKYW1JfM8oXrznUwVkhG+qxg==", "dev": true, "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.5", - "imagetools-core": "^7.0.2", - "sharp": "^0.33.4" + "imagetools-core": "^7.1.0", + "sharp": "^0.34.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", + "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", + "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, @@ -9146,9 +9489,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -9163,9 +9506,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -9180,9 +9523,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -9197,9 +9540,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -9214,9 +9557,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -9231,9 +9574,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -9248,9 +9591,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -9265,9 +9608,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -9282,9 +9625,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -9299,9 +9642,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -9316,9 +9659,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -9333,9 +9676,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -9350,9 +9693,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -9367,9 +9710,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -9384,9 +9727,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -9401,9 +9744,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -9418,9 +9761,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -9435,9 +9778,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -9452,9 +9795,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -9469,9 +9812,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -9486,9 +9829,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -9503,9 +9846,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -9520,9 +9863,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -9537,9 +9880,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9550,31 +9893,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/vitefu": { @@ -9597,31 +9940,32 @@ } }, "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", + "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", + "@vitest/expect": "3.1.4", + "@vitest/mocker": "3.1.4", + "@vitest/pretty-format": "^3.1.4", + "@vitest/runner": "3.1.4", + "@vitest/snapshot": "3.1.4", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.2.0", + "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.1", + "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.13", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", + "vite-node": "3.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -9637,8 +9981,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", + "@vitest/browser": "3.1.4", + "@vitest/ui": "3.1.4", "happy-dom": "*", "jsdom": "*" }, @@ -9741,6 +10085,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9820,6 +10165,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -9904,7 +10250,10 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/web/package.json b/web/package.json index 4ee7c015c8..b47b521e6a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.131.3", + "version": "1.134.0", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { @@ -12,12 +12,14 @@ "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte", "check:typescript": "tsc --noEmit", "check:watch": "npm run check:svelte -- --watch", - "check:code": "npm run format && npm run lint && npm run check:svelte && npm run check:typescript", + "check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript", "check:all": "npm run check:code && npm run test:cov", "lint": "eslint . --max-warnings 0", + "lint:p": "eslint-p . --max-warnings 0 --concurrency=4", "lint:fix": "npm run lint -- --fix", "format": "prettier --check .", - "format:fix": "prettier --write .", + "format:fix": "prettier --write . && npm run format:i18n", + "format:i18n": "npx --yes sort-json ../i18n/*.json", "test": "vitest --run", "test:cov": "vitest --coverage", "test:watch": "vitest dev", @@ -26,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.17.3", + "@immich/ui": "^0.22.4", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -50,6 +52,7 @@ "svelte-i18n": "^4.0.1", "svelte-maplibre": "^1.0.0", "svelte-persisted-store": "^0.12.0", + "tabbable": "^6.2.0", "thumbhash": "^0.1.1" }, "devDependencies": { @@ -58,12 +61,15 @@ "@faker-js/faker": "^9.3.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.4.4", + "@sveltejs/enhanced-img": "^0.6.0", "@sveltejs/kit": "^2.15.2", "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@tailwindcss/postcss": "^4.1.7", + "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/svelte": "^5.2.6", + "@testing-library/svelte": "^5.2.8", "@testing-library/user-event": "^14.5.2", + "@types/chromecast-caf-sender": "^1.0.11", "@types/dom-to-image": "^2.6.7", "@types/justified-layout": "^4.1.4", "@types/lodash-es": "^4.17.12", @@ -74,7 +80,8 @@ "dotenv": "^16.4.7", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", - "eslint-plugin-svelte": "^3.0.0", + "eslint-p": "^0.23.0", + "eslint-plugin-svelte": "^3.9.0", "eslint-plugin-unicorn": "^57.0.0", "factory.ts": "^1.4.1", "globals": "^16.0.0", @@ -86,7 +93,7 @@ "rollup-plugin-visualizer": "^5.14.0", "svelte": "^5.25.3", "svelte-check": "^4.1.5", - "tailwindcss": "^3.4.17", + "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", @@ -94,6 +101,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.14.0" + "node": "22.16.0" } } diff --git a/web/postcss.config.cjs b/web/postcss.config.cjs index 12a703d900..e5640725a9 100644 --- a/web/postcss.config.cjs +++ b/web/postcss.config.cjs @@ -1,6 +1,5 @@ module.exports = { plugins: { - tailwindcss: {}, - autoprefixer: {}, + '@tailwindcss/postcss': {}, }, }; diff --git a/web/src/app.css b/web/src/app.css index 2c8d150b4f..b45926c0c4 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -1,6 +1,72 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import 'tailwindcss'; +@import '@immich/ui/theme/default.css'; +@source "../node_modules/@immich/ui"; +/* @import '/usr/ui/dist/theme/default.css'; */ + +@utility immich-form-input { + @apply rounded-xl bg-slate-200 px-3 py-3 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-100 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800 dark:disabled:text-gray-200; +} + +@utility immich-form-label { + @apply font-medium text-gray-500 dark:text-gray-300; +} + +@utility immich-scrollbar { + /* width */ + scrollbar-width: thin; +} + +@utility scrollbar-hidden { + /* Hidden scrollbar */ + /* width */ + scrollbar-width: none; +} + +@utility scrollbar-stable { + scrollbar-gutter: stable both-edges; +} + +@utility grid-auto-fit-* { + grid-template-columns: repeat(auto-fit, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr)); +} + +@utility grid-auto-fill-* { + grid-template-columns: repeat(auto-fill, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr)); +} + +@custom-variant dark (&:where(.dark, .dark *):not(.light)); + +@theme inline { + --color-immich-primary: rgb(var(--immich-primary)); + --color-immich-bg: rgb(var(--immich-bg)); + --color-immich-fg: rgb(var(--immich-fg)); + --color-immich-gray: rgb(var(--immich-gray)); + --color-immich-error: rgb(var(--immich-error)); + --color-immich-success: rgb(var(--immich-success)); + --color-immich-warning: rgb(var(--immich-warning)); + + --color-immich-dark-primary: rgb(var(--immich-dark-primary)); + --color-immich-dark-bg: rgb(var(--immich-dark-bg)); + --color-immich-dark-fg: rgb(var(--immich-dark-fg)); + --color-immich-dark-gray: rgb(var(--immich-dark-gray)); + --color-immich-dark-error: rgb(var(--immich-dark-error)); + --color-immich-dark-success: rgb(var(--immich-dark-success)); + --color-immich-dark-warning: rgb(var(--immich-dark-warning)); +} + +@theme { + --font-immich-mono: Overpass Mono, monospace; + + --spacing-18: 4.5rem; + + --breakpoint-tall: 800px; + --breakpoint-2xl: 1535px; + --breakpoint-xl: 1279px; + --breakpoint-lg: 1023px; + --breakpoint-md: 767px; + --breakpoint-sm: 639px; + --breakpoint-sidebar: 850px; +} @layer base { :root { @@ -8,7 +74,6 @@ --immich-primary: 66 80 175; --immich-bg: 255 255 255; --immich-fg: 0 0 0; - --immich-gray: 246 246 244; --immich-error: 229 115 115; --immich-success: 129 199 132; --immich-warning: 255 183 77; @@ -23,130 +88,96 @@ --immich-dark-warning: 245 124 0; } - :root { - /* light */ - --immich-ui-primary: 66 80 175; - --immich-ui-dark: 58 58 58; - --immich-ui-light: 255 255 255; - --immich-ui-success: 34 197 94; - --immich-ui-danger: 180 0 0; - --immich-ui-warning: 255 170 0; - --immich-ui-info: 14 165 233; - --immich-ui-default-border: 209 213 219; + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: rgb(var(--immich-ui-default-border)); } - .dark { - /* dark */ - --immich-ui-primary: 172 203 250; - --immich-ui-light: 0 0 0; - --immich-ui-dark: 229 231 235; - /* --immich-success: 56 142 60; */ - --immich-ui-danger: 239 68 68; - --immich-ui-warning: 255 170 0; - --immich-ui-info: 14 165 233; - --immich-ui-default-border: 55 65 81; + button:not(:disabled), + [role='button']:not(:disabled) { + cursor: pointer; } } -@font-face { - font-family: 'Overpass'; - src: url('$lib/assets/fonts/overpass/Overpass.ttf') format('truetype-variations'); - font-weight: 1 999; - font-style: normal; - ascent-override: 106.25%; - size-adjust: 106.25%; -} - -@font-face { - font-family: 'Overpass Mono'; - src: url('$lib/assets/fonts/overpass/OverpassMono.ttf') format('truetype-variations'); - font-weight: 1 999; - font-style: monospace; - ascent-override: 106.25%; - size-adjust: 106.25%; -} - -:root { - font-family: 'Overpass', sans-serif; - /* Used by layouts to ensure proper spacing between navbar and content */ - --navbar-height: calc(theme(spacing.18) + 4px); - --navbar-height-md: calc(theme(spacing.18) + 4px - 14px); -} - -:root.dark { - color-scheme: dark; -} - -:root:not(.dark) { - color-scheme: light; -} - -html { - height: 100%; - width: 100%; -} - -html::-webkit-scrollbar { - width: 8px; -} - -/* Track */ -html::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 16px; -} - -/* Handle */ -html::-webkit-scrollbar-thumb { - background: rgba(85, 86, 87, 0.408); - border-radius: 16px; -} - -/* Handle on hover */ -html::-webkit-scrollbar-thumb:hover { - background: #4250afad; - border-radius: 16px; -} - -body { - margin: 0; - color: #3a3a3a; -} - -input:focus-visible { - outline-offset: 0px !important; - outline: none !important; -} - -.text-white-shadow { - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); -} - -.icon-white-drop-shadow { - filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8)); -} - @layer utilities { - .immich-form-input { - @apply rounded-xl bg-slate-200 px-3 py-3 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-100 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800 dark:disabled:text-gray-200; + @font-face { + font-family: 'Overpass'; + src: url('$lib/assets/fonts/overpass/Overpass.ttf') format('truetype-variations'); + font-weight: 1 999; + font-style: normal; + ascent-override: 106.25%; + size-adjust: 106.25%; } - .immich-form-label { - @apply font-medium text-gray-500 dark:text-gray-300; + @font-face { + font-family: 'Overpass Mono'; + src: url('$lib/assets/fonts/overpass/OverpassMono.ttf') format('truetype-variations'); + font-weight: 1 999; + font-style: monospace; + ascent-override: 106.25%; + size-adjust: 106.25%; } - /* width */ - .immich-scrollbar { - scrollbar-width: thin; + :root { + font-family: 'Overpass', sans-serif; + /* Used by layouts to ensure proper spacing between navbar and content */ + --navbar-height: calc(4.5rem + 4px); + --navbar-height-md: calc(4.5rem + 4px - 14px); } - /* Hidden scrollbar */ - /* width */ - .scrollbar-hidden { - scrollbar-width: none; + :root.dark { + color-scheme: dark; } - .scrollbar-stable { - scrollbar-gutter: stable both-edges; + :root:not(.dark) { + color-scheme: light; + } + + html { + height: 100%; + width: 100%; + } + + html::-webkit-scrollbar { + width: 8px; + } + + /* Track */ + html::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 16px; + } + + /* Handle */ + html::-webkit-scrollbar-thumb { + background: rgba(85, 86, 87, 0.408); + border-radius: 16px; + } + + /* Handle on hover */ + html::-webkit-scrollbar-thumb:hover { + background: #4250afad; + border-radius: 16px; + } + + body { + margin: 0; + color: #3a3a3a; + } + + input:focus-visible { + outline-offset: 0px !important; + outline: none !important; + } + + .text-white-shadow { + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); + } + + .icon-white-drop-shadow { + filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8)); } } diff --git a/web/src/app.html b/web/src/app.html index 18a873b525..776764850f 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -102,12 +102,12 @@ - +

{$t('active')}

@@ -119,7 +119,7 @@

{waitingCount.toLocaleString($locale)} diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index 2c59f59416..73dfb30908 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -3,6 +3,7 @@ notificationController, NotificationType, } from '$lib/components/shared-components/notification/notification'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; import { featureFlags } from '$lib/stores/server-config.store'; import { getJobName } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; @@ -20,10 +21,9 @@ mdiVideo, } from '@mdi/js'; import type { Component } from 'svelte'; + import { t } from 'svelte-i18n'; import JobTile from './job-tile.svelte'; import StorageMigrationDescription from './storage-migration-description.svelte'; - import { dialogController } from '$lib/components/shared-components/dialog/dialog'; - import { t } from 'svelte-i18n'; interface Props { jobs: AllJobStatusResponseDto; @@ -45,7 +45,7 @@ const handleConfirmCommand = async (jobId: JobName, dto: JobCommandDto) => { if (dto.force) { - const isConfirmed = await dialogController.show({ + const isConfirmed = await modalManager.showDialog({ prompt: $t('admin.confirm_reprocess_all_faces'), }); diff --git a/web/src/lib/components/admin-page/restore-dialogue.svelte b/web/src/lib/components/admin-page/restore-dialogue.svelte deleted file mode 100644 index 1386ae9fc4..0000000000 --- a/web/src/lib/components/admin-page/restore-dialogue.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - - - {#snippet promptSnippet()} -

- - {#snippet children({ message })} - {message} - {/snippet} - -

- {/snippet} - diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte index bb288511ac..2f8d391954 100644 --- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte +++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte @@ -4,8 +4,8 @@ import { getByteUnitString, getBytesWithUnit } from '$lib/utils/byte-units'; import type { ServerStatsResponseDto } from '@immich/sdk'; import { mdiCameraIris, mdiChartPie, mdiPlayCircle } from '@mdi/js'; - import StatsCard from './stats-card.svelte'; import { t } from 'svelte-i18n'; + import StatsCard from './stats-card.svelte'; interface Props { stats?: ServerStatsResponseDto; @@ -38,13 +38,13 @@

{$t('total_usage').toUpperCase()}

-